From 54cf663e431285d6ec4f1d547cc95285aa2869c0 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 6 Sep 2018 16:19:19 +0100 Subject: [PATCH] Add support for monitoring multiple repositories --- .../io/spring/issuebot/GitHubProperties.java | 59 ------------- .../spring/issuebot/IssueBotApplication.java | 10 +-- .../io/spring/issuebot/IssueListener.java | 3 +- .../spring/issuebot/MonitoredRepository.java | 49 ----------- .../spring/issuebot/MonitoringProperties.java | 87 +++++++++++++++++++ .../MultiRepositoryIssueListener.java | 50 +++++++++++ .../io/spring/issuebot/RepositoryMonitor.java | 35 +++++--- .../RoutingMultiRepositoryIssueListener.java | 54 ++++++++++++ .../feedback/FeedbackConfiguration.java | 30 +++++-- .../issuebot/triage/TriageConfiguration.java | 25 +++++- src/main/resources/application.yml | 22 ++--- .../issuebot/RepositoryMonitorTests.java | 72 +++++++++++---- 12 files changed, 331 insertions(+), 165 deletions(-) delete mode 100644 src/main/java/io/spring/issuebot/MonitoredRepository.java create mode 100644 src/main/java/io/spring/issuebot/MonitoringProperties.java create mode 100644 src/main/java/io/spring/issuebot/MultiRepositoryIssueListener.java create mode 100644 src/main/java/io/spring/issuebot/RoutingMultiRepositoryIssueListener.java diff --git a/src/main/java/io/spring/issuebot/GitHubProperties.java b/src/main/java/io/spring/issuebot/GitHubProperties.java index 1409b53..a4fe594 100644 --- a/src/main/java/io/spring/issuebot/GitHubProperties.java +++ b/src/main/java/io/spring/issuebot/GitHubProperties.java @@ -16,8 +16,6 @@ package io.spring.issuebot; -import java.util.List; - import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; @@ -29,20 +27,9 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty; @ConfigurationProperties(prefix = "issuebot.github") public class GitHubProperties { - @NestedConfigurationProperty - private Repository repository = new Repository(); - @NestedConfigurationProperty private Credentials credentials = new Credentials(); - public Repository getRepository() { - return this.repository; - } - - public void setRepository(Repository repository) { - this.repository = repository; - } - public Credentials getCredentials() { return this.credentials; } @@ -51,52 +38,6 @@ public class GitHubProperties { this.credentials = credentials; } - /** - * Configuration for a GitHub repository. - */ - public static class Repository { - - /** - * The name of the organization that owns the repository. - */ - private String organization; - - /** - * The name of the repository. - */ - private String name; - - /** - * The names of the repository's collaborators. - */ - private List collaborators; - - public String getOrganization() { - return this.organization; - } - - public void setOrganization(String organization) { - this.organization = organization; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public List getCollaborators() { - return this.collaborators; - } - - public void setCollaborators(List collaborators) { - this.collaborators = collaborators; - } - - } - /** * Configuration for the credentials used to authenticate with GitHub. */ diff --git a/src/main/java/io/spring/issuebot/IssueBotApplication.java b/src/main/java/io/spring/issuebot/IssueBotApplication.java index f2524c7..fb279d9 100644 --- a/src/main/java/io/spring/issuebot/IssueBotApplication.java +++ b/src/main/java/io/spring/issuebot/IssueBotApplication.java @@ -35,7 +35,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; */ @SpringBootApplication @EnableScheduling -@EnableConfigurationProperties(GitHubProperties.class) +@EnableConfigurationProperties({ GitHubProperties.class, MonitoringProperties.class }) public class IssueBotApplication { public static void main(String[] args) { @@ -50,11 +50,9 @@ public class IssueBotApplication { @Bean RepositoryMonitor repositoryMonitor(GitHubOperations gitHub, - GitHubProperties gitHubProperties, List issueListeners) { - return new RepositoryMonitor(gitHub, - new MonitoredRepository( - gitHubProperties.getRepository().getOrganization(), - gitHubProperties.getRepository().getName()), + MonitoringProperties monitoringProperties, + List issueListeners) { + return new RepositoryMonitor(gitHub, monitoringProperties.getRepositories(), issueListeners); } diff --git a/src/main/java/io/spring/issuebot/IssueListener.java b/src/main/java/io/spring/issuebot/IssueListener.java index 57fa7ab..fbe2f59 100644 --- a/src/main/java/io/spring/issuebot/IssueListener.java +++ b/src/main/java/io/spring/issuebot/IssueListener.java @@ -19,7 +19,8 @@ package io.spring.issuebot; import io.spring.issuebot.github.Issue; /** - * An {@code IssueListener} is notified of issues found during repository monitoring. + * An {@code IssueListener} is notified of issues found during monitoring of a specific + * repository. * * @author Andy Wilkinson */ diff --git a/src/main/java/io/spring/issuebot/MonitoredRepository.java b/src/main/java/io/spring/issuebot/MonitoredRepository.java deleted file mode 100644 index a472d96..0000000 --- a/src/main/java/io/spring/issuebot/MonitoredRepository.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2015-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.issuebot; - -/** - * A repository that should be monitored. - * - * @author Andy Wilkinson - */ -public class MonitoredRepository { - - /** - * The name of the organization that owns the repository. - */ - private final String organization; - - /** - * The name of the repository. - */ - private final String name; - - public MonitoredRepository(String organization, String name) { - this.organization = organization; - this.name = name; - } - - public String getOrganization() { - return this.organization; - } - - public String getName() { - return this.name; - } - -} diff --git a/src/main/java/io/spring/issuebot/MonitoringProperties.java b/src/main/java/io/spring/issuebot/MonitoringProperties.java new file mode 100644 index 0000000..797cf91 --- /dev/null +++ b/src/main/java/io/spring/issuebot/MonitoringProperties.java @@ -0,0 +1,87 @@ +/* + * Copyright 2015-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.issuebot; + +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Properties for configuring repository monitoring. + * + * @author Andy Wilkinson + */ +@ConfigurationProperties(prefix = "issuebot.monitoring") +public class MonitoringProperties { + + private List repositories; + + public List getRepositories() { + return this.repositories; + } + + public void setRepositories(List repositories) { + this.repositories = repositories; + } + + /** + * A repository that is monitored. + */ + public static class Repository { + + /** + * The name of the organization that owns the repository. + */ + private String organization; + + /** + * The name of the repository. + */ + private String name; + + /** + * The names of the repository's collaborators. + */ + private List collaborators; + + public String getOrganization() { + return this.organization; + } + + public void setOrganization(String organization) { + this.organization = organization; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public List getCollaborators() { + return this.collaborators; + } + + public void setCollaborators(List collaborators) { + this.collaborators = collaborators; + } + + } + +} diff --git a/src/main/java/io/spring/issuebot/MultiRepositoryIssueListener.java b/src/main/java/io/spring/issuebot/MultiRepositoryIssueListener.java new file mode 100644 index 0000000..3e2e9b4 --- /dev/null +++ b/src/main/java/io/spring/issuebot/MultiRepositoryIssueListener.java @@ -0,0 +1,50 @@ +/* + * Copyright 2015-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.issuebot; + +import io.spring.issuebot.MonitoringProperties.Repository; +import io.spring.issuebot.github.Issue; + +/** + * An {@code IssueListener} is notified of issues found during monitoring of all + * repositories. + * + * @author Andy Wilkinson + */ +public interface MultiRepositoryIssueListener { + + /** + * Notification that, in the given {@code repository}, the given {@code issue} is + * open. + * @param repository the repository to which the issue belongs + * @param issue the open issue + */ + default void onOpenIssue(Repository repository, Issue issue) { + + } + + /** + * Notification that, in the give {@code repository}, the given {@code issue} is being + * closed. + * @param repository the repository to which the issue belongs + * @param issue the issue that is being closed + */ + default void onIssueClosure(Repository repository, Issue issue) { + + } + +} diff --git a/src/main/java/io/spring/issuebot/RepositoryMonitor.java b/src/main/java/io/spring/issuebot/RepositoryMonitor.java index 7760e78..0ddf8e7 100644 --- a/src/main/java/io/spring/issuebot/RepositoryMonitor.java +++ b/src/main/java/io/spring/issuebot/RepositoryMonitor.java @@ -18,6 +18,7 @@ package io.spring.issuebot; import java.util.List; +import io.spring.issuebot.MonitoringProperties.Repository; import io.spring.issuebot.github.GitHubOperations; import io.spring.issuebot.github.Issue; import io.spring.issuebot.github.Page; @@ -37,29 +38,34 @@ class RepositoryMonitor { private final GitHubOperations gitHub; - private final MonitoredRepository repository; + private final List repositories; - private final List issueListeners; + private final List issueListeners; - RepositoryMonitor(GitHubOperations gitHub, MonitoredRepository repository, - List issueListeners) { + RepositoryMonitor(GitHubOperations gitHub, List repositories, + List issueListeners) { this.gitHub = gitHub; - this.repository = repository; + this.repositories = repositories; this.issueListeners = issueListeners; } @Scheduled(fixedRate = 5 * 60 * 1000) void monitor() { - log.info("Monitoring {}/{}", this.repository.getOrganization(), - this.repository.getName()); + for (Repository repository : this.repositories) { + monitor(repository); + } + } + + private void monitor(Repository repository) { + log.info("Monitoring {}/{}", repository.getOrganization(), repository.getName()); try { - Page page = this.gitHub.getIssues(this.repository.getOrganization(), - this.repository.getName()); + Page page = this.gitHub.getIssues(repository.getOrganization(), + repository.getName()); while (page != null) { for (Issue issue : page.getContent()) { - for (IssueListener issueListener : this.issueListeners) { + for (MultiRepositoryIssueListener issueListener : this.issueListeners) { try { - issueListener.onOpenIssue(issue); + issueListener.onOpenIssue(repository, issue); } catch (Exception ex) { log.warn("Listener '{}' failed when handling issue '{}'", @@ -71,10 +77,11 @@ class RepositoryMonitor { } } catch (Exception ex) { - log.warn("A failure occurred during issue monitoring", ex); + log.warn("A failure occurred during monitoring of {}/{}", + repository.getOrganization(), repository.getName(), ex); } - log.info("Monitoring of {}/{} completed", this.repository.getOrganization(), - this.repository.getName()); + log.info("Monitoring of {}/{} completed", repository.getOrganization(), + repository.getName()); } } diff --git a/src/main/java/io/spring/issuebot/RoutingMultiRepositoryIssueListener.java b/src/main/java/io/spring/issuebot/RoutingMultiRepositoryIssueListener.java new file mode 100644 index 0000000..f330cc3 --- /dev/null +++ b/src/main/java/io/spring/issuebot/RoutingMultiRepositoryIssueListener.java @@ -0,0 +1,54 @@ +/* + * Copyright 2015-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.issuebot; + +import java.util.Map; + +import io.spring.issuebot.MonitoringProperties.Repository; +import io.spring.issuebot.github.Issue; + +/** + * A {@code MultiRepositoryIssueListener} that performs repository-based routing to + * specific {@link IssueListener IssueListeners}. + * + * @author Andy Wilkinson + */ +public class RoutingMultiRepositoryIssueListener implements MultiRepositoryIssueListener { + + private final Map delegates; + + public RoutingMultiRepositoryIssueListener(Map delegates) { + this.delegates = delegates; + } + + @Override + public void onOpenIssue(Repository repository, Issue issue) { + IssueListener listener = this.delegates.get(repository); + if (listener != null) { + listener.onOpenIssue(issue); + } + } + + @Override + public void onIssueClosure(Repository repository, Issue issue) { + IssueListener listener = this.delegates.get(repository); + if (listener != null) { + listener.onIssueClosure(issue); + } + } + +} diff --git a/src/main/java/io/spring/issuebot/feedback/FeedbackConfiguration.java b/src/main/java/io/spring/issuebot/feedback/FeedbackConfiguration.java index 314791c..46e81f1 100644 --- a/src/main/java/io/spring/issuebot/feedback/FeedbackConfiguration.java +++ b/src/main/java/io/spring/issuebot/feedback/FeedbackConfiguration.java @@ -16,10 +16,17 @@ package io.spring.issuebot.feedback; -import java.util.List; +import java.util.Collections; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; import io.spring.issuebot.GitHubProperties; import io.spring.issuebot.IssueListener; +import io.spring.issuebot.MonitoringProperties; +import io.spring.issuebot.MonitoringProperties.Repository; +import io.spring.issuebot.MultiRepositoryIssueListener; +import io.spring.issuebot.RoutingMultiRepositoryIssueListener; import io.spring.issuebot.github.GitHubOperations; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -37,18 +44,29 @@ import org.springframework.context.annotation.Configuration; class FeedbackConfiguration { @Bean - FeedbackIssueListener feedbackIssueListener(GitHubOperations gitHub, - GitHubProperties githubProperties, FeedbackProperties feedbackProperties, - List issueListener) { + MultiRepositoryIssueListener feedbackIssueListener( + MonitoringProperties monitoringProperties, GitHubOperations gitHub, + GitHubProperties githubProperties, FeedbackProperties feedbackProperties) { + Map delegates = monitoringProperties.getRepositories() + .stream() + .collect(Collectors.toMap(Function.identity(), + (repository) -> createListener(repository, gitHub, + githubProperties, feedbackProperties))); + return new RoutingMultiRepositoryIssueListener(delegates); + } + + private FeedbackIssueListener createListener(Repository repository, + GitHubOperations gitHub, GitHubProperties githubProperties, + FeedbackProperties feedbackProperties) { return new FeedbackIssueListener(gitHub, feedbackProperties.getRequiredLabel(), - githubProperties.getRepository().getCollaborators(), + repository.getCollaborators(), githubProperties.getCredentials().getUsername(), new StandardFeedbackListener(gitHub, feedbackProperties.getProvidedLabel(), feedbackProperties.getRequiredLabel(), feedbackProperties.getReminderLabel(), feedbackProperties.getReminderComment(), - feedbackProperties.getCloseComment(), issueListener)); + feedbackProperties.getCloseComment(), Collections.emptyList())); } } diff --git a/src/main/java/io/spring/issuebot/triage/TriageConfiguration.java b/src/main/java/io/spring/issuebot/triage/TriageConfiguration.java index c9feb37..e204362 100644 --- a/src/main/java/io/spring/issuebot/triage/TriageConfiguration.java +++ b/src/main/java/io/spring/issuebot/triage/TriageConfiguration.java @@ -17,8 +17,16 @@ package io.spring.issuebot.triage; import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; import io.spring.issuebot.GitHubProperties; +import io.spring.issuebot.IssueListener; +import io.spring.issuebot.MonitoringProperties; +import io.spring.issuebot.MonitoringProperties.Repository; +import io.spring.issuebot.MultiRepositoryIssueListener; +import io.spring.issuebot.RoutingMultiRepositoryIssueListener; import io.spring.issuebot.github.GitHubOperations; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -35,12 +43,23 @@ import org.springframework.context.annotation.Configuration; class TriageConfiguration { @Bean - TriageIssueListener triageIssueListener(GitHubOperations gitHubOperations, - TriageProperties triageProperties, GitHubProperties gitHubProperties) { + MultiRepositoryIssueListener triageIssueListener(GitHubOperations gitHubOperations, + TriageProperties triageProperties, MonitoringProperties monitoringProperties, + GitHubProperties gitHubProperties) { + Map delegates = monitoringProperties.getRepositories() + .stream() + .collect(Collectors.toMap(Function.identity(), + (repository) -> createListener(repository, gitHubOperations, + triageProperties))); + return new RoutingMultiRepositoryIssueListener(delegates); + } + + private TriageIssueListener createListener(Repository repository, + GitHubOperations gitHubOperations, TriageProperties triageProperties) { return new TriageIssueListener( Arrays.asList( new OpenedByCollaboratorTriageFilter( - gitHubProperties.getRepository().getCollaborators()), + repository.getCollaborators()), new LabelledTriageFilter(), new MilestoneAppliedTriageFilter()), new LabelApplyingTriageListener(gitHubOperations, triageProperties.getLabel())); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b1e52c7..f6d5b39 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,14 +1,14 @@ issuebot: - github: - repository: - organization: spring-projects - name: spring-boot - collaborators: - - bclozel - - mbhave - - philwebb - - snicoll - - wilkinsona + monitoring: + repositories: + - organization: spring-projects + name: spring-boot + collaborators: + - bclozel + - mbhave + - philwebb + - snicoll + - wilkinsona triage: label: "status: waiting-for-triage" feedback: @@ -21,4 +21,4 @@ issuebot: will be closed. close_comment: > Closing due to lack of requested feedback. If you would like us to look at this - issue, please provide the requested information and we will re-open the issue. + issue, please provide the requested information and we will re-open the issue. \ No newline at end of file diff --git a/src/test/java/io/spring/issuebot/RepositoryMonitorTests.java b/src/test/java/io/spring/issuebot/RepositoryMonitorTests.java index 76f7771..a584711 100644 --- a/src/test/java/io/spring/issuebot/RepositoryMonitorTests.java +++ b/src/test/java/io/spring/issuebot/RepositoryMonitorTests.java @@ -18,9 +18,11 @@ package io.spring.issuebot; import java.util.Arrays; +import io.spring.issuebot.MonitoringProperties.Repository; import io.spring.issuebot.github.GitHubOperations; import io.spring.issuebot.github.Issue; import io.spring.issuebot.github.Page; +import org.junit.Before; import org.junit.Test; import static org.mockito.BDDMockito.given; @@ -38,34 +40,70 @@ public class RepositoryMonitorTests { private final GitHubOperations gitHub = mock(GitHubOperations.class); - private final IssueListener issueListenerOne = mock(IssueListener.class); + private final MultiRepositoryIssueListener issueListenerOne = mock( + MultiRepositoryIssueListener.class); - private final IssueListener issueListenerTwo = mock(IssueListener.class); + private final MultiRepositoryIssueListener issueListenerTwo = mock( + MultiRepositoryIssueListener.class); + + private final Repository repositoryOne = new Repository(); + + private final Repository repositoryTwo = new Repository(); private final RepositoryMonitor repositoryMonitor = new RepositoryMonitor(this.gitHub, - new MonitoredRepository("test", "test"), + Arrays.asList(this.repositoryOne, this.repositoryTwo), Arrays.asList(this.issueListenerOne, this.issueListenerTwo)); + @Before + public void setUp() { + this.repositoryOne.setOrganization("test"); + this.repositoryOne.setName("one"); + this.repositoryTwo.setOrganization("test"); + this.repositoryTwo.setName("two"); + } + @Test - public void repositoryWithNoIssues() { - given(this.gitHub.getIssues("test", "test")).willReturn(null); + public void repositoriesWithNoIssues() { + given(this.gitHub.getIssues("test", "one")).willReturn(null); + given(this.gitHub.getIssues("test", "two")).willReturn(null); this.repositoryMonitor.monitor(); verifyNoMoreInteractions(this.issueListenerOne, this.issueListenerTwo); } @Test - public void repositoryWithOpenIssues() { + public void oneRepositoryWithOpenIssues() { @SuppressWarnings("unchecked") Page page = mock(Page.class); Issue issueOne = new Issue(null, null, null, null, null, null, null, null); Issue issueTwo = new Issue(null, null, null, null, null, null, null, null); given(page.getContent()).willReturn(Arrays.asList(issueOne, issueTwo)); - given(this.gitHub.getIssues("test", "test")).willReturn(page); + given(this.gitHub.getIssues("test", "one")).willReturn(page); + given(this.gitHub.getIssues("test", "two")).willReturn(null); this.repositoryMonitor.monitor(); - verify(this.issueListenerOne).onOpenIssue(issueOne); - verify(this.issueListenerOne).onOpenIssue(issueTwo); - verify(this.issueListenerTwo).onOpenIssue(issueOne); - verify(this.issueListenerTwo).onOpenIssue(issueTwo); + verify(this.issueListenerOne).onOpenIssue(this.repositoryOne, issueOne); + verify(this.issueListenerOne).onOpenIssue(this.repositoryOne, issueTwo); + verify(this.issueListenerTwo).onOpenIssue(this.repositoryOne, issueOne); + verify(this.issueListenerTwo).onOpenIssue(this.repositoryOne, issueTwo); + } + + @Test + public void bothRepositoriesWithOpenIssues() { + @SuppressWarnings("unchecked") + Page page = mock(Page.class); + Issue issueOne = new Issue(null, null, null, null, null, null, null, null); + Issue issueTwo = new Issue(null, null, null, null, null, null, null, null); + given(page.getContent()).willReturn(Arrays.asList(issueOne, issueTwo)); + given(this.gitHub.getIssues("test", "one")).willReturn(page); + given(this.gitHub.getIssues("test", "two")).willReturn(page); + this.repositoryMonitor.monitor(); + verify(this.issueListenerOne).onOpenIssue(this.repositoryOne, issueOne); + verify(this.issueListenerOne).onOpenIssue(this.repositoryOne, issueTwo); + verify(this.issueListenerTwo).onOpenIssue(this.repositoryOne, issueOne); + verify(this.issueListenerTwo).onOpenIssue(this.repositoryOne, issueTwo); + verify(this.issueListenerOne).onOpenIssue(this.repositoryTwo, issueOne); + verify(this.issueListenerOne).onOpenIssue(this.repositoryTwo, issueTwo); + verify(this.issueListenerTwo).onOpenIssue(this.repositoryTwo, issueOne); + verify(this.issueListenerTwo).onOpenIssue(this.repositoryTwo, issueTwo); } @Test @@ -74,17 +112,19 @@ public class RepositoryMonitorTests { Page page = mock(Page.class); Issue issue = new Issue(null, null, null, null, null, null, null, null); given(page.getContent()).willReturn(Arrays.asList(issue)); - given(this.gitHub.getIssues("test", "test")).willReturn(page); - willThrow(new RuntimeException()).given(this.issueListenerOne).onOpenIssue(issue); + given(this.gitHub.getIssues("test", "one")).willReturn(page); + willThrow(new RuntimeException()).given(this.issueListenerOne) + .onOpenIssue(this.repositoryOne, issue); this.repositoryMonitor.monitor(); - verify(this.issueListenerOne).onOpenIssue(issue); - verify(this.issueListenerTwo).onOpenIssue(issue); + verify(this.issueListenerOne).onOpenIssue(this.repositoryOne, issue); + verify(this.issueListenerTwo).onOpenIssue(this.repositoryOne, issue); } @Test public void exceptionFromGitHubIsHandledGracefully() { - given(this.gitHub.getIssues("test", "test")).willThrow(new RuntimeException()); + given(this.gitHub.getIssues("test", "one")).willThrow(new RuntimeException()); this.repositoryMonitor.monitor(); + verify(this.gitHub).getIssues("test", "one"); } }