Add support for monitoring issues that are waiting for feedback

This commit is contained in:
Andy Wilkinson
2015-12-08 17:00:13 +00:00
parent d19f043c41
commit 2275ca1daa
34 changed files with 1512 additions and 106 deletions

View File

@@ -57,8 +57,8 @@ public class RepositoryMonitorTests {
public void repositoryWithOpenIssues() {
@SuppressWarnings("unchecked")
Page<Issue> page = mock(Page.class);
Issue issueOne = new Issue(null, null, null, null, null);
Issue issueTwo = new Issue(null, null, null, null, null);
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);
this.repositoryMonitor.monitor();

View File

@@ -0,0 +1,150 @@
/*
* Copyright 2015 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.feedback;
import java.time.OffsetDateTime;
import java.util.Arrays;
import org.junit.Test;
import io.spring.issuebot.IssueListener;
import io.spring.issuebot.github.Comment;
import io.spring.issuebot.github.Event;
import io.spring.issuebot.github.GitHubOperations;
import io.spring.issuebot.github.Issue;
import io.spring.issuebot.github.Label;
import io.spring.issuebot.github.PullRequest;
import io.spring.issuebot.github.StandardPage;
import io.spring.issuebot.github.User;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
/**
* Tests for {@link FeedbackIssueListener}.
*
* @author Andy Wilkinson
*/
public class FeedbackIssueListenerTests {
private final GitHubOperations gitHub = mock(GitHubOperations.class);
private final FeedbackListener feedbackListener = mock(FeedbackListener.class);
private final IssueListener listener = new FeedbackIssueListener(this.gitHub,
"required", Arrays.asList("Amy", "Brian"), this.feedbackListener);
@Test
public void pullRequestsAreIgnored() {
Issue issue = new Issue(null, null, null, null, null,
Arrays.asList(new Label("required")), null, new PullRequest("url"));
this.listener.onOpenIssue(issue);
verifyNoMoreInteractions(this.gitHub, this.feedbackListener);
}
@Test
public void issuesWithFeedbackRequiredLabelAreIgnored() {
Issue issue = new Issue(null, null, null, null, null,
Arrays.asList(new Label("something-else")), null, null);
this.listener.onOpenIssue(issue);
verifyNoMoreInteractions(this.gitHub, this.feedbackListener);
}
@Test
public void feedbackRequiredForLabeledIssueWithEvent() {
Issue issue = new Issue(null, null, null, null, null,
Arrays.asList(new Label("required")), null, null);
OffsetDateTime requestTime = OffsetDateTime.now();
given(this.gitHub.getEvents(issue)).willReturn(new StandardPage<>(
Arrays.asList(new Event("labeled", requestTime, new Label("required"))),
() -> null));
this.listener.onOpenIssue(issue);
verify(this.feedbackListener).feedbackRequired(issue, requestTime);
}
@Test
public void feedbackProvidedAfterCommentFromNonCollaborator() {
Issue issue = new Issue("issue_url", null, null, null, null,
Arrays.asList(new Label("required")), null, null);
OffsetDateTime requestTime = OffsetDateTime.now().minusDays(1);
given(this.gitHub.getEvents(issue)).willReturn(new StandardPage<>(
Arrays.asList(new Event("labeled", requestTime, new Label("required"))),
() -> null));
given(this.gitHub.getComments(issue)).willReturn(new StandardPage<>(
Arrays.asList(new Comment(new User("Charlie"), OffsetDateTime.now())),
() -> null));
this.listener.onOpenIssue(issue);
verify(this.feedbackListener).feedbackProvided(issue);
}
@Test
public void feedbackRequiredAfterCommentFromNonCollaboratorBeforeRequest() {
Issue issue = new Issue("issue_url", null, null, null, null,
Arrays.asList(new Label("required")), null, null);
OffsetDateTime requestTime = OffsetDateTime.now().minusDays(1);
given(this.gitHub.getEvents(issue)).willReturn(new StandardPage<>(
Arrays.asList(new Event("labeled", requestTime, new Label("required"))),
() -> null));
given(this.gitHub.getComments(issue)).willReturn(new StandardPage<>(Arrays.asList(
new Comment(new User("Charlie"), OffsetDateTime.now().minusDays(2))),
() -> null));
this.listener.onOpenIssue(issue);
verify(this.feedbackListener).feedbackRequired(issue, requestTime);
}
@Test
public void feedbackRequiredAfterCommentFromCollaborator() {
Issue issue = new Issue(null, null, null, null, null,
Arrays.asList(new Label("required")), null, null);
OffsetDateTime requestTime = OffsetDateTime.now().minusDays(1);
given(this.gitHub.getEvents(issue)).willReturn(new StandardPage<>(
Arrays.asList(new Event("labeled", requestTime, new Label("required"))),
() -> null));
given(this.gitHub.getComments(issue)).willReturn(new StandardPage<>(
Arrays.asList(new Comment(new User("Amy"), OffsetDateTime.now())),
() -> null));
this.listener.onOpenIssue(issue);
verify(this.feedbackListener).feedbackRequired(issue, requestTime);
}
@Test
public void issueWithNoMatchingLabeledEventIsIgnored() {
Issue issue = new Issue(null, null, null, null, null,
Arrays.asList(new Label("required")), null, null);
OffsetDateTime requestTime = OffsetDateTime.now();
given(this.gitHub.getEvents(issue)).willReturn(new StandardPage<>(
Arrays.asList(
new Event("labeled", requestTime, new Label("something-else"))),
() -> null));
this.listener.onOpenIssue(issue);
verifyNoMoreInteractions(this.feedbackListener);
}
@Test
public void eventsWithWrongTypeAreIgnored() {
Issue issue = new Issue(null, null, null, null, null,
Arrays.asList(new Label("required")), null, null);
OffsetDateTime requestTime = OffsetDateTime.now();
given(this.gitHub.getEvents(issue)).willReturn(new StandardPage<>(
Arrays.asList(new Event("milestoned", requestTime, null)), () -> null));
this.listener.onOpenIssue(issue);
verifyNoMoreInteractions(this.feedbackListener);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2015 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.feedback;
import java.time.OffsetDateTime;
import org.junit.Test;
import io.spring.issuebot.github.GitHubOperations;
import io.spring.issuebot.github.Issue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
/**
* Tests for {@link StandardFeedbackListener}.
*
* @author Andy Wilkinson
*/
public class StandardFeedbackListenerTests {
private final GitHubOperations gitHub = mock(GitHubOperations.class);
private final FeedbackListener listener = new StandardFeedbackListener(this.gitHub,
"provided", "required", "reminder", "closing");
private final Issue issue = new Issue(null, null, null, null, null, null, null, null);
@Test
public void feedbackProvided() {
this.listener.feedbackProvided(this.issue);
verify(this.gitHub).addLabel(this.issue, "provided");
verify(this.gitHub).removeLabel(this.issue, "required");
}
@Test
public void feedbackRequiredButReminderNotYetDue() {
this.listener.feedbackRequired(this.issue, OffsetDateTime.now());
verifyNoMoreInteractions(this.gitHub);
}
@Test
public void feedbackRequiredAndReminderDue() {
this.listener.feedbackRequired(this.issue, OffsetDateTime.now().minusDays(8));
verify(this.gitHub).addComment(this.issue, "reminder");
}
@Test
public void feedbackRequiredAndOverdue() {
this.listener.feedbackRequired(this.issue, OffsetDateTime.now().minusDays(15));
verify(this.gitHub).addComment(this.issue, "closing");
}
}

View File

@@ -16,7 +16,6 @@
package io.spring.issuebot.github;
import java.util.Arrays;
import java.util.Date;
import org.junit.Rule;
@@ -33,6 +32,7 @@ import org.springframework.test.web.client.response.DefaultResponseCreator;
import org.springframework.util.Base64Utils;
import org.springframework.web.client.RestTemplate;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
@@ -121,8 +121,8 @@ public class GitHubTemplateTests {
this.server.expect(requestTo("commentsUrl")).andExpect(method(HttpMethod.GET))
.andExpect(basicAuth())
.andRespond(withSuccess("[]", MediaType.APPLICATION_JSON));
Page<Comment> comments = this.gitHub
.getComments(new Issue(null, "commentsUrl", null, null, null));
Page<Comment> comments = this.gitHub.getComments(
new Issue(null, "commentsUrl", null, null, null, null, null, null));
assertThat(comments.getContent().size(), is(0));
assertThat(comments.next(), is(nullValue()));
}
@@ -132,8 +132,8 @@ public class GitHubTemplateTests {
this.server.expect(requestTo("commentsUrl")).andExpect(method(HttpMethod.GET))
.andExpect(basicAuth())
.andRespond(withResource("comments-page-one.json"));
Page<Comment> comments = this.gitHub
.getComments(new Issue(null, "commentsUrl", null, null, null));
Page<Comment> comments = this.gitHub.getComments(
new Issue(null, "commentsUrl", null, null, null, null, null, null));
assertThat(comments.getContent().size(), is(17));
assertThat(comments.next(), is(nullValue()));
}
@@ -148,8 +148,8 @@ public class GitHubTemplateTests {
this.server.expect(requestTo("page-two")).andExpect(method(HttpMethod.GET))
.andExpect(basicAuth())
.andRespond(withResource("comments-page-two.json"));
Page<Comment> pageOne = this.gitHub
.getComments(new Issue(null, "commentsUrl", null, null, null));
Page<Comment> pageOne = this.gitHub.getComments(
new Issue(null, "commentsUrl", null, null, null, null, null, null));
assertThat(pageOne.getContent().size(), is(17));
Page<Comment> pageTwo = pageOne.next();
assertThat(pageTwo, is(not(nullValue())));
@@ -157,25 +157,75 @@ public class GitHubTemplateTests {
}
@Test
public void addLabelToUnlabelledIssue() {
public void addLabelToIssue() {
this.server.expect(requestTo("issueUrl")).andExpect(method(HttpMethod.POST))
.andExpect(basicAuth())
.andExpect(content().string("{\"labels\":[\"test\"]}"))
.andRespond(withResource("issue-single-label.json"));
Issue issue = new Issue("issueUrl", null, null, Arrays.asList(), null);
Issue labelledIssue = this.gitHub.addLabel(issue, "test");
assertThat(labelledIssue.getLabels(), hasSize(1));
.andExpect(content().string("{\"labels\":[\"test\"]}")).andRespond(
withSuccess("[{\"name\":\"test\"}]", MediaType.APPLICATION_JSON));
Issue issue = new Issue("issueUrl", null, null, null, null, null, null, null);
Issue modifiedIssue = this.gitHub.addLabel(issue, "test");
assertThat(modifiedIssue.getLabels(), hasSize(1));
}
@Test
public void addAdditionalLabelToIssue() {
this.server.expect(requestTo("issueUrl")).andExpect(method(HttpMethod.POST))
public void removeLabelFromIssue() {
this.server.expect(requestTo("labels/test")).andExpect(method(HttpMethod.DELETE))
.andExpect(basicAuth())
.andExpect(content().string("{\"labels\":[\"test\"]}"))
.andRespond(withResource("issue-two-labels.json"));
Issue issue = new Issue("issueUrl", null, null, Arrays.asList(), null);
Issue labelledIssue = this.gitHub.addLabel(issue, "test");
assertThat(labelledIssue.getLabels(), hasSize(2));
.andRespond(withSuccess("[]", MediaType.APPLICATION_JSON));
Issue issue = new Issue(null, null, null, "labels{/name}", null, null, null,
null);
Issue modifiedIssue = this.gitHub.removeLabel(issue, "test");
assertThat(modifiedIssue.getLabels(), hasSize(0));
}
@Test
public void addCommentToIssue() {
this.server.expect(requestTo("commentsUrl")).andExpect(method(HttpMethod.POST))
.andExpect(basicAuth())
.andExpect(content().string("{\"body\":\"A test comment\"}"))
.andRespond(withResource("new-comment.json"));
Issue issue = new Issue(null, "commentsUrl", null, null, null, null, null, null);
Comment comment = this.gitHub.addComment(issue, "A test comment");
assertThat(comment, is(not(nullValue())));
}
@Test
public void singlePageOfEvents() {
this.server.expect(requestTo("eventsUrl")).andExpect(method(HttpMethod.GET))
.andExpect(basicAuth()).andRespond(withResource("events-page-one.json"));
Page<Event> events = this.gitHub.getEvents(
new Issue(null, null, "eventsUrl", null, null, null, null, null));
assertThat(events.getContent().size(), is(12));
assertThat(events.next(), is(nullValue()));
}
@Test
public void multiplePagesOfEvents() {
HttpHeaders headers = new HttpHeaders();
headers.set("Link", "<page-two>; rel=\"next\"");
this.server.expect(requestTo("eventsUrl")).andExpect(method(HttpMethod.GET))
.andExpect(basicAuth()).andRespond(withResource("events-page-one.json",
"Link:<page-two>; rel=\"next\""));
this.server.expect(requestTo("page-two")).andExpect(method(HttpMethod.GET))
.andExpect(basicAuth()).andRespond(withResource("events-page-two.json"));
Page<Event> pageOne = this.gitHub.getEvents(
new Issue(null, null, "eventsUrl", null, null, null, null, null));
assertThat(pageOne.getContent().size(), is(12));
Page<Event> pageTwo = pageOne.next();
assertThat(pageTwo, is(not(nullValue())));
assertThat(pageTwo.getContent().size(), is(3));
}
@Test
public void closeIssue() {
this.server.expect(requestTo("issueUrl")).andExpect(method(HttpMethod.PATCH))
.andExpect(basicAuth())
.andExpect(content().string("{\"state\":\"closed\"}"))
.andRespond(withSuccess("{\"url\":\"updatedIssueUrl\"}",
MediaType.APPLICATION_JSON));
Issue closedIssue = this.gitHub
.close(new Issue("issueUrl", null, null, null, null, null, null, null));
assertThat(closedIssue.getUrl(), is(equalTo("updatedIssueUrl")));
}
private DefaultResponseCreator withResource(String resource, String... headers) {

View File

@@ -45,7 +45,7 @@ public class CommentedByCollaboratorTriageFilterTests {
private final TriageFilter filter = new CommentedByCollaboratorTriageFilter(
Arrays.asList("Adam", "Brenda", "Charlie"), this.gitHub);
private final Issue issue = new Issue(null, null, null, null, null);
private final Issue issue = new Issue(null, null, null, null, null, null, null, null);
@Test
@SuppressWarnings("unchecked")
@@ -61,7 +61,7 @@ public class CommentedByCollaboratorTriageFilterTests {
public void noCommentsByCollaborators() {
Page<Comment> pageOne = mock(Page.class);
given(pageOne.getContent())
.willReturn(Arrays.asList(new Comment(new User("Debbie"))));
.willReturn(Arrays.asList(new Comment(new User("Debbie"), null)));
given(this.gitHub.getComments(this.issue)).willReturn(pageOne);
assertThat(this.filter.triaged(this.issue), is(false));
}
@@ -71,7 +71,7 @@ public class CommentedByCollaboratorTriageFilterTests {
public void commentByCollaboratorOnFirstPage() {
Page<Comment> pageOne = mock(Page.class);
given(pageOne.getContent())
.willReturn(Arrays.asList(new Comment(new User("Brenda"))));
.willReturn(Arrays.asList(new Comment(new User("Brenda"), null)));
given(this.gitHub.getComments(this.issue)).willReturn(pageOne);
assertThat(this.filter.triaged(this.issue), is(true));
}
@@ -81,10 +81,10 @@ public class CommentedByCollaboratorTriageFilterTests {
public void commentByCollaboratorOnLaterPage() {
Page<Comment> pageOne = mock(Page.class);
given(pageOne.getContent())
.willReturn(Arrays.asList(new Comment(new User("Debbie"))));
.willReturn(Arrays.asList(new Comment(new User("Debbie"), null)));
Page<Comment> pageTwo = mock(Page.class);
given(pageTwo.getContent())
.willReturn(Arrays.asList(new Comment(new User("Brenda"))));
.willReturn(Arrays.asList(new Comment(new User("Brenda"), null)));
given(pageOne.next()).willReturn(pageTwo);
given(this.gitHub.getComments(this.issue)).willReturn(pageOne);
assertThat(this.filter.triaged(this.issue), is(true));

View File

@@ -38,7 +38,7 @@ public class LabelApplyingTriageListenerTests {
@Test
public void requiresTriage() {
Issue issue = new Issue(null, null, null, null, null);
Issue issue = new Issue(null, null, null, null, null, null, null, null);
this.listener.requiresTriage(issue);
verify(this.gitHub).addLabel(issue, "test");
}

View File

@@ -38,23 +38,22 @@ public class LabelledTriageFilterTests {
@Test
public void issueWithLabels() {
assertThat(this.filter.triaged(
new Issue(null, null, null, Arrays.asList(new Label("test")), null)),
is(true));
assertThat(this.filter.triaged(new Issue(null, null, null, null, null,
Arrays.asList(new Label("test")), null, null)), is(true));
}
@Test
public void issueWithNullLabels() {
assertThat(this.filter.triaged(new Issue(null, null, null, null, null)),
assertThat(
this.filter.triaged(
new Issue(null, null, null, null, null, null, null, null)),
is(false));
}
@Test
public void issueWithNoLabels() {
assertThat(
this.filter.triaged(
new Issue(null, null, null, Collections.emptyList(), null)),
is(false));
assertThat(this.filter.triaged(new Issue(null, null, null, null, null,
Collections.emptyList(), null, null)), is(false));
}
}

View File

@@ -36,15 +36,15 @@ public class MilestoneAppliedTriageFilterTests {
@Test
public void issueWithMilestoneApplied() {
assertThat(
this.filter.triaged(
new Issue(null, null, null, null, new Milestone("test"))),
is(true));
assertThat(this.filter.triaged(new Issue(null, null, null, null, null, null,
new Milestone("test"), null)), is(true));
}
@Test
public void issueWithNoMilestoneApplied() {
assertThat(this.filter.triaged(new Issue(null, null, null, null, null)),
assertThat(
this.filter.triaged(
new Issue(null, null, null, null, null, null, null, null)),
is(false));
}

View File

@@ -38,16 +38,15 @@ public class OpenedByCollaboratorTriageFilterTests {
@Test
public void openedByCollaborator() {
assertThat(
this.filter.triaged(new Issue(null, null, new User("Adam"), null, null)),
assertThat(this.filter.triaged(
new Issue(null, null, null, null, new User("Adam"), null, null, null)),
is(true));
}
@Test
public void openedByAnotherUser() {
assertThat(
this.filter
.triaged(new Issue(null, null, new User("Debbie"), null, null)),
assertThat(this.filter.triaged(
new Issue(null, null, null, null, new User("Debbie"), null, null, null)),
is(false));
}

View File

@@ -46,7 +46,7 @@ public class TriageIssueListenerTests {
@Test
public void listenerIsCalledWhenIssueRequiresTriage() {
Issue issue = new Issue(null, null, null, null, null);
Issue issue = new Issue(null, null, null, null, null, null, null, null);
given(this.triageFilterOne.triaged(issue)).willReturn(false);
given(this.triageFilterTwo.triaged(issue)).willReturn(false);
this.issueListener.onOpenIssue(issue);
@@ -57,7 +57,7 @@ public class TriageIssueListenerTests {
@Test
public void listenerIsNotCalledWhenIssueHasAlreadyBeenTriaged() {
Issue issue = new Issue(null, null, null, null, null);
Issue issue = new Issue(null, null, null, null, null, null, null, null);
given(this.triageFilterOne.triaged(issue)).willReturn(true);
this.issueListener.onOpenIssue(issue);
verify(this.triageFilterOne).triaged(issue);