package com.constellio.app.modules.tasks.model.wrappers; import static com.constellio.app.modules.tasks.model.wrappers.TaskStatusType.IN_PROGRESS; import static com.constellio.app.modules.tasks.model.wrappers.types.TaskStatus.STANDBY_CODE; import static com.constellio.model.entities.schemas.Schemas.IDENTIFIER; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from; import static com.constellio.sdk.tests.TestUtils.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.List; import org.assertj.core.api.BooleanAssert; import org.joda.time.LocalDate; import org.junit.Test; import com.constellio.app.modules.tasks.model.wrappers.structures.TaskFollower; import com.constellio.app.modules.tasks.model.wrappers.structures.TaskReminder; import com.constellio.app.modules.tasks.model.wrappers.types.TaskStatus; import com.constellio.app.modules.tasks.services.TasksSchemasRecordsServices; import com.constellio.model.entities.records.RecordRuntimeException; import com.constellio.model.entities.records.wrappers.Group; import com.constellio.model.entities.records.wrappers.User; import com.constellio.model.services.records.RecordServices; import com.constellio.model.services.records.RecordServicesException; import com.constellio.model.services.records.RecordServicesException.ValidationException; import com.constellio.model.services.search.SearchServices; import com.constellio.model.services.search.query.logical.LogicalSearchQuery; import com.constellio.sdk.tests.ConstellioTest; import com.constellio.sdk.tests.setups.Users; public class TaskAcceptTest extends ConstellioTest { LocalDate zeDate = new LocalDate().minusDays(666); Users users = new Users(); RecordServices recordServices; SearchServices searchServices; private LocalDate now = LocalDate.now(); private Task zeTask; private TasksSchemasRecordsServices schemas; User alice, bob, chuckNorris, charles, dakota, edouard, gandalf, admin, sasquatch, robin; String aliceId, bobId, charlesId, chuckNorrisId, dakotaId, edouardId, gandalfId, sasquatchId, robinId; List<User> allUsers; Group legends, heroes, rumors, sidekicks; String legendsId, heroesId, rumorsId, sidekicksId; public void setUpWithOneCollection() throws Exception { prepareSystem(withZeCollection().withTasksModule().withAllTest(users)); givenTimeIs(now); recordServices = getModelLayerFactory().newRecordServices(); searchServices = getModelLayerFactory().newSearchServices(); schemas = new TasksSchemasRecordsServices(zeCollection, getAppLayerFactory()); zeTask = schemas.newTask(); zeTask.setTitle("zeTask"); admin = users.adminIn(zeCollection); aliceId = (alice = users.aliceIn(zeCollection)).getId(); bobId = (bob = users.bobIn(zeCollection)).getId(); chuckNorrisId = (chuckNorris = users.chuckNorrisIn(zeCollection)).getId(); charlesId = (charles = users.charlesIn(zeCollection)).getId(); dakotaId = (dakota = users.dakotaIn(zeCollection)).getId(); edouardId = (edouard = users.edouardIn(zeCollection)).getId(); gandalfId = (gandalf = users.gandalfIn(zeCollection)).getId(); robinId = (robin = users.robinIn(zeCollection)).getId(); sasquatchId = (sasquatch = users.sasquatchIn(zeCollection)).getId(); legendsId = (legends = users.legendsIn(zeCollection)).getId(); heroesId = (heroes = users.heroesIn(zeCollection)).getId(); rumorsId = (rumors = users.rumorsIn(zeCollection)).getId(); sidekicksId = (sidekicks = users.sidekicksIn(zeCollection)).getId(); allUsers = asList(alice, bob, chuckNorris, charles, dakota, edouard, gandalf, sasquatch, robin); recordServices.update(alice.setCollectionReadAccess(true)); } public void setUpWithTwoCollections() throws Exception { prepareSystem( withZeCollection().withTasksModule().withAllTest(users), withCollection("anotherCollection").withTasksModule().withAllTest(users)); givenTimeIs(now); recordServices = getModelLayerFactory().newRecordServices(); searchServices = getModelLayerFactory().newSearchServices(); schemas = new TasksSchemasRecordsServices(zeCollection, getAppLayerFactory()); zeTask = schemas.newTask(); zeTask.setTitle("zeTask"); admin = users.adminIn(zeCollection); aliceId = (alice = users.aliceIn(zeCollection)).getId(); bobId = (bob = users.bobIn(zeCollection)).getId(); chuckNorrisId = (chuckNorris = users.chuckNorrisIn(zeCollection)).getId(); charlesId = (charles = users.charlesIn(zeCollection)).getId(); dakotaId = (dakota = users.dakotaIn(zeCollection)).getId(); edouardId = (edouard = users.edouardIn(zeCollection)).getId(); gandalfId = (gandalf = users.gandalfIn(zeCollection)).getId(); robinId = (robin = users.robinIn(zeCollection)).getId(); sasquatchId = (sasquatch = users.sasquatchIn(zeCollection)).getId(); legendsId = (legends = users.legendsIn(zeCollection)).getId(); heroesId = (heroes = users.heroesIn(zeCollection)).getId(); rumorsId = (rumors = users.rumorsIn(zeCollection)).getId(); sidekicksId = (sidekicks = users.sidekicksIn(zeCollection)).getId(); allUsers = asList(alice, bob, chuckNorris, charles, dakota, edouard, gandalf, sasquatch, robin); recordServices.update(alice.setCollectionReadAccess(true)); } @Test(expected = RecordServicesException.ValidationException.class) public void whenSaveTaskWithoutTitleThenValidationException() throws Exception { setUpWithOneCollection(); zeTask.setTitle(null); saveAndReload(zeTask); } @Test(expected = RecordServicesException.ValidationException.class) public void givenTaskWithAssigneeAndNoAssignationDateWhenSaveThenValidationException() throws Exception { setUpWithOneCollection(); zeTask.setAssignee(users.aliceIn(zeCollection).getId()); saveAndReload(zeTask); } @Test(expected = RecordServicesException.ValidationException.class) public void givenTaskWithoutAssigneeAndWithAssignationDateWhenSaveThenValidationException() throws Exception { setUpWithOneCollection(); zeTask.setAssignationDate(now); saveAndReload(zeTask); } @Test public void givenTaskWithAssigneeAndWithAssignationDateWhenSaveThenOk() throws Exception { setUpWithOneCollection(); zeTask.setAssignationDate(now).setAssignee(aliceId).setAssigner(aliceId); saveAndReload(zeTask); } @Test public void whenSaveTaskThenMetadataValuesSaved() throws Exception { setUpWithOneCollection(); String zeAssignee = users.aliceIn(zeCollection).getId(); String zeCreator = users.chuckNorrisIn(zeCollection).getId(); zeTask.setAssignee(zeAssignee); zeTask.setAssignationDate(now); List<String> groups = asList(users.legendsIn(zeCollection).getId()); zeTask.setAssigneeGroupsCandidates(groups); List<String> assigneeUsersCandidates = asList(zeAssignee, users.adminIn(zeCollection).getId()); zeTask.setAssigneeUsersCandidates(assigneeUsersCandidates); zeTask.setCreatedBy(zeCreator); zeTask.setAssigner(zeCreator); LocalDate endDate = now.plusDays(2); zeTask.setDueDate(endDate); zeTask.setStartDate(now); zeTask.setProgressPercentage(20d); TaskStatus zeStatus = schemas.newTaskStatus().setCode("zeStatus").setStatusType(IN_PROGRESS).setTitle("status title"); recordServices.add(zeStatus.getWrappedRecord()); zeTask.setStatus(zeStatus.getId()); TaskReminder reminder1 = new TaskReminder().setFixedDate(now.plusDays(1000)); TaskReminder reminder2 = new TaskReminder().setBeforeRelativeDate(true).setNumberOfDaysToRelativeDate(1) .setRelativeDateMetadataCode(Task.DUE_DATE); zeTask.setReminders(asList(reminder1, reminder2)); TaskFollower aliceFollowingTask = new TaskFollower().setFollowerId(users.aliceIn(zeCollection).getId()); List<TaskFollower> followers = new ArrayList<>(); followers.add(aliceFollowingTask); zeTask.setTaskFollowers(followers); zeTask = saveAndReload(zeTask); assertThat(zeTask.getAssignee()).isEqualTo(zeAssignee); assertThat(zeTask.getAssignedOn()).isEqualTo(now); assertThat(zeTask.getAssigneeGroupsCandidates()).containsAll(groups); assertThat(zeTask.getAssigneeUsersCandidates()).containsAll(assigneeUsersCandidates); assertThat(zeTask.getCreatedBy()).isEqualTo(zeCreator); assertThat(zeTask.getDueDate()).isEqualTo(endDate); assertThat(zeTask.getStartDate()).isEqualTo(now); assertThat(zeTask.getStatus()).isEqualTo(zeStatus.getId()); assertThat(zeTask.getReminders()).containsOnly(reminder1, reminder2); assertThat(zeTask.getTaskFollowers()).hasSize(2); assertThat(zeTask.getTaskFollowers()).contains(aliceFollowingTask); assertThat(zeTask.getFollowersIds()).containsOnly(users.aliceIn(zeCollection).getId()); assertThat(zeTask.getNextReminderOn()).isEqualTo(now.plusDays(1)); } @Test public void whenStatusNotSetThenTaskWithStandbyStatus() throws Exception { setUpWithOneCollection(); zeTask = saveAndReload(zeTask); String standbyStatusId = schemas.getTaskStatusWithCode(STANDBY_CODE).getId(); assertThat(zeTask.getStatus()).isEqualTo(standbyStatusId); } @Test(expected = RecordRuntimeException.CannotSetManualValueInAutomaticField.class) public void whenTrySetCalculatedMetadataThenThrowUnsupportedSetOnCalculatedMetadata() throws Exception { setUpWithOneCollection(); try { zeTask.set(Task.FOLLOWERS_IDS, null); fail(""); } catch (RecordRuntimeException.CannotSetManualValueInAutomaticField e) { //OK } zeTask.set(Task.NEXT_REMINDER_ON, null); } @Test public void givenTaskWithReminderHavingFixedDateWhenGetNextReminderOnThenReminderFixedDate() throws Exception { setUpWithOneCollection(); TaskReminder reminder1 = new TaskReminder().setFixedDate(now.plusDays(1000)); zeTask.setReminders(asList(reminder1)); zeTask = saveAndReload(zeTask); assertThat(zeTask.getNextReminderOn()).isEqualTo(reminder1.getFixedDate()); } @Test public void whenTrySetInvalidPercentageThenThrowInvalidPercentage() throws Exception { setUpWithOneCollection(); zeTask.set(Task.PROGRESS_PERCENTAGE, null); saveAndReload(zeTask); zeTask.set(Task.PROGRESS_PERCENTAGE, 0); saveAndReload(zeTask); zeTask.set(Task.PROGRESS_PERCENTAGE, 100.0); saveAndReload(zeTask); zeTask.set(Task.PROGRESS_PERCENTAGE, 22.1); saveAndReload(zeTask); try { zeTask.set(Task.PROGRESS_PERCENTAGE, -1); saveAndReload(zeTask); fail(""); } catch (RecordServicesException.ValidationException e) { //OK } try { zeTask.set(Task.PROGRESS_PERCENTAGE, 100.01); saveAndReload(zeTask); fail(""); } catch (RecordServicesException.ValidationException e1) { //OK } } @Test public void givenAssignedTaskWithFollowersThenUsersHaveGoodAccess() throws Exception { setUpWithOneCollection(); zeTask.setCreatedBy(dakotaId); zeTask.setAssigner(dakotaId); zeTask.setAssignee(bobId); zeTask.setAssignationDate(new LocalDate()); zeTask.setTaskFollowers(asList( new TaskFollower().setFollowerId(chuckNorrisId), new TaskFollower().setFollowerId(edouardId) )); saveAndReload(zeTask); validateThat(zeTask).canOnlyBeReadBy(alice, dakota, bob, chuckNorris, edouard, admin); validateThat(zeTask).canOnlyBeWrittenBy(dakota, bob, admin); validateThat(zeTask).canOnlyBeDeletedBy(dakota, admin); } @Test public void givenAssignedTaskWithMultipleCandidatesAndFollowersThenUsersHaveGoodAccess() throws Exception { setUpWithOneCollection(); zeTask.setCreatedBy(charlesId); zeTask.setAssigner(charlesId); zeTask.setAssignee(bobId); zeTask.setAssigneeUsersCandidates(asList(bobId, gandalfId)); zeTask.setAssignationDate(new LocalDate()); zeTask.setTaskFollowers(asList( new TaskFollower().setFollowerId(chuckNorrisId), new TaskFollower().setFollowerId(bobId) )); saveAndReload(zeTask); validateThat(zeTask).canOnlyBeReadBy(alice, charles, bob, gandalf, chuckNorris, admin); validateThat(zeTask).canOnlyBeWrittenBy(charles, bob, gandalf, admin); validateThat(zeTask).canOnlyBeDeletedBy(charles, admin); } @Test public void givenAssignedTaskWithMultipleCandidateGroupsAndFollowersThenUsersHaveGoodAccess() throws Exception { setUpWithOneCollection(); zeTask.setCreatedBy(charlesId); zeTask.setAssignee(bobId); zeTask.setAssigner(charlesId); zeTask.setAssigneeGroupsCandidates(asList(legendsId)); zeTask.setAssignationDate(new LocalDate()); zeTask.setTaskFollowers(asList( new TaskFollower().setFollowerId(chuckNorrisId), new TaskFollower().setFollowerId(bobId) )); saveAndReload(zeTask); validateThat(zeTask).canOnlyBeReadBy(alice, charles, bob, edouard, gandalf, chuckNorris, admin); validateThat(zeTask).canOnlyBeWrittenBy(alice, charles, bob, edouard, gandalf, admin); validateThat(zeTask).canOnlyBeDeletedBy(charles, admin); } @Test public void givenTaskHasADueDateSettedAfterItsParentThenException() throws Exception { setUpWithOneCollection(); Task rootTask = schemas.newTaskWithId("rootTask"); rootTask.setTitle("root task"); rootTask.setCreatedBy(charlesId); rootTask.setAssigneeGroupsCandidates(asList(legendsId)); rootTask.setDueDate(zeDate); saveAndReload(rootTask); Task childTaskWithDueDateBefore = schemas.newTaskWithId("childTask1"); childTaskWithDueDateBefore.setTitle("sub task"); childTaskWithDueDateBefore.setCreatedBy(bobId); childTaskWithDueDateBefore.setParentTask(rootTask); childTaskWithDueDateBefore.setAssigneeGroupsCandidates(asList(legendsId)); childTaskWithDueDateBefore.setDueDate(zeDate.minusDays(1)); saveAndReload(childTaskWithDueDateBefore); Task childTaskWithDueDateSameDay = schemas.newTaskWithId("childTask2"); childTaskWithDueDateSameDay.setTitle("sub task"); childTaskWithDueDateSameDay.setCreatedBy(bobId); childTaskWithDueDateSameDay.setParentTask(rootTask); childTaskWithDueDateSameDay.setAssigneeGroupsCandidates(asList(legendsId)); childTaskWithDueDateSameDay.setDueDate(zeDate); saveAndReload(childTaskWithDueDateSameDay); Task childTaskWithDueDateAfter = schemas.newTaskWithId("childTask3"); childTaskWithDueDateAfter.setTitle("sub task"); childTaskWithDueDateAfter.setCreatedBy(bobId); childTaskWithDueDateAfter.setParentTask(rootTask); childTaskWithDueDateAfter.setAssigneeGroupsCandidates(asList(legendsId)); childTaskWithDueDateAfter.setDueDate(zeDate.plusDays(1)); try { saveAndReload(childTaskWithDueDateAfter); fail("Exception expected"); } catch (ValidationException ve) { assertThat(ve.getErrors().getValidationErrors()).extracting("code").containsOnly( "com.constellio.app.modules.tasks.model.validators.TaskValidator_dueDateMustBeLesserOrEqualThanParentDueDate"); } } @Test public void givenAMultilevelTasksHierarchyThenGoodAccessToUsersAndCreatorOfRootTaskCanDeleteCompleteHierarchy() throws Exception { setUpWithOneCollection(); Task rootTask = schemas.newTaskWithId("rootTask"); rootTask.setTitle("root task"); rootTask.setCreatedBy(charlesId); rootTask.setAssigner(charlesId); rootTask.setAssignee(bobId); rootTask.setAssigneeGroupsCandidates(asList(legendsId)); rootTask.setAssignationDate(new LocalDate()); rootTask.setTaskFollowers(asList( new TaskFollower().setFollowerId(chuckNorrisId), new TaskFollower().setFollowerId(bobId) )); saveAndReload(rootTask); Task childTask = schemas.newTaskWithId("childTask"); childTask.setTitle("sub task"); childTask.setCreatedBy(bobId); childTask.setParentTask(rootTask); childTask.setAssigner(charlesId); childTask.setAssignee(sasquatchId); childTask.setAssignationDate(new LocalDate()); childTask.setTaskFollowers(asList( new TaskFollower().setFollowerId(dakotaId) )); saveAndReload(childTask); Task childChildTask = schemas.newTaskWithId("childChildTask"); childChildTask.setTitle("sub sub task"); childChildTask.setCreatedBy(gandalfId); childChildTask.setParentTask(childTask); childChildTask.setAssigneeGroupsCandidates(asList(sidekicksId)); saveAndReload(childChildTask); validateThat(rootTask).canOnlyBeReadBy(alice, charles, bob, edouard, gandalf, chuckNorris, admin); validateThat(rootTask).canOnlyBeWrittenBy(alice, charles, bob, edouard, gandalf, admin); validateThat(rootTask).canOnlyBeDeletedBy(charles, admin); validateThat(childTask).canOnlyBeReadBy(alice, charles, bob, edouard, gandalf, chuckNorris, admin, dakota, sasquatch); validateThat(childTask).canOnlyBeWrittenBy(alice, charles, bob, edouard, gandalf, admin, sasquatch); validateThat(childTask).canOnlyBeDeletedBy(charles, admin, bob); validateThat(childChildTask) .canOnlyBeReadBy(alice, charles, bob, edouard, gandalf, chuckNorris, admin, dakota, sasquatch, robin); validateThat(childChildTask).canOnlyBeWrittenBy(alice, charles, bob, edouard, gandalf, admin, sasquatch, robin); validateThat(childChildTask).canOnlyBeDeletedBy(charles, admin, bob, gandalf); assertThat(searchServices.searchRecordIds(new LogicalSearchQuery(from(schemas.userTask.schemaType()).returnAll()))) .contains("rootTask", "childTask", "childChildTask"); recordServices.logicallyDelete(rootTask.getWrappedRecord(), charles); recordServices.physicallyDelete(rootTask.getWrappedRecord(), charles); assertThat(searchServices.searchRecordIds(new LogicalSearchQuery(from(schemas.userTask.schemaType()).returnAll()))) .doesNotContain("rootTask", "childTask", "childChildTask"); } @Test public void givenTwoCollectionsWithTaskModuleThenBothHasValidDefaultStatus() throws Exception { setUpWithTwoCollections(); TasksSchemasRecordsServices tasks = new TasksSchemasRecordsServices("anotherCollection", getAppLayerFactory()); TaskStatus status = tasks.getTaskStatusWithCode(TaskStatus.STANDBY_CODE); System.out.println(tasks.getTypes().getMetadata(Task.DEFAULT_SCHEMA + "_" + Task.STATUS).getDefaultValue()); assertThat(tasks.getTypes().getMetadata(Task.DEFAULT_SCHEMA + "_" + Task.STATUS) .getDefaultValue()).isEqualTo(status.getId()); tasks = new TasksSchemasRecordsServices(zeCollection, getAppLayerFactory()); status = tasks.getTaskStatusWithCode(TaskStatus.STANDBY_CODE); System.out.println(tasks.getTypes().getMetadata(Task.DEFAULT_SCHEMA + "_" + Task.STATUS).getDefaultValue()); assertThat(tasks.getTypes().getMetadata(Task.DEFAULT_SCHEMA + "_" + Task.STATUS) .getDefaultValue()).isEqualTo(status.getId()); } private TaskValidation validateThat(Task task) { return new TaskValidation(task); } private class TaskValidation { Task task; private TaskValidation(Task task) { this.task = task; } private void canOnlyBeDeletedBy(User... expectedUsers) { List<User> expectedUserLists = asList(expectedUsers); for (User user : allUsers) { BooleanAssert booleanAssert = assertThat(user.hasDeleteAccess().on(task)).describedAs( user.getUsername() + " delete access on ze task"); if (expectedUserLists.contains(user)) { booleanAssert.isTrue(); } else { booleanAssert.isFalse(); } List<String> tasks = searchServices.searchRecordIds(new LogicalSearchQuery() .filteredWithUserDelete(user) .setCondition(from(schemas.userTask.schemaType()).where(IDENTIFIER).isEqualTo(task.getId()))); booleanAssert = assertThat(!tasks.isEmpty()).describedAs( user.getUsername() + " delete access on ze task when searching"); if (expectedUserLists.contains(user)) { booleanAssert.isTrue(); } else { booleanAssert.isFalse(); } booleanAssert = assertThat(recordServices.isLogicallyThenPhysicallyDeletable(task.getWrappedRecord(), user)) .describedAs(user.getUsername() + " delete access on ze task when deleting"); if (expectedUserLists.contains(user)) { booleanAssert.isTrue(); } else { booleanAssert.isFalse(); } } } private void canOnlyBeWrittenBy(User... expectedUsers) { List<User> expectedUserLists = asList(expectedUsers); for (User user : allUsers) { BooleanAssert booleanAssert = assertThat(user.hasWriteAccess().on(task)).describedAs( user.getUsername() + " write access on ze task"); if (expectedUserLists.contains(user)) { booleanAssert.isTrue(); } else { booleanAssert.isFalse(); } List<String> tasks = searchServices.searchRecordIds(new LogicalSearchQuery() .filteredWithUserWrite(user) .setCondition(from(schemas.userTask.schemaType()).where(IDENTIFIER).isEqualTo(task.getId()))); booleanAssert = assertThat(!tasks.isEmpty()).describedAs( user.getUsername() + " write access on ze task when searching"); if (expectedUserLists.contains(user)) { booleanAssert.isTrue(); } else { booleanAssert.isFalse(); } } } private void canOnlyBeReadBy(User... expectedUsers) { List<User> expectedUserLists = asList(expectedUsers); for (User user : allUsers) { BooleanAssert booleanAssert = assertThat(user.hasReadAccess().on(task)).describedAs( user.getUsername() + " read access on ze task"); if (expectedUserLists.contains(user)) { booleanAssert.isTrue(); } else { booleanAssert.isFalse(); } List<String> tasks = searchServices.searchRecordIds(new LogicalSearchQuery() .filteredWithUser(user) .setCondition(from(schemas.userTask.schemaType()).where(IDENTIFIER).isEqualTo(task.getId()))); booleanAssert = assertThat(!tasks.isEmpty()).describedAs( user.getUsername() + " read access on ze task when searching"); if (expectedUserLists.contains(user)) { booleanAssert.isTrue(); } else { booleanAssert.isFalse(); } } } } private Task saveAndReload(Task task) throws RecordServicesException { recordServices.add(task.getWrappedRecord()); return schemas.getTask(task.getId()); } }