package com.metservice.kanban.model; import static com.metservice.kanban.model.WorkItem.ROOT_WORK_ITEM_ID; import static com.metservice.kanban.utils.DateUtils.parseIsoDate; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.isNull; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Map; import org.hamcrest.core.IsNull; import org.joda.time.LocalDate; import org.junit.Before; import org.junit.Test; import com.metservice.kanban.utils.WorkingDayUtils; public class WorkItemTest { private static LocalDateFormatter formatter = new LocalDateFormatter(); private static class LocalDateFormatter { public LocalDate parse(String pattern) throws ParseException { return LocalDate.fromDateFields(new SimpleDateFormat("dd/MM/yyyy").parse(pattern)); } } private WorkItemType type; @Before public void setup() { type = new WorkItemType("feature", "phase1", "phase2", "phase3"); } @Test public void testPhaseOnDateNull() throws ParseException { // TODO Use TestKanbanBoardBuilder to construct test data instead WorkItem workItem = new WorkItem(1, type); assertThat(workItem.getPhaseOnDate(formatter.parse("14/02/2011")), nullValue()); } @Test public void testTruncatedName() throws ParseException { WorkItem workItem = new WorkItem(1, type); // 1234567890123456879012345687901234567890 assertThat(workItem.getTruncatedName(), is(workItem.getName())); workItem.setName("Very long name, Very long name, Very long name, Very long name, Very long name, Very long name, Very long name...."); assertThat(workItem.getTruncatedName(), is("Very long name, Very long name, Very lon")); workItem.setName("Short name"); assertThat(workItem.getTruncatedName(), is("Short name")); } @Test public void testPhaseOnDate() throws ParseException { // TODO Use TestKanbanBoardBuilder to construct test data instead WorkItem workItem = new WorkItem(1, type); workItem.setDate("phase1", formatter.parse("14/02/2011")); workItem.setDate("phase2", formatter.parse("18/02/2011")); assertThat(workItem.getPhaseOnDate(formatter.parse("14/02/2011")), is("phase1")); assertThat(workItem.getPhaseOnDate(formatter.parse("15/02/2011")), is("phase1")); assertThat(workItem.getPhaseOnDate(formatter.parse("16/02/2011")), is("phase1")); assertThat(workItem.getPhaseOnDate(formatter.parse("17/02/2011")), is("phase1")); assertThat(workItem.getPhaseOnDate(formatter.parse("18/02/2011")), is("phase2")); assertThat(workItem.getPhaseOnDate(formatter.parse("19/02/2011")), is("phase2")); assertThat(workItem.getPhaseOnDate(formatter.parse("20/02/2011")), is("phase2")); assertThat(workItem.getPhaseOnDate(formatter.parse("20/02/2012")), is("phase2")); workItem.setDate("phase3", formatter.parse("19/02/2011")); assertThat(workItem.getPhaseOnDate(formatter.parse("18/02/2011")), is("phase2")); assertThat(workItem.getPhaseOnDate(formatter.parse("19/02/2011")), is("phase3")); assertThat(workItem.getPhaseOnDate(formatter.parse("20/02/2011")), is("phase3")); assertThat(workItem.getPhaseOnDate(formatter.parse("20/02/2012")), is("phase3")); } @Test public void testGetLastPhaseDate() throws ParseException { WorkItem workItem = new WorkItem(1, type); assertThat(workItem.getLastPhaseDate(), is(nullValue())); workItem.advance(formatter.parse("20/01/2012")); assertThat(workItem.getLastPhaseDate(), is(formatter.parse("20/01/2012"))); assertThat(workItem.getCurrentPhase(), is("feature")); workItem.advance(formatter.parse("21/01/2012")); assertThat(workItem.getLastPhaseDate(), is(formatter.parse("21/01/2012"))); assertThat(workItem.getCurrentPhase(), is("phase1")); workItem.advance(formatter.parse("22/01/2012")); assertThat(workItem.getLastPhaseDate(), is(formatter.parse("22/01/2012"))); assertThat(workItem.getCurrentPhase(), is("phase2")); workItem.advance(formatter.parse("23/01/2012")); assertThat(workItem.getLastPhaseDate(), is(formatter.parse("23/01/2012"))); assertThat(workItem.getCurrentPhase(), is("phase3")); } @Test public void testIsInFinalPhase() throws ParseException { // TODO Use TestKanbanBoardBuilder to construct test data instead WorkItem workItem = new WorkItem(1, type); assertThat(workItem.isCompleted(), is(false)); workItem.setDate("phase1", formatter.parse("14/02/2011")); assertThat(workItem.isCompleted(), is(false)); workItem.setDate("phase2", formatter.parse("18/02/2011")); assertThat(workItem.isCompleted(), is(false)); workItem.setDate("phase3", formatter.parse("20/02/2011")); assertThat(workItem.isCompleted(), is(true)); } @Test public void testAdvance() throws ParseException { // TODO Use TestKanbanBoardBuilder to construct test data instead WorkItem workItem = new WorkItem(1, type); workItem.setDate("phase1", formatter.parse("10/02/2011")); assertThat(workItem.getPhaseOnDate(formatter.parse("10/02/2011")), is("phase1")); assertThat(workItem.getPhaseOnDate(formatter.parse("11/02/2011")), is("phase1")); assertThat(workItem.getPhaseOnDate(formatter.parse("12/02/2011")), is("phase1")); workItem.advance(formatter.parse("11/02/2011")); assertThat(workItem.getPhaseOnDate(formatter.parse("10/02/2011")), is("phase1")); assertThat(workItem.getPhaseOnDate(formatter.parse("11/02/2011")), is("phase2")); assertThat(workItem.getPhaseOnDate(formatter.parse("12/02/2011")), is("phase2")); } @Test public void testStop() throws ParseException { WorkItem workItem = new WorkItem(1, type); workItem.setDate("phase1", formatter.parse("10/02/2011")); assertFalse(workItem.isBlocked()); workItem.stop(); assertTrue(workItem.isBlocked()); } @Test(expected = IllegalStateException.class) public void testInvalidAdvance() throws ParseException { // TODO Use TestKanbanBoardBuilder to construct test data instead WorkItem workItem = new WorkItem(1, type); workItem.setDate("phase1", formatter.parse("10/02/2011")); workItem.setDate("phase2", formatter.parse("12/02/2011")); workItem.setDate("phase3", formatter.parse("13/02/2011")); workItem.advance(formatter.parse("11/02/2011")); } @Test(expected = IllegalStateException.class) public void cannotGetCurrentPhaseIfAdvanceWasNeverCalled() { // Note: this behaviour is great for error detection in tests, if this behaviour becomes inconvenient, consider // adding a hasCurrentPhase() method WorkItemType featureType = new WorkItemType("phase"); WorkItem feature = new WorkItem(1, featureType); feature.getCurrentPhase(); } @Test public void canChangeParentByCreatingNewInstance() { WorkItemType featureType = new WorkItemType("phase"); WorkItem workItem = new WorkItem(1, featureType, "phase"); WorkItemTree tree = new DefaultWorkItemTree(); tree.addWorkItem(workItem); int newParentId = tree.getNewId(); WorkItem reparentedWorkItem = workItem.withNewParent(newParentId); assertThat(reparentedWorkItem.getId(), is(workItem.getId())); assertThat(reparentedWorkItem.getParentId(), is(newParentId)); assertThat(reparentedWorkItem.getType(), is(workItem.getType())); assertThat(reparentedWorkItem.getName(), is(workItem.getName())); assertThat(reparentedWorkItem.getAverageCaseEstimate(), is(workItem.getAverageCaseEstimate())); assertThat(reparentedWorkItem.getImportance(), is(workItem.getImportance())); assertThat(reparentedWorkItem.getNotes(), is(workItem.getNotes())); assertThat(reparentedWorkItem.getDate("phase"), is(workItem.getDate("phase"))); assertThat(reparentedWorkItem.getCurrentPhase(), is("phase")); } @Test public void canConstructAlreadyAdvancedToAGivenPhase() { WorkItemType type = new WorkItemType("phase 1", "phase 2"); WorkItem workItem = new WorkItem(2, 1, type, "phase 2"); assertThat(workItem.getId(), is(2)); assertThat(workItem.getParentId(), is(1)); assertThat(workItem.getType(), is(type)); assertThat(workItem.getCurrentPhase(), is("phase 2")); } @Test public void canConstructWithoutSpecifyingAParentId() { WorkItemType type = new WorkItemType(); WorkItem workItem = new WorkItem(6, type); assertThat(workItem.getId(), is(6)); assertThat(workItem.getParentId(), is(ROOT_WORK_ITEM_ID)); assertThat(workItem.getType(), is(type)); } @Test public void canConstructWithoutSpecifyingAParentIdAndAlreadyAdvancedToAGivenPhase() { WorkItemType type = new WorkItemType("phase 1", "phase 2"); WorkItem workItem = new WorkItem(11, type, "phase 2"); assertThat(workItem.getId(), is(11)); assertThat(workItem.getParentId(), is(ROOT_WORK_ITEM_ID)); assertThat(workItem.getType(), is(type)); assertThat(workItem.getCurrentPhase(), is("phase 2")); } @Test(expected = IllegalArgumentException.class) public void cannotPassANonexistentPhaseToConstructor() { WorkItemType type = new WorkItemType("phase"); new WorkItem(1, type, "nonexistent phase"); } @Test public void datesCanBeSetUsingStrings() throws ParseException { WorkItem workItem = new WorkItem(1, new WorkItemType("phase")); workItem.setDateAsString("phase", "2011-05-31"); assertThat(workItem.getDate("phase"), is(parseIsoDate("2011-05-31"))); } @Test public void getPhaseDurationsHasCorrectDurationsPartial() throws ParseException{ WorkItem workItem = new WorkItem(1, type); workItem.setDate("phase1", formatter.parse("10/07/2011")); workItem.setDate("phase2", formatter.parse("18/07/2011")); Map<String, Integer> phaseDurations = workItem.getPhaseDurations(); assertThat(phaseDurations.keySet().size(), is(4)); assertThat(phaseDurations.get("phase1"), is(5)); LocalDate today = new LocalDate(); int days = WorkingDayUtils. getWorkingDaysBetween(formatter.parse("18/07/2011"), today); assertThat(phaseDurations.get("phase2"), is(days)); assertThat(phaseDurations.get("phase3"), is(0)); } @Test public void getPhaseDurationsHasCorrectDurationsComplete() throws ParseException{ WorkItem workItem = new WorkItem(1, type); workItem.setDate("phase1", formatter.parse("10/07/2011")); workItem.setDate("phase2", formatter.parse("18/07/2011")); workItem.setDate("phase3", formatter.parse("25/07/2011")); Map<String, Integer> phaseDurations = workItem.getPhaseDurations(); assertThat(phaseDurations.keySet().size(), is(4)); assertThat(phaseDurations.get("phase1"), is(5)); assertThat(phaseDurations.get("phase2"), is(5)); LocalDate today = new LocalDate(); int days = WorkingDayUtils. getWorkingDaysBetween(formatter.parse("25/07/2011"), today); assertThat(phaseDurations.get("phase3"), is(days)); } @Test public void getPhaseDurationsHasCorrectDurationsComplete2() throws ParseException { WorkItem workItem = new WorkItem(1, type); workItem.setDate("feature", formatter.parse("10/10/2011")); workItem.setDate("phase1", formatter.parse("12/10/2011")); workItem.setDate("phase3", formatter.parse("15/10/2011")); Map<String, Integer> phaseDurations = workItem.getPhaseDurations(); assertThat(phaseDurations.keySet().size(), is(4)); assertThat(phaseDurations.get("feature"), is(2)); assertThat(phaseDurations.get("phase1"), is(3)); assertThat(phaseDurations.get("phase2"), is(0)); } @Test public void setWorkStreamAsStringTrims() { WorkItem workItem = new WorkItem(1, type); workItem.setWorkStreamsAsString("a , b, c "); assertThat(workItem.getWorkStreamsAsString(), is("a,b,c")); assertThat(workItem.getWorkStreams().size(), is(3)); assertThat(workItem.getWorkStreams().get(0), is("a")); assertThat(workItem.getWorkStreams().get(1), is("b")); assertThat(workItem.getWorkStreams().get(2), is("c")); } @Test public void setWorkStreamAsStringEmptyOrNull() { WorkItem workItem = new WorkItem(1, type); workItem.setWorkStreamsAsString(""); assertThat(workItem.getWorkStreamsAsString(), is("")); assertThat(workItem.getWorkStreams().size(), is(0)); workItem.setWorkStreamsAsString(null); assertThat(workItem.getWorkStreamsAsString(), is("")); assertThat(workItem.getWorkStreams().size(), is(0)); } }