diff --git a/src/main/java/teammates/common/datatransfer/DataBundle.java b/src/main/java/teammates/common/datatransfer/DataBundle.java index 2286f55592b..a7919d68147 100644 --- a/src/main/java/teammates/common/datatransfer/DataBundle.java +++ b/src/main/java/teammates/common/datatransfer/DataBundle.java @@ -11,6 +11,7 @@ import teammates.common.datatransfer.attributes.FeedbackResponseCommentAttributes; import teammates.common.datatransfer.attributes.FeedbackSessionAttributes; import teammates.common.datatransfer.attributes.InstructorAttributes; +import teammates.common.datatransfer.attributes.NotificationAttributes; import teammates.common.datatransfer.attributes.StudentAttributes; import teammates.common.datatransfer.attributes.StudentProfileAttributes; @@ -31,4 +32,5 @@ public class DataBundle { public Map feedbackResponses = new LinkedHashMap<>(); public Map feedbackResponseComments = new LinkedHashMap<>(); public Map profiles = new LinkedHashMap<>(); + public Map notifications = new LinkedHashMap<>(); } diff --git a/src/main/java/teammates/common/datatransfer/attributes/NotificationAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/NotificationAttributes.java index e4a8971c3a6..a6c98fdf5bc 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/NotificationAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/NotificationAttributes.java @@ -160,7 +160,7 @@ public void setUpdatedAt(Instant updatedAt) { } /** - * Sorts the list of notifications by the start time. + * Sorts the list of notifications by the start time, with the latest as the first element. */ public static void sortByStartTime(List notifications) { notifications.sort(Comparator.comparing(NotificationAttributes::getStartTime)); @@ -365,6 +365,8 @@ public B withEndTime(Instant endTime) { } public B withType(NotificationType type) { + assert type != null; + updateOptions.typeOption = UpdateOption.of(type); return thisBuilder; } diff --git a/src/main/java/teammates/logic/core/DataBundleLogic.java b/src/main/java/teammates/logic/core/DataBundleLogic.java index c5973025b33..02e3aae3ed0 100644 --- a/src/main/java/teammates/logic/core/DataBundleLogic.java +++ b/src/main/java/teammates/logic/core/DataBundleLogic.java @@ -19,6 +19,7 @@ import teammates.common.datatransfer.attributes.FeedbackResponseCommentAttributes; import teammates.common.datatransfer.attributes.FeedbackSessionAttributes; import teammates.common.datatransfer.attributes.InstructorAttributes; +import teammates.common.datatransfer.attributes.NotificationAttributes; import teammates.common.datatransfer.attributes.StudentAttributes; import teammates.common.datatransfer.attributes.StudentProfileAttributes; import teammates.common.exception.InvalidParametersException; @@ -33,6 +34,7 @@ import teammates.storage.api.FeedbackResponsesDb; import teammates.storage.api.FeedbackSessionsDb; import teammates.storage.api.InstructorsDb; +import teammates.storage.api.NotificationsDb; import teammates.storage.api.ProfilesDb; import teammates.storage.api.StudentsDb; @@ -55,6 +57,7 @@ public final class DataBundleLogic { private final FeedbackQuestionsDb fqDb = FeedbackQuestionsDb.inst(); private final FeedbackResponsesDb frDb = FeedbackResponsesDb.inst(); private final FeedbackResponseCommentsDb fcDb = FeedbackResponseCommentsDb.inst(); + private final NotificationsDb nfDb = NotificationsDb.inst(); private DataBundleLogic() { // prevent initialization @@ -90,6 +93,7 @@ public DataBundle persistDataBundle(DataBundle dataBundle) throws InvalidParamet Collection questions = dataBundle.feedbackQuestions.values(); Collection responses = dataBundle.feedbackResponses.values(); Collection responseComments = dataBundle.feedbackResponseComments.values(); + Collection notifications = dataBundle.notifications.values(); // For ensuring only one account per Google ID is created Map googleIdAccountMap = new HashMap<>(); @@ -115,6 +119,7 @@ public DataBundle persistDataBundle(DataBundle dataBundle) throws InvalidParamet List newFeedbackResponses = frDb.putEntities(responses); List newFeedbackResponseComments = fcDb.putEntities(responseComments); + List newNotifications = nfDb.putEntities(notifications); updateDataBundleValue(newAccounts, dataBundle.accounts); updateDataBundleValue(newAccountRequests, dataBundle.accountRequests); @@ -126,6 +131,7 @@ public DataBundle persistDataBundle(DataBundle dataBundle) throws InvalidParamet updateDataBundleValue(createdQuestions, dataBundle.feedbackQuestions); updateDataBundleValue(newFeedbackResponses, dataBundle.feedbackResponses); updateDataBundleValue(newFeedbackResponseComments, dataBundle.feedbackResponseComments); + updateDataBundleValue(newNotifications, dataBundle.notifications); return dataBundle; @@ -379,6 +385,9 @@ public void removeDataBundle(DataBundle dataBundle) { dataBundle.accountRequests.values().forEach(accountRequest -> { accountRequestsDb.deleteAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); }); + dataBundle.notifications.values().forEach(notification -> { + nfDb.deleteNotification(notification.getNotificationId()); + }); } private void deleteCourses(Collection courses) { diff --git a/src/main/java/teammates/ui/webapi/GetNotificationAction.java b/src/main/java/teammates/ui/webapi/GetNotificationAction.java index a4dc9263d50..171e703a9d1 100644 --- a/src/main/java/teammates/ui/webapi/GetNotificationAction.java +++ b/src/main/java/teammates/ui/webapi/GetNotificationAction.java @@ -15,15 +15,28 @@ public class GetNotificationAction extends Action { private static final String INVALID_TARGET_USER = "Target user can only be STUDENT or INSTRUCTOR."; + private static final String UNAUTHORIZED_ACCESS = "You are not allowed to view this resource!"; @Override AuthType getMinAuthLevel() { - return AuthType.PUBLIC; + return AuthType.LOGGED_IN; } @Override void checkSpecificAccessControl() throws UnauthorizedAccessException { - // Any user can get notifications as long as its parameters are valid + if (userInfo.isAdmin) { + return; + } + String targetUserString = getRequestParamValue(Const.ParamsNames.NOTIFICATION_TARGET_USER); + String targetUserErrorMessage = FieldValidator.getInvalidityInfoForNotificationTargetUser(targetUserString); + if (!targetUserErrorMessage.isEmpty()) { + throw new InvalidHttpParameterException(targetUserErrorMessage); + } + NotificationTargetUser targetUser = NotificationTargetUser.valueOf(targetUserString); + if (targetUser == NotificationTargetUser.INSTRUCTOR && userInfo.isStudent + || targetUser == NotificationTargetUser.STUDENT && userInfo.isInstructor) { + throw new UnauthorizedAccessException(UNAUTHORIZED_ACCESS); + } } @Override diff --git a/src/test/java/teammates/common/datatransfer/attributes/NotificationAttributesTest.java b/src/test/java/teammates/common/datatransfer/attributes/NotificationAttributesTest.java new file mode 100644 index 00000000000..585f187b46e --- /dev/null +++ b/src/test/java/teammates/common/datatransfer/attributes/NotificationAttributesTest.java @@ -0,0 +1,231 @@ +package teammates.common.datatransfer.attributes; + +import java.time.Instant; + +import org.testng.annotations.Test; + +import teammates.common.datatransfer.NotificationTargetUser; +import teammates.common.datatransfer.NotificationType; +import teammates.storage.entity.Notification; +import teammates.test.BaseTestCase; + +/** + * SUT: {@link NotificationAttributes}. + */ +public class NotificationAttributesTest extends BaseTestCase { + @Test + public void testValueOf_withAllFieldPopulatedNotificationAttributes_shouldGenerateAttributesCorrectly() { + Notification notification = new Notification("valid-notification-id", + Instant.now().plusSeconds(3600), Instant.now().plusSeconds(7200), + NotificationType.DEPRECATION, NotificationTargetUser.INSTRUCTOR, + "valid notification title", "valid notification message", false, + Instant.now(), Instant.now()); + NotificationAttributes nfa = NotificationAttributes.valueOf(notification); + verifyNotificationEquals(nfa, notification); + } + + @Test + public void testBuilder_withNullArguments_shouldThrowException() { + assertThrows(AssertionError.class, () -> { + NotificationAttributes + .builder(null) + .build(); + }); + + assertThrows(AssertionError.class, () -> { + NotificationAttributes + .builder("notificationId") + .withStartTime(null) + .build(); + }); + + assertThrows(AssertionError.class, () -> { + NotificationAttributes + .builder("notificationId") + .withEndTime(null) + .build(); + }); + + assertThrows(AssertionError.class, () -> { + NotificationAttributes + .builder("notificationId") + .withType(null) + .build(); + }); + + assertThrows(AssertionError.class, () -> { + NotificationAttributes + .builder("notificationId") + .withTargetUser(null) + .build(); + }); + + assertThrows(AssertionError.class, () -> { + NotificationAttributes + .builder("notificationId") + .withTitle(null) + .build(); + }); + + assertThrows(AssertionError.class, () -> { + NotificationAttributes + .builder("notificationId") + .withMessage(null) + .build(); + }); + } + + @Test + public void testBuilder_withTypicalData_shouldBuildCorrectAttributes() { + NotificationAttributes nfa = generateTypicalNotificationAttributesObject(); + assertEquals("notificationId", nfa.getNotificationId()); + assertEquals(Instant.ofEpochSecond(1234567890), nfa.getStartTime()); + assertEquals(Instant.ofEpochSecond(1234567890).plusSeconds(7200), nfa.getEndTime()); + assertEquals(NotificationType.DEPRECATION, nfa.getType()); + assertEquals(NotificationTargetUser.INSTRUCTOR, nfa.getTargetUser()); + assertEquals("valid notification title", nfa.getTitle()); + assertEquals("valid message", nfa.getMessage()); + } + + @Test + public void testCopyConstructor_shouldDoDeepCopyOfNotificationDetails() { + NotificationAttributes nfa1 = generateTypicalNotificationAttributesObject(); + NotificationAttributes nfa2 = nfa1.getCopy(); + nfa1.setMessage("The first message"); + nfa2.setMessage("The second message"); + + assertEquals(nfa1.getMessage(), "The first message"); + assertEquals(nfa2.getMessage(), "The second message"); + } + + @Test + public void testUpdateOptions_withTypicalUpdateOptions_shouldUpdateAttributeCorrectly() { + NotificationAttributes.UpdateOptions updateOptions = + NotificationAttributes.updateOptionsBuilder("notificationId") + .withStartTime(Instant.ofEpochSecond(1234567890).plusSeconds(1000)) + .withEndTime(Instant.ofEpochSecond(1234567890).plusSeconds(10000)) + .withType(NotificationType.VERSION_NOTE) + .withTargetUser(NotificationTargetUser.STUDENT) + .withTitle("The edited title") + .withMessage("The edited message") + .build(); + + assertEquals("notificationId", updateOptions.getNotificationId()); + + NotificationAttributes notificationAttributes = generateTypicalNotificationAttributesObject(); + notificationAttributes.update(updateOptions); + + assertEquals(Instant.ofEpochSecond(1234567890).plusSeconds(1000), notificationAttributes.getStartTime()); + assertEquals(Instant.ofEpochSecond(1234567890).plusSeconds(10000), notificationAttributes.getEndTime()); + assertEquals(NotificationType.VERSION_NOTE, notificationAttributes.getType()); + assertEquals(NotificationTargetUser.STUDENT, notificationAttributes.getTargetUser()); + assertEquals("The edited title", notificationAttributes.getTitle()); + assertEquals("The edited message", notificationAttributes.getMessage()); + } + + @Test + public void testUpdateOptionsBuilder_withNullInput_shouldFailWithAssertionError() { + assertThrows(AssertionError.class, () -> + NotificationAttributes.updateOptionsBuilder((String) null)); + assertThrows(AssertionError.class, () -> + NotificationAttributes.updateOptionsBuilder("notificationId") + .withStartTime(null)); + assertThrows(AssertionError.class, () -> + NotificationAttributes.updateOptionsBuilder("notificationId") + .withEndTime(null)); + assertThrows(AssertionError.class, () -> + NotificationAttributes.updateOptionsBuilder("notificationId") + .withType(null)); + assertThrows(AssertionError.class, () -> + NotificationAttributes.updateOptionsBuilder("notificationId") + .withTargetUser(null)); + assertThrows(AssertionError.class, () -> + NotificationAttributes.updateOptionsBuilder("notificationId") + .withTitle(null)); + assertThrows(AssertionError.class, () -> + NotificationAttributes.updateOptionsBuilder("notificationId") + .withMessage(null)); + } + + @Test + public void testEquals() { + NotificationAttributes notificationAttributes = generateTypicalNotificationAttributesObject(); + + // When the two notifications are exact copies + NotificationAttributes notificationAttributesCopy = notificationAttributes.getCopy(); + + assertTrue(notificationAttributes.equals(notificationAttributesCopy)); + + // When the two notifications have same values but created at different time + NotificationAttributes notificationAttributesSimilar = generateTypicalNotificationAttributesObject(); + + assertTrue(notificationAttributes.equals(notificationAttributesSimilar)); + + NotificationAttributes notificationAttributesDifferent = + NotificationAttributes.builder("differentId") + .withStartTime(Instant.ofEpochSecond(1234567890)) + .withEndTime(Instant.ofEpochSecond(1234567890).plusSeconds(7200)) + .withType(NotificationType.DEPRECATION) + .withTargetUser(NotificationTargetUser.INSTRUCTOR) + .withTitle("valid notification title") + .withMessage("valid message") + .build(); + + assertFalse(notificationAttributes.equals(notificationAttributesDifferent)); + + // When the other object is of different class + assertFalse(notificationAttributes.equals(3)); + } + + @Test + public void testHashCode() { + NotificationAttributes notificationAttributes = generateTypicalNotificationAttributesObject(); + + // When the two notifications are exact copies + NotificationAttributes notificationAttributesCopy = notificationAttributes.getCopy(); + + assertTrue(notificationAttributes.hashCode() == notificationAttributesCopy.hashCode()); + + // When the two notifications have same values but created at different time + NotificationAttributes notificationAttributesSimilar = generateTypicalNotificationAttributesObject(); + + assertTrue(notificationAttributes.hashCode() == notificationAttributesSimilar.hashCode()); + + // notification attributes with a different id. + NotificationAttributes notificationAttributesDifferent = + NotificationAttributes.builder("differentId") + .withStartTime(Instant.ofEpochSecond(1234567890)) + .withEndTime(Instant.ofEpochSecond(1234567890).plusSeconds(7200)) + .withType(NotificationType.DEPRECATION) + .withTargetUser(NotificationTargetUser.INSTRUCTOR) + .withTitle("valid notification title") + .withMessage("valid message") + .build(); + + assertFalse(notificationAttributes.hashCode() == notificationAttributesDifferent.hashCode()); + } + + private NotificationAttributes generateTypicalNotificationAttributesObject() { + return NotificationAttributes.builder("notificationId") + .withStartTime(Instant.ofEpochSecond(1234567890)) + .withEndTime(Instant.ofEpochSecond(1234567890).plusSeconds(7200)) + .withType(NotificationType.DEPRECATION) + .withTargetUser(NotificationTargetUser.INSTRUCTOR) + .withTitle("valid notification title") + .withMessage("valid message") + .build(); + } + + private void verifyNotificationEquals(NotificationAttributes nfa, Notification notification) { + assertEquals(notification.getNotificationId(), nfa.getNotificationId()); + assertEquals(notification.getStartTime(), nfa.getStartTime()); + assertEquals(notification.getEndTime(), nfa.getEndTime()); + assertEquals(notification.getType(), nfa.getType()); + assertEquals(notification.getTargetUser(), nfa.getTargetUser()); + assertEquals(notification.getTitle(), nfa.getTitle()); + assertEquals(notification.getMessage(), nfa.getMessage()); + assertEquals(notification.isShown(), nfa.isShown()); + assertEquals(notification.getCreatedAt(), nfa.getCreatedAt()); + assertEquals(notification.getUpdatedAt(), nfa.getUpdatedAt()); + } +} diff --git a/src/test/java/teammates/common/util/FieldValidatorTest.java b/src/test/java/teammates/common/util/FieldValidatorTest.java index c0f308cf236..ba885686619 100644 --- a/src/test/java/teammates/common/util/FieldValidatorTest.java +++ b/src/test/java/teammates/common/util/FieldValidatorTest.java @@ -589,6 +589,79 @@ public void testGetInvalidityInfoForTimeForVisibilityStartAndResultsPublish_inva visibilityStart, resultsPublish)); } + @Test + public void testGetInvalidityInfoForTimeForNotificationStartAndEnd_valid_returnEmptyString() { + Instant notificationStart = TimeHelperExtension.getInstantHoursOffsetFromNow(-1); + Instant notificationEnd = TimeHelperExtension.getInstantHoursOffsetFromNow(1); + assertEquals("", + FieldValidator.getInvalidityInfoForTimeForNotificationStartAndEnd( + notificationStart, notificationEnd)); + } + + @Test + public void testGetInvalidityInfoForTimeForNotificationStartAndEnd_inValid_returnErrorString() { + Instant notificationStart = TimeHelperExtension.getInstantHoursOffsetFromNow(1); + Instant notificationEnd = TimeHelperExtension.getInstantHoursOffsetFromNow(-1); + assertEquals("The time when the notification will expire for this notification cannot be earlier " + + "than the time when the notification will be visible.", + FieldValidator.getInvalidityInfoForTimeForNotificationStartAndEnd( + notificationStart, notificationEnd)); + } + + @Test + public void testGetInvalidityInfoForNotificationTitle_valid_returnEmptyString() { + assertEquals("", FieldValidator.getInvalidityInfoForNotificationTitle("valid title")); + } + + @Test + public void testGetInvalidityInfoForNotificationTitle_inValid_returnErrorString() { + assertEquals("The field 'notification title' is empty.", + FieldValidator.getInvalidityInfoForNotificationTitle("")); + String invalidNotificationTitle = StringHelperExtension.generateStringOfLength( + FieldValidator.NOTIFICATION_TITLE_MAX_LENGTH + 1); + assertEquals("\"" + invalidNotificationTitle + "\" is not acceptable to TEAMMATES as a/an " + + "notification title because it is too long. " + + "The value of a/an notification title should be no longer than " + + FieldValidator.NOTIFICATION_TITLE_MAX_LENGTH + + " characters. It should not be empty.", + FieldValidator.getInvalidityInfoForNotificationTitle(invalidNotificationTitle)); + } + + @Test + public void testGetInvalidityInfoForNotificationBody_valid_returnEmptyString() { + assertEquals("", FieldValidator.getInvalidityInfoForNotificationBody("valid body")); + } + + @Test + public void testGetInvalidityInfoForNotificationBody_inValid_returnErrorString() { + assertEquals("The field 'notification message' is empty.", + FieldValidator.getInvalidityInfoForNotificationBody("")); + } + + @Test + public void testGetInvalidityInfoForNotificationType_valid_returnEmptyString() { + assertEquals("", FieldValidator.getInvalidityInfoForNotificationType("DEPRECATION")); + } + + @Test + public void testGetInvalidityInfoForNotificationType_inValid_returnErrorString() { + String notificationType = "invalid type"; + assertEquals("\"" + notificationType + "\" is not an accepted notification type to TEAMMATES. ", + FieldValidator.getInvalidityInfoForNotificationType(notificationType)); + } + + @Test + public void testGetInvalidityInfoForNotificationTargetUser_valid_returnEmptyString() { + assertEquals("", FieldValidator.getInvalidityInfoForNotificationTargetUser("GENERAL")); + } + + @Test + public void testGetInvalidityInfoForNotificationTargetUser_inValid_returnErrorString() { + String user = "invalid user"; + assertEquals("\"" + user + "\" is not an accepted notification target user to TEAMMATES. ", + FieldValidator.getInvalidityInfoForNotificationTargetUser(user)); + } + @Test public void testRegexName() { ______TS("success: typical name"); diff --git a/src/test/java/teammates/ui/webapi/GetNotificationActionTest.java b/src/test/java/teammates/ui/webapi/GetNotificationActionTest.java new file mode 100644 index 00000000000..3f76b69b6b4 --- /dev/null +++ b/src/test/java/teammates/ui/webapi/GetNotificationActionTest.java @@ -0,0 +1,177 @@ +package teammates.ui.webapi; + +import java.util.List; + +import org.testng.annotations.Test; + +import teammates.common.datatransfer.NotificationTargetUser; +import teammates.common.datatransfer.attributes.InstructorAttributes; +import teammates.common.datatransfer.attributes.NotificationAttributes; +import teammates.common.datatransfer.attributes.StudentAttributes; +import teammates.common.util.Const; +import teammates.ui.output.NotificationData; +import teammates.ui.output.NotificationsData; + +/** + * SUT: {@link GetNotificationAction}. + */ +public class GetNotificationActionTest extends BaseActionTest { + + // TODO: add tests for isfetchingall + + @Override + String getActionUri() { + return Const.ResourceURIs.NOTIFICATION; + } + + @Override + String getRequestMethod() { + return GET; + } + + @Test + @Override + protected void testExecute() { + // See independent test cases + } + + @Test + @Override + protected void testAccessControl() { + InstructorAttributes instructor = typicalBundle.instructors.get("instructor1OfCourse1"); + loginAsInstructor(instructor.getGoogleId()); + ______TS("student notification not accessible to instructor"); + String[] requestParams = new String[] { + Const.ParamsNames.NOTIFICATION_TARGET_USER, NotificationTargetUser.STUDENT.toString(), + Const.ParamsNames.NOTIFICATION_IS_FETCHING_ALL, String.valueOf(true), + }; + verifyCannotAccess(requestParams); + + ______TS("accessible to instructor"); + requestParams = new String[] { + Const.ParamsNames.NOTIFICATION_TARGET_USER, NotificationTargetUser.INSTRUCTOR.toString(), + Const.ParamsNames.NOTIFICATION_IS_FETCHING_ALL, String.valueOf(true), + }; + verifyCanAccess(requestParams); + + ______TS("instructor notification not accessible to student"); + StudentAttributes studentAttributes = typicalBundle.students.get("student1InCourse1"); + loginAsStudent(studentAttributes.getGoogleId()); + requestParams = new String[] { + Const.ParamsNames.NOTIFICATION_TARGET_USER, NotificationTargetUser.INSTRUCTOR.toString(), + Const.ParamsNames.NOTIFICATION_IS_FETCHING_ALL, String.valueOf(true), + }; + verifyCannotAccess(requestParams); + + ______TS("accessible to student"); + requestParams = new String[] { + Const.ParamsNames.NOTIFICATION_TARGET_USER, NotificationTargetUser.STUDENT.toString(), + Const.ParamsNames.NOTIFICATION_IS_FETCHING_ALL, String.valueOf(true), + }; + verifyCanAccess(requestParams); + + ______TS("unknown target user"); + loginAsInstructor(instructor.getGoogleId()); + requestParams = new String[] { + Const.ParamsNames.NOTIFICATION_TARGET_USER, "unknown", + Const.ParamsNames.NOTIFICATION_IS_FETCHING_ALL, String.valueOf(true), + }; + verifyHttpParameterFailureAcl(requestParams); + + ______TS("accessible to admin"); + loginAsAdmin(); + requestParams = new String[] { + Const.ParamsNames.NOTIFICATION_TARGET_USER, null, + Const.ParamsNames.NOTIFICATION_IS_FETCHING_ALL, String.valueOf(true), + }; + verifyCanAccess(requestParams); + } + + @Test + public void testExecute_withValidUserTypeForNonAdmin_shouldReturnData() { + ______TS("Request to fetch notification"); + InstructorAttributes instructor = typicalBundle.instructors.get("instructor1OfCourse1"); + loginAsInstructor(instructor.getGoogleId()); + NotificationAttributes notification = typicalBundle.notifications.get("notification5"); + int expectedNumberOfNotifications = + logic.getActiveNotificationsByTargetUser(notification.getTargetUser()).size(); + + String[] requestParams = new String[] { + Const.ParamsNames.NOTIFICATION_TARGET_USER, NotificationTargetUser.INSTRUCTOR.toString(), + Const.ParamsNames.NOTIFICATION_IS_FETCHING_ALL, String.valueOf(true), + }; + + GetNotificationAction action = getAction(requestParams); + JsonResult jsonResult = getJsonResult(action); + + NotificationsData output = (NotificationsData) jsonResult.getOutput(); + List notifications = output.getNotifications(); + + assertEquals(expectedNumberOfNotifications, notifications.size()); + + NotificationData firstNotification = notifications.get(0); + verifyNotificationEquals(notification, firstNotification); + } + + @Test + public void testExecute_withoutUserTypeForAdmin_shouldReturnAllNotifications() { + ______TS("Admin request to fetch notification"); + int expectedNumberOfNotifications = typicalBundle.notifications.size(); + loginAsAdmin(); + NotificationAttributes notification = typicalBundle.notifications.get("notification6"); + + String[] requestParams = new String[] { + Const.ParamsNames.NOTIFICATION_TARGET_USER, null, + Const.ParamsNames.NOTIFICATION_IS_FETCHING_ALL, String.valueOf(true), + }; + + GetNotificationAction action = getAction(requestParams); + JsonResult jsonResult = getJsonResult(action); + + NotificationsData output = (NotificationsData) jsonResult.getOutput(); + List notifications = output.getNotifications(); + + assertEquals(expectedNumberOfNotifications, + logic.getAllNotifications().size()); + assertEquals(expectedNumberOfNotifications, notifications.size()); + + NotificationData firstNotification = notifications.get(0); + verifyNotificationEquals(notification, firstNotification); + } + + @Test + public void testExecute_withoutUserTypeForNonAdmin_shouldFail() { + ______TS("Request without user type for non admin"); + InstructorAttributes instructor = typicalBundle.instructors.get("instructor1OfCourse1"); + loginAsInstructor(instructor.getGoogleId()); + GetNotificationAction action = getAction(Const.ParamsNames.NOTIFICATION_IS_FETCHING_ALL, String.valueOf(true)); + assertThrows(AssertionError.class, action::execute); + } + + @Test + public void testExecute_invalidUserType_shouldFail() { + ______TS("Request without invalid user type"); + InstructorAttributes instructor = typicalBundle.instructors.get("instructor1OfCourse1"); + loginAsInstructor(instructor.getGoogleId()); + + // when usertype is GENERAL + verifyHttpParameterFailure(Const.ParamsNames.NOTIFICATION_TARGET_USER, + NotificationTargetUser.GENERAL.toString(), + Const.ParamsNames.NOTIFICATION_IS_FETCHING_ALL, + String.valueOf(true)); + + // when usertype is a random string + verifyHttpParameterFailure(Const.ParamsNames.NOTIFICATION_TARGET_USER, + "invalid string", + Const.ParamsNames.NOTIFICATION_IS_FETCHING_ALL, + String.valueOf(true)); + } + + private void verifyNotificationEquals(NotificationAttributes expected, NotificationData actual) { + assertEquals(expected.getNotificationId(), actual.getNotificationId()); + assertEquals(expected.getType(), actual.getNotificationType()); + assertEquals(expected.getTargetUser(), actual.getTargetUser()); + assertEquals(expected.getTitle(), actual.getTitle()); + assertEquals(expected.getMessage(), actual.getMessage()); + } +} diff --git a/src/test/resources/data/typicalDataBundle.json b/src/test/resources/data/typicalDataBundle.json index dc69804fc02..6871d6f6ec2 100644 --- a/src/test/resources/data/typicalDataBundle.json +++ b/src/test/resources/data/typicalDataBundle.json @@ -1869,5 +1869,79 @@ "institute": "TEAMMATES Test Institute 2", "createdAt": "2011-01-01T00:00:00Z" } + }, + "notifications": { + "notification1": { + "notificationId": "notification1", + "startTime": "2011-01-01T00:00:00Z", + "endTime": "2099-01-01T00:00:00Z", + "createdAt": "2011-01-01T00:00:00Z", + "updatedAt": "2011-01-01T00:00:00Z", + "type": "DEPRECATION", + "targetUser": "GENERAL", + "title": "A deprecation note", + "message": "Deprecation happens in three minutes", + "shown": false + }, + "notification2": { + "notificationId": "notification2", + "startTime": "2011-02-02T00:00:00Z", + "endTime": "2099-02-02T00:00:00Z", + "createdAt": "2011-01-01T00:00:00Z", + "updatedAt": "2011-01-01T00:00:00Z", + "type": "VERSION_NOTE", + "targetUser": "STUDENT", + "title": "A note for update", + "message": "Exciting features", + "shown": false + }, + "notification3": { + "notificationId": "notification3", + "startTime": "2011-03-03T00:00:00Z", + "endTime": "2099-03-03T00:00:00Z", + "createdAt": "2011-01-01T00:00:00Z", + "updatedAt": "2011-01-01T00:00:00Z", + "type": "VERSION_NOTE", + "targetUser": "INSTRUCTOR", + "title": "The first version note", + "message": "The version note content", + "shown": false + }, + "notification4": { + "notificationId": "notification4", + "startTime": "2011-04-04T00:00:00Z", + "endTime": "2099-04-04T00:00:00Z", + "createdAt": "2011-01-01T00:00:00Z", + "updatedAt": "2011-01-01T00:00:00Z", + "type": "MAINTENANCE", + "targetUser": "GENERAL", + "title": "The note of maintenance", + "message": "The content of maintenance", + "shown": false + }, + "notification5": { + "notificationId": "notification5", + "startTime": "2011-05-05T00:00:00Z", + "endTime": "2099-05-05T00:00:00Z", + "createdAt": "2011-01-01T00:00:00Z", + "updatedAt": "2011-01-01T00:00:00Z", + "type": "TIPS", + "targetUser": "INSTRUCTOR", + "title": "The first tip to instructor", + "message": "The first tip content", + "shown": false + }, + "notification6": { + "notificationId": "notification6", + "startTime": "2011-06-06T00:00:00Z", + "endTime": "2099-06-06T00:00:00Z", + "createdAt": "2011-01-01T00:00:00Z", + "updatedAt": "2011-01-01T00:00:00Z", + "type": "DEPRECATION", + "targetUser": "STUDENT", + "title": "The note of maintenance", + "message": "The content of maintenance", + "shown": false + } } }