diff --git a/src/main/java/edu/tamu/app/model/ServiceType.java b/src/main/java/edu/tamu/app/model/ServiceType.java index e1c3db76..5c0b11a8 100644 --- a/src/main/java/edu/tamu/app/model/ServiceType.java +++ b/src/main/java/edu/tamu/app/model/ServiceType.java @@ -8,7 +8,8 @@ public enum ServiceType { VERSION_ONE("Version One"), - GITHUB("GitHub"); + GITHUB_PROJECT("GitHub Project"), + GITHUB_MILESTONE("GitHub Milestone"); private String gloss; diff --git a/src/main/java/edu/tamu/app/service/manager/GitHubMilestoneService.java b/src/main/java/edu/tamu/app/service/manager/GitHubMilestoneService.java new file mode 100644 index 00000000..4ea94c8f --- /dev/null +++ b/src/main/java/edu/tamu/app/service/manager/GitHubMilestoneService.java @@ -0,0 +1,343 @@ +package edu.tamu.app.service.manager; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import org.apache.log4j.Logger; +import org.kohsuke.github.GHIssue; +import org.kohsuke.github.GHIssueState; +import org.kohsuke.github.GHLabel; +import org.kohsuke.github.GHMilestone; +import org.kohsuke.github.GHMilestoneState; +import org.kohsuke.github.GHOrganization; +import org.kohsuke.github.GHProject; +import org.kohsuke.github.GHProject.ProjectStateFilter; +import org.kohsuke.github.GHProjectCard; +import org.kohsuke.github.GHProjectColumn; +import org.kohsuke.github.GHRepository; +import org.kohsuke.github.GHUser; +import org.kohsuke.github.GitHub; +import org.kohsuke.github.GitHubBuilder; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import edu.tamu.app.cache.model.Card; +import edu.tamu.app.cache.model.Member; +import edu.tamu.app.cache.model.RemoteProject; +import edu.tamu.app.cache.model.Sprint; +import edu.tamu.app.model.ManagementService; +import edu.tamu.app.model.request.FeatureRequest; +import edu.tamu.app.rest.TokenAuthRestTemplate; + +public class GitHubMilestoneService extends MappingRemoteProjectManagerBean { + + private static final Logger logger = Logger.getLogger(GitHubProjectService.class); + + protected static final String ORGANIZATION = "TAMULib"; + protected static final String REQUEST_LABEL = "request"; + protected static final String ISSUE_LABEL = "issue"; + protected static final String FEATURE_LABEL = "feature"; + protected static final String DEFECT_LABEL = "bug"; + protected static final String SPRINT = "SPRINT"; + + private final ManagementService managementService; + + private final GitHubBuilder ghBuilder; + + private final GitHub github; + + private final Map members; + + private final RestTemplate restTemplate; + + private GHLabel label; + + public GitHubMilestoneService(final ManagementService managementService) throws IOException { + this.managementService = managementService; + ghBuilder = new GitHubBuilder(); + github = getGitHubInstance(); + restTemplate = getRestTemplate(); + members = new HashMap(); + } + + @Override + public List getRemoteProject() throws Exception { + logger.info("Fetching remote projects"); + final List remoteProjects = new ArrayList(); + final GHOrganization org = github.getOrganization(ORGANIZATION); + for (GHRepository repo : org.getRepositories().values()) { + final List labels = repo.listLabels().asList(); + remoteProjects.add(buildRemoteProject(repo, labels)); + } + return remoteProjects; + } + + @Override + public RemoteProject getRemoteProjectByScopeId(final String scopeId) throws Exception { + logger.info("Fetching remote project by scope id " + scopeId); + GHRepository repo = github.getRepositoryById(scopeId); + List labels = repo.listLabels().asList(); + return buildRemoteProject(repo, labels); + } + + @Override + public List getActiveSprintsByScopeId(final String scopeId) throws Exception { + logger.info("Fetching active sprints for remote project with scope id " + scopeId); + List activeSprints = new ArrayList(); + GHRepository repo = github.getRepositoryById(scopeId); + List projects = repo.listProjects(ProjectStateFilter.OPEN).asList(); + for (GHProject project : projects) { + String sprintId = String.valueOf(project.getId()); + String projectName = repo.getName(); + Map> partitionedCards = getCards(project); + for (Entry> partition : partitionedCards.entrySet()) { + activeSprints.add(new Sprint(sprintId, partition.getKey(), projectName, partition.getValue())); + } + } + return activeSprints; + } + + @Override + public List getAdditionalActiveSprints() throws Exception { + GHOrganization organization = github.getOrganization(ORGANIZATION); + List projects = organization.listProjects(ProjectStateFilter.OPEN).asList(); + List sprints = new ArrayList(); + for (GHProject project : projects) { + String sprintId = String.valueOf(project.getId()); + Map> partitionedCards = getCards(project); + String productName = String.format("%s - %s", organization.getName(), project.getName()); + int count = 0; + for (Entry> partition : partitionedCards.entrySet()) { + sprints.add(new Sprint(sprintId + "-" + count, partition.getKey(), productName, partition.getValue())); + count++; + } + } + return sprints; + } + + @Override + public String push(final FeatureRequest request) throws Exception { + logger.info("Submitting feature request " + request.getTitle() + " to product with scope id " + request.getScopeId()); + + String scopeId = String.valueOf(request.getScopeId()); + String title = request.getTitle(); + String body = request.getDescription(); + GHRepository repo = github.getRepositoryById(scopeId); + + return Long.toString(repo.createIssue(title).body(body).create().getId()); + } + + protected GitHub getGitHubInstance() throws IOException { + final Optional endpoint = Optional.of(managementService.getUrl()); + final Optional token = Optional.of(managementService.getToken()); + + if (!endpoint.isPresent()) { + throw new RuntimeException("GitHub service endpoint was not defined"); + } + + if (!token.isPresent()) { + throw new RuntimeException("GitHub token was not defined"); + } + + return ghBuilder + .withEndpoint(endpoint.get()) + .withOAuthToken(token.get()) + .build(); + } + + private RestTemplate getRestTemplate() { + return new TokenAuthRestTemplate(managementService.getToken()); + } + + private RemoteProject buildRemoteProject(GHRepository repo, List labels) throws IOException { + final String scopeId = String.valueOf(repo.getId()); + final String name = repo.getName(); + long requestCount = getPrimaryWorkItemCount(REQUEST_LABEL, repo, labels); + long issueCount = getPrimaryWorkItemCount(ISSUE_LABEL, repo, labels); + long featureCount = getPrimaryWorkItemCount(FEATURE_LABEL, repo, labels); + long defectCount = getPrimaryWorkItemCount(DEFECT_LABEL, repo, labels); + + return new RemoteProject(scopeId, name, requestCount, issueCount, featureCount, defectCount, 0L); + } + + private long getPrimaryWorkItemCount(final String type, final GHRepository repo, final List labels) + throws IOException { + label = getLabelByName(labels, type); + if (label == null) { + return 0; + } + return repo.listIssues(GHIssueState.OPEN) + .asList() + .stream() + .filter(this::cardIsLabelType) + .count(); + } + + private GHLabel getLabelByName(final List labels, final String name) { + GHLabel returnValue = null; + Optional match = labels.stream() + .filter(label -> label.getName().equals(name)) + .findFirst(); + if (match.isPresent()) { + returnValue = match.get(); + } + return returnValue; + } + + private boolean cardIsLabelType(GHIssue card) { + try { + Collection labels = card.getLabels(); + if (label.getName().equals(ISSUE_LABEL) && isAnIssue(card)) { + return true; + } + return hasLabelByName(labels, label.getName()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private boolean isAnIssue(GHIssue card) throws IOException { + Collection labels = card.getLabels(); + return !hasLabelByName(labels, REQUEST_LABEL) + && !hasLabelByName(labels, DEFECT_LABEL) + && !hasLabelByName(labels, FEATURE_LABEL); + } + + private boolean hasLabelByName(Collection labels, String name) { + return labels.parallelStream() + .filter(cardLabel -> cardLabel.getName().equals(name)) + .findAny() + .isPresent(); + } + + private Map> getCards(GHProject project) throws IOException { + Map> cardsByMilestone = new HashMap<>(); + for (GHProjectColumn column : project.listColumns().asList()) { + List projectCards = column.listCards().asList(); + Map cardContents = new HashMap<>(); + for (GHProjectCard card : projectCards) { + cardContents.put(card.getId(), card.getContent()); + } + Map> partitionedCards = projectCards.stream() + // Card without contents is a note + .filter(c -> cardContents.get(c.getId()) != null) + // Card without a milestone is not on the sprint + .filter(c -> cardContents.get(c.getId()).getMilestone() != null) + .collect(Collectors.groupingBy(c -> cardContents.get(c.getId()).getMilestone())); + + for (Entry> partition : partitionedCards.entrySet()) { + List cards = new ArrayList<>(); + for (GHProjectCard card : partition.getValue()) { + GHIssue content = cardContents.get(card.getId()); + String id = String.valueOf(card.getId()); + String name = content.getTitle(); + String number = String.valueOf(content.getNumber()); + String type = getCardType(content); + String description = content.getBody(); + String status = card.getColumn().getName(); + // TODO: Figure out how we want to handle sizes + String estimate = null; + List assignees = new ArrayList(); + for (GHUser user : content.getAssignees()) { + assignees.add(getMember(user)); + } + cards.add(new Card(id, number, mapCardType(type), name, description, mapStatus(status), mapEstimate(estimate), assignees)); + } + GHMilestone milestone = partition.getKey(); + if (milestone.getState().equals(GHMilestoneState.OPEN) && milestone.getTitle().toUpperCase().contains(SPRINT)) { + String title = partition.getKey().getTitle(); + if (cardsByMilestone.containsKey(title)) { + cardsByMilestone.get(title).addAll(cards); + } else { + cardsByMilestone.put(title, cards); + } + } + } + } + return cardsByMilestone; + } + + private String getCardType(GHIssue content) throws IOException { + List labels = (List) content.getLabels(); + GHLabel label = labels.stream() + .filter(label1 -> label1.getName().equals(DEFECT_LABEL)) + .findFirst() + .orElseGet(() -> labels.stream() + .filter(label2 -> label2.getName().equals(FEATURE_LABEL)) + .findFirst() + .orElseGet(() -> labels.stream() + .filter(label3 -> label3.getName().equals(ISSUE_LABEL)) + .findFirst() + .orElseGet(() -> labels.stream() + .filter(label4 -> label4.getName().equals(REQUEST_LABEL)) + .findFirst() + .orElse(null) + ) + ) + ); + return label == null ? null : label.getName(); + } + + protected Member getMember(GHUser user) throws IOException { + Member member; + String memberId = String.valueOf(user.getId()); + Optional cachedMember = getCachedMember(memberId); + if (cachedMember.isPresent()) { + member = cachedMember.get(); + } else { + String name = user.getName(); + String avatarUrlString = user.getAvatarUrl(); + String avatarPath = getAvatarPath(avatarUrlString); + member = new Member(memberId, name, avatarPath); + + Optional avatarUrl = Optional.ofNullable(getClass().getResource("/images/" + avatarPath)); + if (!avatarUrl.isPresent()) { + storeAvatar(avatarUrlString); + } + + cacheMember(memberId, member); + } + return member; + } + + private Optional getCachedMember(final String id) { + return Optional.ofNullable(members.get(id)); + } + + private String getAvatarPath(String url) { + return url.substring(url.indexOf("/u/") + 3, url.indexOf("?")); + } + + private void storeAvatar(String avatarUrl) throws IOException { + URL imagesPath = getClass().getResource("/images/"); + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM)); + HttpEntity entity = new HttpEntity(headers); + ResponseEntity response = restTemplate.exchange(avatarUrl, HttpMethod.GET, entity, byte[].class, "1"); + if (response.getStatusCode().equals(HttpStatus.OK)) { + File file = new File(imagesPath.getFile() + getAvatarPath(avatarUrl)); + Files.write(file.toPath(), response.getBody()); + } + } + + private void cacheMember(String id, Member member) { + members.put(id, member); + } + +} diff --git a/src/main/java/edu/tamu/app/service/manager/GitHubService.java b/src/main/java/edu/tamu/app/service/manager/GitHubProjectService.java similarity index 94% rename from src/main/java/edu/tamu/app/service/manager/GitHubService.java rename to src/main/java/edu/tamu/app/service/manager/GitHubProjectService.java index 8825e7fd..e02baba9 100644 --- a/src/main/java/edu/tamu/app/service/manager/GitHubService.java +++ b/src/main/java/edu/tamu/app/service/manager/GitHubProjectService.java @@ -41,15 +41,16 @@ import edu.tamu.app.model.request.FeatureRequest; import edu.tamu.app.rest.TokenAuthRestTemplate; -public class GitHubService extends MappingRemoteProjectManagerBean { +public class GitHubProjectService extends MappingRemoteProjectManagerBean { - private static final Logger logger = Logger.getLogger(GitHubService.class); + private static final Logger logger = Logger.getLogger(GitHubProjectService.class); protected static final String ORGANIZATION = "TAMULib"; protected static final String REQUEST_LABEL = "request"; protected static final String ISSUE_LABEL = "issue"; protected static final String FEATURE_LABEL = "feature"; protected static final String DEFECT_LABEL = "bug"; + protected static final String SPRINT = "SPRINT"; private final ManagementService managementService; @@ -63,7 +64,7 @@ public class GitHubService extends MappingRemoteProjectManagerBean { private GHLabel label; - public GitHubService(final ManagementService managementService) throws IOException { + public GitHubProjectService(final ManagementService managementService) throws IOException { this.managementService = managementService; ghBuilder = new GitHubBuilder(); github = getGitHubInstance(); @@ -98,6 +99,10 @@ public List getActiveSprintsByScopeId(final String scopeId) throws Excep GHRepository repo = github.getRepositoryById(scopeId); List projects = repo.listProjects(ProjectStateFilter.OPEN).asList(); for (GHProject project : projects) { + // Ignore projects without "Sprint" in the name + if (!project.getName().toUpperCase().contains(SPRINT)) { + continue; + } String sprintId = String.valueOf(project.getId()); String name = project.getName(); String projectName = repo.getName(); @@ -113,10 +118,14 @@ public List getAdditionalActiveSprints() throws Exception { List projects = organization.listProjects(ProjectStateFilter.OPEN).asList(); List sprints = new ArrayList(); for (GHProject project : projects) { + // Ignore projects without "Sprint" in the name + if (!project.getName().toUpperCase().contains(SPRINT)) { + continue; + } String sprintId = String.valueOf(project.getId()); - String name = project.getName(); + String productName = String.format("%s - %s", organization.getName(), project.getName()); List cards = getCards(project); - sprints.add(new Sprint(sprintId, name, ORGANIZATION, cards)); + sprints.add(new Sprint(sprintId, productName, ORGANIZATION, cards)); } return sprints; } diff --git a/src/main/java/edu/tamu/app/service/registry/ManagementBeanRegistry.java b/src/main/java/edu/tamu/app/service/registry/ManagementBeanRegistry.java index 5a5f930c..2f7a4f88 100644 --- a/src/main/java/edu/tamu/app/service/registry/ManagementBeanRegistry.java +++ b/src/main/java/edu/tamu/app/service/registry/ManagementBeanRegistry.java @@ -13,7 +13,8 @@ import com.versionone.apiclient.exceptions.V1Exception; import edu.tamu.app.model.ManagementService; -import edu.tamu.app.service.manager.GitHubService; +import edu.tamu.app.service.manager.GitHubMilestoneService; +import edu.tamu.app.service.manager.GitHubProjectService; import edu.tamu.app.service.manager.VersionOneService; @Service @@ -39,8 +40,11 @@ public void register(ManagementService managementService) { case VERSION_ONE: service = Optional.of((ManagementBean) new VersionOneService(managementService)); break; - case GITHUB: - service = Optional.of((ManagementBean) new GitHubService(managementService)); + case GITHUB_PROJECT: + service = Optional.of((ManagementBean) new GitHubProjectService(managementService)); + break; + case GITHUB_MILESTONE: + service = Optional.of((ManagementBean) new GitHubMilestoneService(managementService)); break; default: break; diff --git a/src/test/java/edu/tamu/app/cache/service/ActiveSprintsScheduledCacheServiceTest.java b/src/test/java/edu/tamu/app/cache/service/ActiveSprintsScheduledCacheServiceTest.java index 0c7b36ca..e9224bb9 100644 --- a/src/test/java/edu/tamu/app/cache/service/ActiveSprintsScheduledCacheServiceTest.java +++ b/src/test/java/edu/tamu/app/cache/service/ActiveSprintsScheduledCacheServiceTest.java @@ -48,7 +48,7 @@ public class ActiveSprintsScheduledCacheServiceTest { private static final String TEST_PROJECT_TOKEN2 = "9876543210"; private static final RemoteProjectManager TEST_REMOTE_PROJECT_MANAGER1 = new RemoteProjectManager("Test Remote Project Manager 1", ServiceType.VERSION_ONE, TEST_PROJECT_URL1, TEST_PROJECT_TOKEN1); - private static final RemoteProjectManager TEST_REMOTE_PROJECT_MANAGER2 = new RemoteProjectManager("Test Remote Project Manager 2", ServiceType.GITHUB, TEST_PROJECT_URL2, TEST_PROJECT_TOKEN2); + private static final RemoteProjectManager TEST_REMOTE_PROJECT_MANAGER2 = new RemoteProjectManager("Test Remote Project Manager 2", ServiceType.GITHUB_PROJECT, TEST_PROJECT_URL2, TEST_PROJECT_TOKEN2); private static final RemoteProjectInfo TEST_REMOTE_PROJECT_INFO1 = new RemoteProjectInfo(TEST_PROJECT_SCOPE1, TEST_REMOTE_PROJECT_MANAGER1); private static final RemoteProjectInfo TEST_REMOTE_PROJECT_INFO2 = new RemoteProjectInfo(TEST_PROJECT_SCOPE2, TEST_REMOTE_PROJECT_MANAGER2); diff --git a/src/test/java/edu/tamu/app/model/ModelTest.java b/src/test/java/edu/tamu/app/model/ModelTest.java index 503d4814..ad1be925 100644 --- a/src/test/java/edu/tamu/app/model/ModelTest.java +++ b/src/test/java/edu/tamu/app/model/ModelTest.java @@ -49,7 +49,7 @@ public abstract class ModelTest { protected static final Date TEST_INTERNAL_REQUEST_CREATED_ON2 = new Date(System.currentTimeMillis() + 100); protected static final RemoteProjectManager TEST_REMOTE_PROJECT_MANAGER1 = new RemoteProjectManager(TEST_REMOTE_PROJECT_MANAGER1_NAME, ServiceType.VERSION_ONE, TEST_PROJECT_URL1, TEST_PROJECT_TOKEN1); - protected static final RemoteProjectManager TEST_REMOTE_PROJECT_MANAGER2 = new RemoteProjectManager(TEST_REMOTE_PROJECT_MANAGER2_NAME, ServiceType.GITHUB, TEST_PROJECT_URL2, TEST_PROJECT_TOKEN2); + protected static final RemoteProjectManager TEST_REMOTE_PROJECT_MANAGER2 = new RemoteProjectManager(TEST_REMOTE_PROJECT_MANAGER2_NAME, ServiceType.GITHUB_PROJECT, TEST_PROJECT_URL2, TEST_PROJECT_TOKEN2); protected static final RemoteProjectInfo TEST_REMOTE_PROJECT_INFO1 = new RemoteProjectInfo(TEST_PROJECT_SCOPE1, TEST_REMOTE_PROJECT_MANAGER1); protected static final RemoteProjectInfo TEST_REMOTE_PROJECT_INFO2 = new RemoteProjectInfo(TEST_PROJECT_SCOPE2, TEST_REMOTE_PROJECT_MANAGER1); diff --git a/src/test/java/edu/tamu/app/model/RemoteProjectManagerTest.java b/src/test/java/edu/tamu/app/model/RemoteProjectManagerTest.java index aa6c6988..24f7ed3f 100644 --- a/src/test/java/edu/tamu/app/model/RemoteProjectManagerTest.java +++ b/src/test/java/edu/tamu/app/model/RemoteProjectManagerTest.java @@ -22,14 +22,14 @@ public class RemoteProjectManagerTest extends ModelTest { @Test public void testCreate() { RemoteProjectManager remoteProjectManager1 = remoteProjectManagerRepo.create(new RemoteProjectManager(TEST_REMOTE_PROJECT_MANAGER1_NAME, ServiceType.VERSION_ONE, TEST_PROJECT_URL1, TEST_PROJECT_TOKEN1)); - RemoteProjectManager remoteProjectManager2 = remoteProjectManagerRepo.create(new RemoteProjectManager(TEST_REMOTE_PROJECT_MANAGER2_NAME, ServiceType.GITHUB, TEST_PROJECT_URL2, TEST_PROJECT_TOKEN2)); + RemoteProjectManager remoteProjectManager2 = remoteProjectManagerRepo.create(new RemoteProjectManager(TEST_REMOTE_PROJECT_MANAGER2_NAME, ServiceType.GITHUB_PROJECT, TEST_PROJECT_URL2, TEST_PROJECT_TOKEN2)); assertEquals("Remote project manager repo had incorrect number of remote project managers!", 2, remoteProjectManagerRepo.count()); assertEquals("Remote project manager had incorrect name!", TEST_REMOTE_PROJECT_MANAGER1_NAME, remoteProjectManager1.getName()); assertEquals("Remote project manager had incorrect service type!", ServiceType.VERSION_ONE, remoteProjectManager1.getType()); assertEquals("Remote project manager had incorrect url!", TEST_PROJECT_URL1, remoteProjectManager1.getUrl()); assertEquals("Remote project manager had incorrect token!", TEST_PROJECT_TOKEN1, remoteProjectManager1.getToken()); assertEquals("Remote project manager had incorrect name!", TEST_REMOTE_PROJECT_MANAGER2_NAME, remoteProjectManager2.getName()); - assertEquals("Remote project manager had incorrect service type!", ServiceType.GITHUB, remoteProjectManager2.getType()); + assertEquals("Remote project manager had incorrect service type!", ServiceType.GITHUB_PROJECT, remoteProjectManager2.getType()); assertEquals("Remote project manager had incorrect url!", TEST_PROJECT_URL2, remoteProjectManager2.getUrl()); assertEquals("Remote project manager had incorrect token!", TEST_PROJECT_TOKEN2, remoteProjectManager2.getToken()); } diff --git a/src/test/java/edu/tamu/app/service/manager/GitHubMilestoneServiceTest.java b/src/test/java/edu/tamu/app/service/manager/GitHubMilestoneServiceTest.java new file mode 100644 index 00000000..3a7f09a6 --- /dev/null +++ b/src/test/java/edu/tamu/app/service/manager/GitHubMilestoneServiceTest.java @@ -0,0 +1,462 @@ +package edu.tamu.app.service.manager; + +import static edu.tamu.app.service.manager.GitHubMilestoneService.DEFECT_LABEL; +import static edu.tamu.app.service.manager.GitHubMilestoneService.FEATURE_LABEL; +import static edu.tamu.app.service.manager.GitHubMilestoneService.ISSUE_LABEL; +import static edu.tamu.app.service.manager.GitHubMilestoneService.REQUEST_LABEL; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Answers.RETURNS_DEEP_STUBS; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.springframework.test.util.ReflectionTestUtils.setField; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.kohsuke.github.GHIssue; +import org.kohsuke.github.GHIssueState; +import org.kohsuke.github.GHLabel; +import org.kohsuke.github.GHMilestone; +import org.kohsuke.github.GHMilestoneState; +import org.kohsuke.github.GHOrganization; +import org.kohsuke.github.GHProject; +import org.kohsuke.github.GHProject.ProjectStateFilter; +import org.kohsuke.github.GHProjectCard; +import org.kohsuke.github.GHProjectColumn; +import org.kohsuke.github.GHRepository; +import org.kohsuke.github.GHUser; +import org.kohsuke.github.GitHub; +import org.kohsuke.github.GitHubBuilder; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; + +import edu.tamu.app.cache.model.Member; +import edu.tamu.app.cache.model.RemoteProject; +import edu.tamu.app.cache.model.Sprint; +import edu.tamu.app.mapping.CardTypeMappingService; +import edu.tamu.app.mapping.EstimateMappingService; +import edu.tamu.app.mapping.StatusMappingService; +import edu.tamu.app.model.CardType; +import edu.tamu.app.model.Estimate; +import edu.tamu.app.model.ManagementService; +import edu.tamu.app.model.RemoteProjectManager; +import edu.tamu.app.model.ServiceType; +import edu.tamu.app.model.Status; +import edu.tamu.app.model.repo.CardTypeRepo; +import edu.tamu.app.model.repo.EstimateRepo; +import edu.tamu.app.model.repo.StatusRepo; +import edu.tamu.app.model.request.FeatureRequest; + +@RunWith(SpringRunner.class) +public class GitHubMilestoneServiceTest extends CacheMockTests { + private static final String TEST_REPOSITORY1_NAME = "Test repository 1 name"; + private static final String TEST_REPOSITORY2_NAME = "Test repository 2 name"; + private static final String TEST_UNUSED_LABEL_NAME = "unused"; + private static final String TEST_FEATURE_REQUEST_TITLE = "Feature request"; + private static final String TEST_FEATURE_REQUEST_DESCRIPTION = "Description of feature request"; + private static final String TEST_USER1_NAME = "User 1 name"; + private static final String TEST_USER1_AVATAR_PATH = "https://avatars2.githubusercontent.com/u/1234567?v=4"; + private static final String TEST_USER2_AVATAR_PATH = "https://avatars2.githubusercontent.com/u/2222222?v=4"; + private static final String TEST_USER3_AVATAR_PATH = "https://avatars2.githubusercontent.com/u/3333333?v=4"; + private static final String TEST_USER1_AVATAR_NAME = "1234567"; + private static final String TEST_COLUMN1_NAME = "Test Column 1"; + private static final String TEST_PROJECT1_NAME = "Test Project 1 Name"; + private static final String TEST_PROJECT2_NAME = "Test Project 2 Name"; + private static final String TEST_PROJECT3_NAME = "Test Project 3 Name"; + private static final String TEST_MILESTONE_TITLE = "Test Milestone Sprint Title"; + private static final Long TEST_REPOSITORY1_ID = 1L; + private static final Long TEST_USER1_ID = 3L; + + private static final String TEST_PROJECT_URL1 = "http://localhost/1"; + + private static final String TEST_PROJECT_TOKEN1 = "0123456789"; + + private static final GHLabel TEST_LABEL1 = mock(GHLabel.class); + private static final GHLabel TEST_LABEL2 = mock(GHLabel.class); + private static final GHLabel TEST_LABEL3 = mock(GHLabel.class); + private static final GHLabel TEST_LABEL4 = mock(GHLabel.class); + private static final GHLabel TEST_LABEL5 = mock(GHLabel.class); + + private static final GHIssue TEST_ISSUE1 = mock(GHIssue.class, RETURNS_DEEP_STUBS.get()); + private static final GHIssue TEST_ISSUE2 = mock(GHIssue.class, RETURNS_DEEP_STUBS.get()); + private static final GHIssue TEST_ISSUE3 = mock(GHIssue.class, RETURNS_DEEP_STUBS.get()); + private static final GHIssue TEST_ISSUE4 = mock(GHIssue.class, RETURNS_DEEP_STUBS.get()); + private static final GHIssue TEST_ISSUE5 = mock(GHIssue.class, RETURNS_DEEP_STUBS.get()); + + private static final GHUser TEST_USER1 = mock(GHUser.class); + private static final GHUser TEST_USER2 = mock(GHUser.class); + private static final GHUser TEST_USER3 = mock(GHUser.class); + + private static final GHMilestone TEST_MILESTONE = mock(GHMilestone.class); + + private static final GHProjectCard TEST_CARD1 = mock(GHProjectCard.class, RETURNS_DEEP_STUBS.get()); + private static final GHProjectCard TEST_CARD2 = mock(GHProjectCard.class, RETURNS_DEEP_STUBS.get()); + private static final GHProjectCard TEST_CARD3 = mock(GHProjectCard.class, RETURNS_DEEP_STUBS.get()); + private static final GHProjectCard TEST_CARD4 = mock(GHProjectCard.class, RETURNS_DEEP_STUBS.get()); + private static final GHProjectCard TEST_CARD5 = mock(GHProjectCard.class, RETURNS_DEEP_STUBS.get()); + + private static final GHProjectColumn TEST_COLUMN1 = mock(GHProjectColumn.class, RETURNS_DEEP_STUBS.get()); + private static final GHProjectColumn TEST_COLUMN2 = mock(GHProjectColumn.class, RETURNS_DEEP_STUBS.get()); + private static final GHProjectColumn TEST_COLUMN3 = mock(GHProjectColumn.class, RETURNS_DEEP_STUBS.get()); + + private static final GHProject TEST_PROJECT1 = mock(GHProject.class, RETURNS_DEEP_STUBS.get()); + private static final GHProject TEST_PROJECT2 = mock(GHProject.class, RETURNS_DEEP_STUBS.get()); + private static final GHProject TEST_PROJECT3 = mock(GHProject.class, RETURNS_DEEP_STUBS.get()); + + private static final GHRepository TEST_REPOSITORY1 = mock(GHRepository.class, RETURNS_DEEP_STUBS.get()); + private static final GHRepository TEST_REPOSITORY2 = mock(GHRepository.class, RETURNS_DEEP_STUBS.get()); + + private static final GHOrganization TEST_ORGANIZATION = mock(GHOrganization.class, RETURNS_DEEP_STUBS.get()); + + private static final FeatureRequest TEST_FEATURE_REQUEST = mock(FeatureRequest.class); + + private static final RestTemplate restTemplate = mock(RestTemplate.class); + + @SuppressWarnings("unchecked") + private ResponseEntity response = (ResponseEntity) mock(ResponseEntity.class); + + private static final List ALL_TEST_LABELS = new ArrayList( + Arrays.asList(new GHLabel[] { TEST_LABEL1, TEST_LABEL2, TEST_LABEL3, TEST_LABEL4, TEST_LABEL5 })); + private static final List TEST_CARD1_LABELS = new ArrayList( + Arrays.asList(new GHLabel[] { TEST_LABEL1, TEST_LABEL5 })); + private static final List TEST_CARD2_LABELS = new ArrayList( + Arrays.asList(new GHLabel[] { TEST_LABEL2, TEST_LABEL5 })); + private static final List TEST_CARD3_LABELS = new ArrayList( + Arrays.asList(new GHLabel[] { TEST_LABEL3, TEST_LABEL5 })); + private static final List TEST_CARD4_LABELS = new ArrayList( + Arrays.asList(new GHLabel[] { TEST_LABEL4 })); + private static final List TEST_CARD5_LABELS = new ArrayList( + Arrays.asList(new GHLabel[] { TEST_LABEL5 })); + + private static final List TEST_USERS1 = new ArrayList(Arrays.asList(new GHUser[] { TEST_USER1 })); + private static final List TEST_USERS2 = new ArrayList( + Arrays.asList(new GHUser[] { TEST_USER1, TEST_USER2 })); + private static final List TEST_USERS3 = new ArrayList(Arrays.asList(new GHUser[] {})); + private static final List TEST_USERS4 = new ArrayList(Arrays.asList(new GHUser[] { TEST_USER2 })); + private static final List TEST_USERS5 = new ArrayList( + Arrays.asList(new GHUser[] { TEST_USER3, TEST_USER1 })); + + private static final List TEST_COLUMN1_CARDS = new ArrayList( + Arrays.asList(new GHProjectCard[] { TEST_CARD1, TEST_CARD2, TEST_CARD3 })); + private static final List TEST_COLUMN2_CARDS = new ArrayList( + Arrays.asList(new GHProjectCard[] { TEST_CARD3, TEST_CARD4 })); + private static final List TEST_COLUMN3_CARDS = new ArrayList( + Arrays.asList(new GHProjectCard[] { TEST_CARD5 })); + + private static final List TEST_ISSUE_LIST = new ArrayList( + Arrays.asList((new GHIssue[] { TEST_ISSUE1, TEST_ISSUE2, TEST_ISSUE3, TEST_ISSUE4, TEST_ISSUE5 }))); + + private static final List TEST_PROJECT_COLUMNS = new ArrayList( + Arrays.asList(new GHProjectColumn[] { TEST_COLUMN1, TEST_COLUMN2, TEST_COLUMN3 })); + + private static final List TEST_PROJECTS = new ArrayList( + Arrays.asList(new GHProject[] { TEST_PROJECT1, TEST_PROJECT2, TEST_PROJECT3 })); + + private static final Map TEST_REPOSITORY_MAP = Stream.of( + new Object[][] { { TEST_REPOSITORY1_NAME, TEST_REPOSITORY1 }, { TEST_REPOSITORY2_NAME, TEST_REPOSITORY2 } }) + .collect(Collectors.toMap(data -> (String) data[0], data -> (GHRepository) data[1])); + + private GitHubBuilder ghBuilder; + + private GitHubMilestoneService gitHubMilestoneService; + + private GitHub github; + + @Before + public void setUp() throws Exception { + ManagementService managementService = new RemoteProjectManager("GitHub", ServiceType.GITHUB_MILESTONE, TEST_PROJECT_URL1, TEST_PROJECT_TOKEN1); + + + CardTypeRepo cardTypeRepo = mock(CardTypeRepo.class); + StatusRepo statusRepo = mock(StatusRepo.class); + EstimateRepo estimateRepo = mock(EstimateRepo.class); + + CardTypeMappingService cardTypeMappingService = mock(CardTypeMappingService.class, Mockito.CALLS_REAL_METHODS); + + StatusMappingService statusMappingService = mock(StatusMappingService.class, Mockito.CALLS_REAL_METHODS); + + EstimateMappingService estimateMappingService = mock(EstimateMappingService.class, Mockito.CALLS_REAL_METHODS); + + ghBuilder = mock(GitHubBuilder.class); + + gitHubMilestoneService = mock(GitHubMilestoneService.class, Mockito.CALLS_REAL_METHODS); + + github = mock(GitHub.class); + + when(ghBuilder.withEndpoint(any(String.class))).thenReturn(ghBuilder); + when(ghBuilder.withOAuthToken(any(String.class))).thenReturn(ghBuilder); + when(ghBuilder.build()).thenReturn(github); + + when(github.getOrganization(any(String.class))).thenReturn(TEST_ORGANIZATION); + when(github.getRepositoryById(any(String.class))).thenReturn(TEST_REPOSITORY1); + + when(TEST_ORGANIZATION.getRepositories()).thenReturn(TEST_REPOSITORY_MAP); + when(TEST_ORGANIZATION.listProjects(any(ProjectStateFilter.class)).asList()).thenReturn(TEST_PROJECTS); + + when(TEST_REPOSITORY1.getId()).thenReturn(TEST_REPOSITORY1_ID); + when(TEST_REPOSITORY1.createIssue(any(String.class)).body(any(String.class)).create()).thenReturn(TEST_ISSUE1); + when(TEST_REPOSITORY1.listProjects(any(ProjectStateFilter.class)).asList()).thenReturn(TEST_PROJECTS); + when(TEST_REPOSITORY1.listProjects().asList()).thenReturn(TEST_PROJECTS); + when(TEST_REPOSITORY1.listIssues(any(GHIssueState.class)).asList()).thenReturn(TEST_ISSUE_LIST); + when(TEST_REPOSITORY2.listIssues(any(GHIssueState.class)).asList()).thenReturn(TEST_ISSUE_LIST); + when(TEST_REPOSITORY2.listProjects().asList()).thenReturn(TEST_PROJECTS); + when(TEST_REPOSITORY1.listLabels().asList()).thenReturn(ALL_TEST_LABELS); + when(TEST_REPOSITORY2.listLabels().asList()).thenReturn(ALL_TEST_LABELS); + + when(TEST_PROJECT1.listColumns().asList()).thenReturn(TEST_PROJECT_COLUMNS); + when(TEST_PROJECT2.listColumns().asList()).thenReturn(TEST_PROJECT_COLUMNS); + when(TEST_PROJECT3.listColumns().asList()).thenReturn(TEST_PROJECT_COLUMNS); + + when(TEST_PROJECT1.getName()).thenReturn(TEST_PROJECT1_NAME); + when(TEST_PROJECT2.getName()).thenReturn(TEST_PROJECT2_NAME); + when(TEST_PROJECT3.getName()).thenReturn(TEST_PROJECT3_NAME); + + when(TEST_COLUMN1.listCards().asList()).thenReturn(TEST_COLUMN1_CARDS); + when(TEST_COLUMN2.listCards().asList()).thenReturn(TEST_COLUMN2_CARDS); + when(TEST_COLUMN3.listCards().asList()).thenReturn(TEST_COLUMN3_CARDS); + + when(TEST_MILESTONE.getState()).thenReturn(GHMilestoneState.OPEN); + when(TEST_MILESTONE.getTitle()).thenReturn(TEST_MILESTONE_TITLE); + + when(TEST_CARD1.getId()).thenReturn(1L); + when(TEST_CARD2.getId()).thenReturn(2L); + when(TEST_CARD3.getId()).thenReturn(3L); + when(TEST_CARD4.getId()).thenReturn(4L); + when(TEST_CARD5.getId()).thenReturn(5L); + + when(TEST_CARD1.getContent()).thenReturn(TEST_ISSUE1); + when(TEST_CARD2.getContent()).thenReturn(TEST_ISSUE2); + when(TEST_CARD3.getContent()).thenReturn(TEST_ISSUE3); + when(TEST_CARD4.getContent()).thenReturn(TEST_ISSUE4); + when(TEST_CARD5.getContent()).thenReturn(TEST_ISSUE5); + when(TEST_CARD1.getColumn()).thenReturn(TEST_COLUMN1); + + when(TEST_ISSUE1.getLabels()).thenReturn(TEST_CARD1_LABELS); + when(TEST_ISSUE2.getLabels()).thenReturn(TEST_CARD2_LABELS); + when(TEST_ISSUE3.getLabels()).thenReturn(TEST_CARD3_LABELS); + when(TEST_ISSUE4.getLabels()).thenReturn(TEST_CARD4_LABELS); + when(TEST_ISSUE5.getLabels()).thenReturn(TEST_CARD5_LABELS); + when(TEST_ISSUE1.getAssignees()).thenReturn(TEST_USERS1); + when(TEST_ISSUE1.getMilestone()).thenReturn(TEST_MILESTONE); + when(TEST_ISSUE2.getMilestone()).thenReturn(TEST_MILESTONE); + when(TEST_ISSUE3.getMilestone()).thenReturn(TEST_MILESTONE); + when(TEST_ISSUE4.getMilestone()).thenReturn(TEST_MILESTONE); + when(TEST_ISSUE5.getMilestone()).thenReturn(TEST_MILESTONE); + + when(TEST_COLUMN1.getName()).thenReturn(TEST_COLUMN1_NAME); + + when(TEST_CARD2.getContent().getLabels()).thenReturn(TEST_CARD2_LABELS); + when(TEST_CARD3.getContent().getLabels()).thenReturn(TEST_CARD3_LABELS); + when(TEST_CARD4.getContent().getLabels()).thenReturn(TEST_CARD4_LABELS); + when(TEST_CARD5.getContent().getLabels()).thenReturn(TEST_CARD5_LABELS); + when(TEST_CARD2.getContent().getAssignees()).thenReturn(TEST_USERS2); + when(TEST_CARD3.getContent().getAssignees()).thenReturn(TEST_USERS3); + when(TEST_CARD4.getContent().getAssignees()).thenReturn(TEST_USERS4); + when(TEST_CARD5.getContent().getAssignees()).thenReturn(TEST_USERS5); + + when(TEST_USER1.getId()).thenReturn(TEST_USER1_ID); + when(TEST_USER1.getName()).thenReturn(TEST_USER1_NAME); + when(TEST_USER1.getAvatarUrl()).thenReturn(TEST_USER1_AVATAR_PATH); + when(TEST_USER2.getAvatarUrl()).thenReturn(TEST_USER2_AVATAR_PATH); + when(TEST_USER3.getAvatarUrl()).thenReturn(TEST_USER3_AVATAR_PATH); + + when(TEST_LABEL1.getName()).thenReturn(REQUEST_LABEL); + when(TEST_LABEL2.getName()).thenReturn(ISSUE_LABEL); + when(TEST_LABEL3.getName()).thenReturn(FEATURE_LABEL); + when(TEST_LABEL4.getName()).thenReturn(DEFECT_LABEL); + when(TEST_LABEL5.getName()).thenReturn(TEST_UNUSED_LABEL_NAME); + + when(TEST_FEATURE_REQUEST.getProductId()).thenReturn(TEST_REPOSITORY1_ID); + when(TEST_FEATURE_REQUEST.getTitle()).thenReturn(TEST_FEATURE_REQUEST_TITLE); + when(TEST_FEATURE_REQUEST.getDescription()).thenReturn(TEST_FEATURE_REQUEST_DESCRIPTION); + + when(restTemplate.exchange( + any(String.class), + any(HttpMethod.class), + Mockito.>any(), + Mockito.>any(), + Mockito.anyVararg())) + .thenReturn(response); + + when(response.getStatusCode()).thenReturn(HttpStatus.NOT_FOUND); + + when(cardTypeRepo.findByMapping(any(String.class))).thenAnswer(new Answer>() { + @Override + public Optional answer(InvocationOnMock invocation) { + String identifier = (String) invocation.getArguments()[0]; + Optional cardType = Optional.empty(); + switch (identifier) { + case "Story": + cardType = Optional + .of(new CardType("Feature", new HashSet(Arrays.asList(new String[] { "Story" })))); + break; + case "Defect": + cardType = Optional + .of(new CardType("Defect", new HashSet(Arrays.asList(new String[] { "Defect" })))); + break; + } + return cardType; + } + }); + + when(cardTypeRepo.findByIdentifier(any(String.class))) + .thenReturn(new CardType("Feature", new HashSet(Arrays.asList(new String[] { "Story" })))); + + when(statusRepo.findByMapping(any(String.class))).thenAnswer(new Answer>() { + @Override + public Optional answer(InvocationOnMock invocation) { + String identifier = (String) invocation.getArguments()[0]; + Optional status = Optional.empty(); + switch (identifier) { + case "None": + case "Feature": + status = Optional.of( + new Status("None", new HashSet(Arrays.asList(new String[] { "None", "Future" })))); + break; + case "In Progress": + status = Optional.of(new Status("In Progress", + new HashSet(Arrays.asList(new String[] { "In Progress" })))); + break; + case "Done": + status = Optional + .of(new Status("Done", new HashSet(Arrays.asList(new String[] { "Done" })))); + break; + case "Accepted": + status = Optional.of( + new Status("Accepted", new HashSet(Arrays.asList(new String[] { "Accepted" })))); + break; + } + return status; + } + }); + + when(estimateRepo.findByMapping(any(String.class))).thenAnswer(new Answer>() { + @Override + public Optional answer(InvocationOnMock invocation) { + return Optional.empty(); + } + }); + + when(statusRepo.findByIdentifier(any(String.class))) + .thenReturn(new Status("None", new HashSet(Arrays.asList(new String[] { "None", "Future" })))); + + setField(cardTypeMappingService, "serviceMappingRepo", cardTypeRepo); + setField(statusMappingService, "serviceMappingRepo", statusRepo); + setField(estimateMappingService, "serviceMappingRepo", estimateRepo); + + setField(gitHubMilestoneService, "ghBuilder", ghBuilder); + setField(gitHubMilestoneService, "managementService", managementService); + setField(gitHubMilestoneService, "cardTypeMappingService", cardTypeMappingService); + setField(gitHubMilestoneService, "statusMappingService", statusMappingService); + setField(gitHubMilestoneService, "estimateMappingService", estimateMappingService); + setField(gitHubMilestoneService, "github", github); + setField(gitHubMilestoneService, "members", new HashMap()); + setField(gitHubMilestoneService, "restTemplate", restTemplate); + } + + @Test + public void testGetRemoteProjects() throws Exception { + List remoteProjects = gitHubMilestoneService.getRemoteProject(); + assertEquals("Didn't get all the remote projects", 2, remoteProjects.size()); + assertEquals("Number of Requests was incorrect", 1, remoteProjects.get(0).getRequestCount()); + assertEquals("Number of Issues was incorrect", 2, remoteProjects.get(0).getIssueCount()); + assertEquals("Number of Features was incorrect", 1, remoteProjects.get(0).getFeatureCount()); + assertEquals("Number of Defects was incorrect", 1, remoteProjects.get(0).getDefectCount()); + } + + @Test + public void testGetRemoteProjectByScopeId() throws Exception { + RemoteProject project = gitHubMilestoneService.getRemoteProjectByScopeId(String.valueOf(TEST_REPOSITORY1_ID)); + assertNotNull("Didn't get the remote project", project); + assertEquals("Did not get the expected project", String.valueOf(TEST_REPOSITORY1_ID), project.getId()); + assertEquals("Number of Requests was incorrect", 1, project.getRequestCount()); + assertEquals("Number of Issues was incorrect", 2, project.getIssueCount()); + assertEquals("Number of Features was incorrect", 1, project.getFeatureCount()); + assertEquals("Number of Defects was incorrect", 1, project.getDefectCount()); + } + + @Test + public void testGetActiveSprintsByProjectId() throws Exception { + List activeSprints = gitHubMilestoneService.getActiveSprintsByScopeId(String.valueOf(TEST_REPOSITORY1_ID)); + assertEquals("Didn't get all active sprints", 3, activeSprints.size()); + } + + @Test + public void testGetAdditionalActiveSprints() throws Exception { + List additionalSprints = gitHubMilestoneService.getAdditionalActiveSprints(); + assertEquals("Didn't get all additional sprints", 3, additionalSprints.size()); + } + + @Test + public void testPush() throws Exception { + String id = gitHubMilestoneService.push(TEST_FEATURE_REQUEST); + assertNotNull(id); + } + + @Test + public void testGetMember() throws IOException { + Member member = gitHubMilestoneService.getMember(TEST_USER1); + assertEquals("Member ID is incorrect", String.valueOf(TEST_USER1_ID), member.getId()); + assertEquals("Member Name is incorrect", TEST_USER1_NAME, member.getName()); + assertEquals("Member Avatar URL is incorrect", TEST_USER1_AVATAR_NAME, member.getAvatar()); + } + + @Test + public void testGetGitHubInstanceWithInvalidServiceEndpoint() throws IOException { + ManagementService invalidManagementService = new RemoteProjectManager("GitHub", ServiceType.GITHUB_MILESTONE, TEST_PROJECT_URL1, TEST_PROJECT_TOKEN1); + + setField(gitHubMilestoneService, "managementService", invalidManagementService); + + try { + gitHubMilestoneService.getGitHubInstance(); + } catch (RuntimeException e) { + assertEquals(e.getMessage(), "GitHub service endpoint was not defined"); + } + } + + @Test + public void testGetGitHubInstanceWithInvalidToken() throws IOException { + ManagementService invalidManagementService = new RemoteProjectManager("GitHub", ServiceType.GITHUB_MILESTONE, TEST_PROJECT_URL1, TEST_PROJECT_TOKEN1); + + setField(gitHubMilestoneService, "managementService", invalidManagementService); + + try { + gitHubMilestoneService.getGitHubInstance(); + } catch (RuntimeException e) { + assertEquals(e.getMessage(), "GitHub token was not defined"); + } + } + + @Test + public void testGetGitHubInstanceByToken() throws IOException { + GitHub gitHubInstance = gitHubMilestoneService.getGitHubInstance(); + assertNotNull("GitHub object was not created", gitHubInstance); + } + + @Test + public void testGetCardsWithNote() throws Exception { + when(TEST_CARD1.getContent()).thenReturn(null); + List sprints = gitHubMilestoneService.getAdditionalActiveSprints(); + assertEquals("Didn't get expected number of cards", 5, sprints.get(0).getCards().size()); + } +} \ No newline at end of file diff --git a/src/test/java/edu/tamu/app/service/manager/GitHubServiceTest.java b/src/test/java/edu/tamu/app/service/manager/GitHubProjectServiceTest.java similarity index 87% rename from src/test/java/edu/tamu/app/service/manager/GitHubServiceTest.java rename to src/test/java/edu/tamu/app/service/manager/GitHubProjectServiceTest.java index 50fd1981..658837b8 100644 --- a/src/test/java/edu/tamu/app/service/manager/GitHubServiceTest.java +++ b/src/test/java/edu/tamu/app/service/manager/GitHubProjectServiceTest.java @@ -1,9 +1,9 @@ package edu.tamu.app.service.manager; -import static edu.tamu.app.service.manager.GitHubService.DEFECT_LABEL; -import static edu.tamu.app.service.manager.GitHubService.FEATURE_LABEL; -import static edu.tamu.app.service.manager.GitHubService.ISSUE_LABEL; -import static edu.tamu.app.service.manager.GitHubService.REQUEST_LABEL; +import static edu.tamu.app.service.manager.GitHubProjectService.DEFECT_LABEL; +import static edu.tamu.app.service.manager.GitHubProjectService.FEATURE_LABEL; +import static edu.tamu.app.service.manager.GitHubProjectService.ISSUE_LABEL; +import static edu.tamu.app.service.manager.GitHubProjectService.REQUEST_LABEL; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.Answers.RETURNS_DEEP_STUBS; @@ -66,7 +66,7 @@ import edu.tamu.app.model.request.FeatureRequest; @RunWith(SpringRunner.class) -public class GitHubServiceTest extends CacheMockTests { +public class GitHubProjectServiceTest extends CacheMockTests { private static final String TEST_REPOSITORY1_NAME = "Test repository 1 name"; private static final String TEST_REPOSITORY2_NAME = "Test repository 2 name"; private static final String TEST_UNUSED_LABEL_NAME = "unused"; @@ -78,6 +78,9 @@ public class GitHubServiceTest extends CacheMockTests { private static final String TEST_USER3_AVATAR_PATH = "https://avatars2.githubusercontent.com/u/3333333?v=4"; private static final String TEST_USER1_AVATAR_NAME = "1234567"; private static final String TEST_COLUMN1_NAME = "Test Column 1"; + private static final String TEST_PROJECT1_NAME = "Test Project 1 Sprint Name"; + private static final String TEST_PROJECT2_NAME = "Test Project 2 Sprint Name"; + private static final String TEST_PROJECT3_NAME = "Test Project 3 Sprint Name"; private static final Long TEST_REPOSITORY1_ID = 1L; private static final Long TEST_USER1_ID = 3L; @@ -170,13 +173,13 @@ public class GitHubServiceTest extends CacheMockTests { private GitHubBuilder ghBuilder; - private GitHubService gitHubService; + private GitHubProjectService gitHubProjectService; private GitHub github; @Before public void setUp() throws Exception { - ManagementService managementService = new RemoteProjectManager("GitHub", ServiceType.GITHUB, TEST_PROJECT_URL1, TEST_PROJECT_TOKEN1); + ManagementService managementService = new RemoteProjectManager("GitHub", ServiceType.GITHUB_PROJECT, TEST_PROJECT_URL1, TEST_PROJECT_TOKEN1); CardTypeRepo cardTypeRepo = mock(CardTypeRepo.class); @@ -191,7 +194,7 @@ public void setUp() throws Exception { ghBuilder = mock(GitHubBuilder.class); - gitHubService = mock(GitHubService.class, Mockito.CALLS_REAL_METHODS); + gitHubProjectService = mock(GitHubProjectService.class, Mockito.CALLS_REAL_METHODS); github = mock(GitHub.class); @@ -219,6 +222,10 @@ public void setUp() throws Exception { when(TEST_PROJECT2.listColumns().asList()).thenReturn(TEST_PROJECT_COLUMNS); when(TEST_PROJECT3.listColumns().asList()).thenReturn(TEST_PROJECT_COLUMNS); + when(TEST_PROJECT1.getName()).thenReturn(TEST_PROJECT1_NAME); + when(TEST_PROJECT2.getName()).thenReturn(TEST_PROJECT2_NAME); + when(TEST_PROJECT3.getName()).thenReturn(TEST_PROJECT3_NAME); + when(TEST_COLUMN1.listCards().asList()).thenReturn(TEST_COLUMN1_CARDS); when(TEST_COLUMN2.listCards().asList()).thenReturn(TEST_COLUMN2_CARDS); when(TEST_COLUMN3.listCards().asList()).thenReturn(TEST_COLUMN3_CARDS); @@ -335,19 +342,19 @@ public Optional answer(InvocationOnMock invocation) { setField(statusMappingService, "serviceMappingRepo", statusRepo); setField(estimateMappingService, "serviceMappingRepo", estimateRepo); - setField(gitHubService, "ghBuilder", ghBuilder); - setField(gitHubService, "managementService", managementService); - setField(gitHubService, "cardTypeMappingService", cardTypeMappingService); - setField(gitHubService, "statusMappingService", statusMappingService); - setField(gitHubService, "estimateMappingService", estimateMappingService); - setField(gitHubService, "github", github); - setField(gitHubService, "members", new HashMap()); - setField(gitHubService, "restTemplate", restTemplate); + setField(gitHubProjectService, "ghBuilder", ghBuilder); + setField(gitHubProjectService, "managementService", managementService); + setField(gitHubProjectService, "cardTypeMappingService", cardTypeMappingService); + setField(gitHubProjectService, "statusMappingService", statusMappingService); + setField(gitHubProjectService, "estimateMappingService", estimateMappingService); + setField(gitHubProjectService, "github", github); + setField(gitHubProjectService, "members", new HashMap()); + setField(gitHubProjectService, "restTemplate", restTemplate); } @Test public void testGetRemoteProjects() throws Exception { - List remoteProjects = gitHubService.getRemoteProject(); + List remoteProjects = gitHubProjectService.getRemoteProject(); assertEquals("Didn't get all the remote projects", 2, remoteProjects.size()); assertEquals("Number of Requests was incorrect", 1, remoteProjects.get(0).getRequestCount()); assertEquals("Number of Issues was incorrect", 2, remoteProjects.get(0).getIssueCount()); @@ -357,7 +364,7 @@ public void testGetRemoteProjects() throws Exception { @Test public void testGetRemoteProjectByScopeId() throws Exception { - RemoteProject project = gitHubService.getRemoteProjectByScopeId(String.valueOf(TEST_REPOSITORY1_ID)); + RemoteProject project = gitHubProjectService.getRemoteProjectByScopeId(String.valueOf(TEST_REPOSITORY1_ID)); assertNotNull("Didn't get the remote project", project); assertEquals("Did not get the expected project", String.valueOf(TEST_REPOSITORY1_ID), project.getId()); assertEquals("Number of Requests was incorrect", 1, project.getRequestCount()); @@ -368,25 +375,25 @@ public void testGetRemoteProjectByScopeId() throws Exception { @Test public void testGetActiveSprintsByProjectId() throws Exception { - List activeSprints = gitHubService.getActiveSprintsByScopeId(String.valueOf(TEST_REPOSITORY1_ID)); + List activeSprints = gitHubProjectService.getActiveSprintsByScopeId(String.valueOf(TEST_REPOSITORY1_ID)); assertEquals("Didn't get all active sprints", 3, activeSprints.size()); } @Test public void testGetAdditionalActiveSprints() throws Exception { - List additionalSprints = gitHubService.getAdditionalActiveSprints(); + List additionalSprints = gitHubProjectService.getAdditionalActiveSprints(); assertEquals("Didn't get all additional sprints", 3, additionalSprints.size()); } @Test public void testPush() throws Exception { - String id = gitHubService.push(TEST_FEATURE_REQUEST); + String id = gitHubProjectService.push(TEST_FEATURE_REQUEST); assertNotNull(id); } @Test public void testGetMember() throws IOException { - Member member = gitHubService.getMember(TEST_USER1); + Member member = gitHubProjectService.getMember(TEST_USER1); assertEquals("Member ID is incorrect", String.valueOf(TEST_USER1_ID), member.getId()); assertEquals("Member Name is incorrect", TEST_USER1_NAME, member.getName()); assertEquals("Member Avatar URL is incorrect", TEST_USER1_AVATAR_NAME, member.getAvatar()); @@ -394,12 +401,12 @@ public void testGetMember() throws IOException { @Test public void testGetGitHubInstanceWithInvalidServiceEndpoint() throws IOException { - ManagementService invalidManagementService = new RemoteProjectManager("GitHub", ServiceType.GITHUB, TEST_PROJECT_URL1, TEST_PROJECT_TOKEN1); + ManagementService invalidManagementService = new RemoteProjectManager("GitHub", ServiceType.GITHUB_PROJECT, TEST_PROJECT_URL1, TEST_PROJECT_TOKEN1); - setField(gitHubService, "managementService", invalidManagementService); + setField(gitHubProjectService, "managementService", invalidManagementService); try { - gitHubService.getGitHubInstance(); + gitHubProjectService.getGitHubInstance(); } catch (RuntimeException e) { assertEquals(e.getMessage(), "GitHub service endpoint was not defined"); } @@ -407,12 +414,12 @@ public void testGetGitHubInstanceWithInvalidServiceEndpoint() throws IOException @Test public void testGetGitHubInstanceWithInvalidToken() throws IOException { - ManagementService invalidManagementService = new RemoteProjectManager("GitHub", ServiceType.GITHUB, TEST_PROJECT_URL1, TEST_PROJECT_TOKEN1); + ManagementService invalidManagementService = new RemoteProjectManager("GitHub", ServiceType.GITHUB_PROJECT, TEST_PROJECT_URL1, TEST_PROJECT_TOKEN1); - setField(gitHubService, "managementService", invalidManagementService); + setField(gitHubProjectService, "managementService", invalidManagementService); try { - gitHubService.getGitHubInstance(); + gitHubProjectService.getGitHubInstance(); } catch (RuntimeException e) { assertEquals(e.getMessage(), "GitHub token was not defined"); } @@ -420,14 +427,14 @@ public void testGetGitHubInstanceWithInvalidToken() throws IOException { @Test public void testGetGitHubInstanceByToken() throws IOException { - GitHub gitHubInstance = gitHubService.getGitHubInstance(); + GitHub gitHubInstance = gitHubProjectService.getGitHubInstance(); assertNotNull("GitHub object was not created", gitHubInstance); } @Test public void testGetCardsWithNote() throws Exception { when(TEST_CARD1.getContent()).thenReturn(null); - List sprints = gitHubService.getAdditionalActiveSprints(); + List sprints = gitHubProjectService.getAdditionalActiveSprints(); assertEquals("Didn't get expected number of cards", 5, sprints.get(0).getCards().size()); } } \ No newline at end of file