Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development: Restructure non-notification mails to use data transfer objects #10219

Open
wants to merge 26 commits into
base: develop
Choose a base branch
from

Conversation

xHadie
Copy link

@xHadie xHadie commented Jan 27, 2025

Checklist

General

Server

  • Important: I implemented the changes with a very good performance and prevented too many (unnecessary) and too complex database calls.
  • I strictly followed the server coding and design guidelines.
  • I added multiple integration tests (Spring) related to the features (with a high test coverage).
  • I documented the Java code using JavaDoc style.

Motivation and Context

The usage of DTOs prevents the leakage of potentially sensitive information, makes use cases clearer, and results in more efficient data transfer.
In the context of the mail service, it allows developers to easily see what information is actually being transferred in a mail.

Furthermore, this PR introduces a straightforward testing strategy for HTML mail content generated by thymeleaf.
This ensures that the shift to DTOs within this PR did not break the interpretation and send logic of the mail messages. In addition, it allows us to test that DTO content is really being used within the mail content, making editing the scopes of DTOs and sending methods more convenient.

During development, it revealed, for instance, that the dataExportFailedAdminEmail.html template used the recipient username instead of the username connected to the data export.

In case this PR gets accepted, we can apply the same approach to the notification-based mails and also refactor the underlying bulk of thymeleaf fragments.

Implements a part of #9524

Description

Architecture:

  • Implemented AbstractMailContentTest, which allows testing HTML content generated by thymeleaf via getGeneratedEmailTemplateText for the correct rendering of text
  • Implemented IMailRecipientUserDTO, which contains the minimal set of required attributes a mail receiver needs to have to be compatible with MailSendingService. Individual mail templates will implement this interface in a dedicated recipient record, which may be extended with additional attributes
  • Adjusted MailSendingService::sendMail to use IMailRecipientUserDTO instead of User
  • Adjusted MailService::createBaseContext, MailService::sendEmailFromTemplate, MailService::prepareTemplateAndSendEmail and MailService::prepareTemplateAndSendEmailWithArgumentInSubject to use IMailRecipientUserDTO instead of User

[Use case] Activation mail:

  • Added ActivationMailRecipientDTO and the accompanying ActivationMailTest and adjusted MailService::sendActivationEmail to use the DTO
  • Adjusted activationEmail.html

[Use case] Password reset mail:

  • same as activation mail

[Use case] SAML2 password reset mail:

  • same as activation mail

[Use case] Data export failed mail:

  • Added DataExportFailedContentDTO, which contains only information that is relevant to the template
  • Added DataExportFailedMailAdminRecipientDTO and the accompanying ActivationMailTest and adjusted MailService::sendActivationEmail to use the DTOs
  • Adjusted the parameter list of MailService::sendDataExportFailedEmailForAdmin
  • Adjusted dataExportFailedAdminEmail.html

[Use case] Data export successful mail:

  • Added DataExportSuccessfulContentsDTO and DataExportSuccessfulContentDTO, which contain only information that is relevant to the template.
  • Added DataExportSuccessfulMailAdminRecipientDTO and the accompanying DataExportSuccessfulMailTest and adjusted MailService::sendSuccessfulDataExportsEmailToAdmin to use the dto
  • Adjusted the parameter list of MailService::sendSuccessfulDataExportsEmailToAdmin
  • Adjusted successfulDataExportsAdminEmail.html

[Use case] Notifications mails:

  • Added NotificationMailRecipientDTO and adjusted MailService::sendNotification
  • This is a minimal required change. Notifications will be adjusted to use DTOs in a separate PR

Additional Improvements:

  • Reduced the visibility of MailService::sendSuccessfulDataExportsEmailToAdmin from public to private and adjusted DataExportScheduleServiceTest to use the parent method with an equal name
  • Reduced the visibility of MailService::sendDataExportFailedEmailForAdmin from public to private
  • Reduced the visibility of MailService::sendEmailFromTemplate from public to private
  • Fixed wrong username usage in dataExportFailedAdminEmail.html

Identified issues:

  • creationEmail.html is not being used
  • Missing structures and naming conventions for mail templates and in general, mail-related project substructures
  • MailService needs refactoring and is doing too much. Some of the private methods are relevant only for single-use cases. In my opinion, it would make sense to extract individual mail construction into their own classes and let MailService delegate the constructed mails to MailSendingService.

Testserver States

Note

These badges show the state of the test servers.
Green = Currently available, Red = Currently locked
Click on the badges to get to the test servers.







Review Progress

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

Use Case Instructions Manual Test - Local Manual Test - Server
Password Reset Email 1. Log out from Artemis.
2. Click "Forgot Password?".
3. Enter your registered email.
4. Click [Reset Password].
5. Verify that you receive a password reset email and check its content for accuracy.
Account Activation Email This test can only be performed locally. -
SAML2 Password Reset Email This test can only be performed locally. -
Data Export Failed Email This test can only be performed locally. -
Data Export Successful Email 1. Navigate to a test server with a Privacy Notice (e.g., TS3).
2. Click on Privacy in the footer.
3. Click Request Data Export.
4. Enter your geXXYYY username.
5. Verify that you receive an email (this may take a few days).
Notification Emails 1. Enable email notifications for "Exercise Released".
2. Create a Text Exercise.
3. Wait a few minutes.
4. Verify that you receive an "Exercise Released" email notification.

Test Coverage

Class/File Line Coverage Confirmation (assert/expect)
ActivationMailRecipientDTO.java 100%
DataExportFailedContentDTO.java 100%
DataExportSuccessfulContentDTO.java 100%
DataExportSuccessfulContentsDTO.java 100%
DataExportSuccessfulMailAdminRecipientDTO.java 100%
NotificationMailRecipientDTO.java 100%
PasswordResetRecipientDTO.java 100%
SAML2SetPasswordMailRecipientDTO.java 100%
WeeklyExerciseSummaryDTO.java 100%
WeeklySummaryMailContentDTO.java 100%
WeeklySummaryMailRecipientDTO.java 100%
IMailRecipientUserDTO.java 100%
MailSendingService.java 100%
MailService.java 95%

Summary by CodeRabbit

  • New Features
    • Enhanced email notifications with improved content presentation for activations, password resets, data export alerts, and weekly summaries.
    • Introduction of new data transfer objects (DTOs) for email recipient handling, improving structure and clarity.
  • Bug Fixes
    • Updated email templates to correctly reference new data structures for user information and failure reasons.
  • Refactor
    • Updated the handling of email recipient data to ensure consistency and streamlined message formatting across all notifications.
  • Tests
    • Expanded automated tests to validate the correct injection and display of user data within all email templates.
    • Introduced new test classes to cover various email notification scenarios, ensuring robust error handling and content validation.

@github-actions github-actions bot added tests server Pull requests that update Java code. (Added Automatically!) template communication Pull requests that affect the corresponding module core Pull requests that affect the corresponding module labels Jan 27, 2025
@xHadie xHadie temporarily deployed to artemis-test2.artemis.cit.tum.de January 27, 2025 10:38 — with GitHub Actions Inactive
@xHadie xHadie temporarily deployed to artemis-test3.artemis.cit.tum.de January 28, 2025 07:33 — with GitHub Actions Inactive
@xHadie xHadie temporarily deployed to artemis-test1.artemis.cit.tum.de January 28, 2025 10:16 — with GitHub Actions Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/data_export_failed_mail/DataExportFailedContentDTO.java (1)

27-35: ⚠️ Potential issue

Add validation and sanitize exception message.

The current implementation has potential issues:

  1. No sanitization of exception messages
  2. Raw exception message might contain sensitive information

Apply this diff to implement message sanitization:

    public static DataExportFailedContentDTO of(Exception exception, DataExport dataExport) {
        if (exception == null || dataExport == null) {
            throw new IllegalArgumentException("Parameters cannot be null");
        }
        if (dataExport.getUser() == null) {
            throw new IllegalStateException("DataExport user cannot be null");
        }
+       // Sanitize exception message to prevent sensitive information leakage
+       String sanitizedMessage = sanitizeExceptionMessage(exception.getMessage());
-       return new DataExportFailedContentDTO(exception.getMessage(), dataExport.getUser().getLogin());
+       return new DataExportFailedContentDTO(sanitizedMessage, dataExport.getUser().getLogin());
    }

+   private static String sanitizeExceptionMessage(String message) {
+       if (message == null) {
+           return "Unknown error";
+       }
+       // Remove potential sensitive information
+       return message.replaceAll("(?i)(password|secret|key|token|credentials)=.*?(&|$)", "$1=*****$2")
+                    .replaceAll("(?i)(jdbc|ftp|sftp|smtp)://[^\\s]*", "[REDACTED_URL]")
+                    .replaceAll("\\d{4}[- ]?\\d{4}[- ]?\\d{4}[- ]?\\d{4}", "[REDACTED_CARD_NUMBER]");
+   }
🧹 Nitpick comments (1)
src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/password_reset_mail/PasswordResetRecipientDTO.java (1)

8-12: LGTM! Well-structured DTO using Java record.

The implementation follows best practices:

  • Uses Java record for immutable data structure
  • Implements the DTO interface contract
  • Includes JSON serialization optimization

Consider enhancing the class-level JavaDoc to document the purpose of each field:

 /**
  * DTO for the password reset mail recipient
+ *
+ * @param langKey  The language key for localization
+ * @param email    The recipient's email address
+ * @param login    The recipient's login identifier
+ * @param resetKey The password reset token
  */
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f86988f and b65f74a.

📒 Files selected for processing (4)
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/activation_mail/ActivationMailRecipientDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/data_export_failed_mail/DataExportFailedContentDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/password_reset_mail/PasswordResetRecipientDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/saml2_set_password_mail/SAML2SetPasswordMailRecipientDTO.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/activation_mail/ActivationMailRecipientDTO.java
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/saml2_set_password_mail/SAML2SetPasswordMailRecipientDTO.java
🧰 Additional context used
📓 Path-based instructions (1)
`src/main/java/**/*.java`: naming:CamelCase; principles:{sin...

src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/password_reset_mail/PasswordResetRecipientDTO.java
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/data_export_failed_mail/DataExportFailedContentDTO.java
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: Call Build Workflow / Build and Push Docker Image
  • GitHub Check: Call Build Workflow / Build .war artifact
  • GitHub Check: client-tests
  • GitHub Check: client-style
  • GitHub Check: server-style
  • GitHub Check: server-tests
  • GitHub Check: Analyse
🔇 Additional comments (6)
src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/password_reset_mail/PasswordResetRecipientDTO.java (2)

1-7: LGTM! Package structure and imports are well-organized.

The package structure follows Java conventions, and imports are specific without any star imports.


14-31: LGTM! Factory method includes proper validation.

The implementation includes appropriate null checks and validation as previously suggested, with clear error messages and well-documented exceptions.

src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/data_export_failed_mail/DataExportFailedContentDTO.java (4)

1-6: LGTM!

Package structure follows naming conventions and imports are properly organized without wildcards.


7-13: LGTM!

Documentation is clear and complete. The @JsonInclude annotation helps minimize the DTO payload.


14-14: LGTM!

Appropriate use of Java record for an immutable DTO with minimal data fields.


16-26: LGTM!

Factory method documentation is thorough and clearly describes parameters, return value, and potential exceptions.

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 9, 2025
@helios-aet helios-aet bot temporarily deployed to artemis-test6.artemis.cit.tum.de February 10, 2025 09:04 Inactive
Copy link
Contributor

@ole-ve ole-ve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the code, see my inline comments.

Really simplifies the mail templating, nice work 👍

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/ActivationMailTest.java (2)

17-19: ⚠️ Potential issue

Update test to use ActivationMailRecipientDTO.

The test currently uses the User entity directly, which doesn't align with the PR's objective of using DTOs for mail services.


7-7: ⚠️ Potential issue

Replace User import with ActivationMailRecipientDTO.

The PR aims to transition from using User entities to DTOs. Update the import to use ActivationMailRecipientDTO instead.

-import de.tum.cit.aet.artemis.core.domain.User;
+import de.tum.cit.aet.artemis.communication.service.notifications.mails.dto.activation_mail.ActivationMailRecipientDTO;
🧹 Nitpick comments (6)
src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/AbstractMailContentTest.java (2)

30-53: Consider making non-protected fields private.

Fields that are only used within this class should be private to maintain encapsulation. This includes templateEngine, markdownCustomLinkRendererService, and markdownCustomReferenceRendererService.

Apply this diff:

-    private SpringTemplateEngine templateEngine;
+    @Autowired
+    private final SpringTemplateEngine templateEngine;

-    private MarkdownCustomLinkRendererService markdownCustomLinkRendererService;
+    @Autowired
+    private final MarkdownCustomLinkRendererService markdownCustomLinkRendererService;

-    private MarkdownCustomReferenceRendererService markdownCustomReferenceRendererService;
+    @Autowired
+    private final MarkdownCustomReferenceRendererService markdownCustomReferenceRendererService;

58-62: Consider parameterizing the language key.

The createMinimalMailRecipientUser method hardcodes the language key to "de". Consider making it a parameter to support testing with different locales.

Apply this diff:

-    protected User createMinimalMailRecipientUser() {
+    protected User createMinimalMailRecipientUser(String langKey) {
         User recipient = new User();
-        recipient.setLangKey("de");
+        recipient.setLangKey(langKey);
         return recipient;
     }
src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/DataExportSuccessfulMailTest.java (2)

14-35: Enhance test method for better clarity and coverage.

While the test follows good practices like AAA pattern and AssertJ assertions, consider these improvements:

  1. Rename method to describe the scenario without "test" prefix (e.g., verifyUserLoginsAreInjectedIntoTemplate).
  2. Use more specific assertions to verify exact content and order.
  3. Use more descriptive variable names (e.g., adminRecipient instead of recipient).
  4. Add assertions for other template variables.
-    void testThatVariablesAreInjectedIntoTheTemplate() {
+    void verifyUserLoginsAreInjectedIntoTemplate() {
         // Arrange:
-        User recipient = createMinimalMailRecipientUser();
-        recipient.setLogin("test_login");
-        recipient.setResetKey("test_reset_key");
+        User adminRecipient = createMinimalMailRecipientUser();
+        adminRecipient.setLogin("admin_login");
+        adminRecipient.setResetKey("test_reset_key");
-        String subject = createExpectedSubject(recipient, "email.successfulDataExportCreationsAdmin.title");
+        String emailSubject = createExpectedSubject(adminRecipient, "email.successfulDataExportCreationsAdmin.title");

         Set<DataExport> dataExports = createThreeDataExportsWithThreeDifferentUsers();

         // Act:
-        mailService.sendSuccessfulDataExportsEmailToAdmin(recipient, dataExports);
+        mailService.sendSuccessfulDataExportsEmailToAdmin(adminRecipient, dataExports);

         // Assert:
-        String capturedContent = getGeneratedEmailTemplateText(subject);
-        assertThat(capturedContent).contains("test_subject_1");
-        assertThat(capturedContent).contains("test_subject_2");
-        assertThat(capturedContent).contains("test_subject_3");
+        String emailContent = getGeneratedEmailTemplateText(emailSubject);
+        assertThat(emailContent)
+            .contains("Dear admin_login")
+            .contains("The following data exports have been created successfully:")
+            .satisfies(content -> 
+                assertThat(content.lines())
+                    .filteredOn(line -> line.contains("test_subject"))
+                    .containsExactlyInAnyOrder(
+                        "test_subject_1",
+                        "test_subject_2",
+                        "test_subject_3"
+                    )
+            );

37-52: Improve helper method for better reusability and readability.

Consider these improvements:

  1. Make the method more concise using streams.
  2. Use more meaningful test data.
  3. Consider moving to a test utility class for reuse.
-    private static Set<DataExport> createThreeDataExportsWithThreeDifferentUsers() {
-        User dataExportSubjectUser1 = new User();
-        User dataExportSubjectUser2 = new User();
-        User dataExportSubjectUser3 = new User();
-        dataExportSubjectUser1.setLogin("test_subject_1");
-        dataExportSubjectUser2.setLogin("test_subject_2");
-        dataExportSubjectUser3.setLogin("test_subject_3");
-        DataExport dataExport1 = new DataExport();
-        DataExport dataExport2 = new DataExport();
-        DataExport dataExport3 = new DataExport();
-        dataExport1.setUser(dataExportSubjectUser1);
-        dataExport2.setUser(dataExportSubjectUser2);
-        dataExport3.setUser(dataExportSubjectUser3);
-
-        return Set.of(dataExport1, dataExport2, dataExport3);
+    private static Set<DataExport> createThreeDataExportsWithThreeDifferentUsers() {
+        return Stream.of("student", "tutor", "instructor")
+            .map(role -> {
+                User user = new User();
+                user.setLogin("test_" + role);
+                DataExport dataExport = new DataExport();
+                dataExport.setUser(user);
+                return dataExport;
+            })
+            .collect(Collectors.toSet());
+    }

Consider moving this to a test utility class:

public final class DataExportTestUtil {
    private DataExportTestUtil() {
        // Prevent instantiation
    }

    public static Set<DataExport> createDataExportsWithUsers(String... userLogins) {
        return Arrays.stream(userLogins)
            .map(login -> {
                User user = new User();
                user.setLogin(login);
                DataExport dataExport = new DataExport();
                dataExport.setUser(user);
                return dataExport;
            })
            .collect(Collectors.toSet());
    }
}
src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/ActivationMailTest.java (2)

14-15: Make test method name more specific.

The current method name testThatVariablesAreInjectedIntoTheTemplate is too generic. Consider renaming it to be more specific about which variables are being tested.

-    void testThatVariablesAreInjectedIntoTheTemplate() {
+    void testThatLoginAndActivationKeyAreInjectedIntoActivationTemplate() {

27-28: Make assertions more specific.

The current assertions only check if the values exist somewhere in the content. Consider using more specific assertions that verify where in the template these values should appear.

-        assertThat(capturedContent).contains("test_student");
-        assertThat(capturedContent).contains("test_key");
+        assertThat(capturedContent)
+            .describedAs("Login should be present in the greeting section")
+            .contains("<span>Dear test_student</span>");
+        assertThat(capturedContent)
+            .describedAs("Activation key should be present in the activation link")
+            .contains("key=test_key");
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b65f74a and a3fd585.

📒 Files selected for processing (7)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/AbstractMailContentTest.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/ActivationMailTest.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/DataExportFailedMailTest.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/DataExportSuccessfulMailTest.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/PasswordResetMailTest.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/SAML2SetPasswordMailTest.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/WeeklySummaryMailTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/DataExportFailedMailTest.java
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/SAML2SetPasswordMailTest.java
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/WeeklySummaryMailTest.java
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/PasswordResetMailTest.java
🧰 Additional context used
📓 Path-based instructions (1)
`src/test/java/**/*.java`: test_naming: descriptive; test_si...

src/test/java/**/*.java: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true

  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/ActivationMailTest.java
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/AbstractMailContentTest.java
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/DataExportSuccessfulMailTest.java
🔇 Additional comments (4)
src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/AbstractMailContentTest.java (2)

1-28: LGTM! Well-structured test class.

The class is well-organized with appropriate package structure, imports, and documentation. The package-private visibility is suitable for a test class.


73-77: Potential test flakiness in parallel execution.

The getGeneratedEmailTemplateText method might cause flaky tests when running in parallel, as it uses a shared ArgumentCaptor.

Consider adding a unique identifier (like the subject) to the verification to ensure test isolation:

     protected String getGeneratedEmailTemplateText(String expectedSubject) {
         ArgumentCaptor<String> contentCaptor = ArgumentCaptor.forClass(String.class);
-        verify(mailSendingService).sendEmail(any(), eq(expectedSubject), contentCaptor.capture(), anyBoolean(), anyBoolean());
+        verify(mailSendingService).sendEmail(any(), eq(expectedSubject), contentCaptor.capture(), anyBoolean(), anyBoolean());
         return contentCaptor.getValue();
     }
src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/DataExportSuccessfulMailTest.java (1)

1-12: LGTM!

The class is well-structured with appropriate naming, minimal imports, and proper inheritance from AbstractMailContentTest.

src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/ActivationMailTest.java (1)

9-9: LGTM!

The class name is descriptive and follows test naming conventions. The inheritance from AbstractMailContentTest is appropriate for mail content testing.

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 17, 2025
coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 17, 2025
@helios-aet helios-aet bot temporarily deployed to artemis-test2.artemis.cit.tum.de February 17, 2025 21:28 Inactive
ole-ve
ole-ve previously approved these changes Feb 18, 2025
Copy link
Contributor

@ole-ve ole-ve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for integrating the suggestion, code looks good 👍

BBesrour
BBesrour previously approved these changes Feb 19, 2025
Copy link
Member

@BBesrour BBesrour left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code

@xHadie xHadie dismissed stale reviews from BBesrour, coderabbitai[bot], and ole-ve via b913cf9 February 19, 2025 13:14
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (6)
src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/SAML2SetPasswordMailTest.java (3)

8-8: ⚠️ Potential issue

Replace User import with SAML2SetPasswordMailRecipientDTO.

The PR aims to transition from User entities to DTOs for mail recipients. Update the import to use SAML2SetPasswordMailRecipientDTO instead.

-import de.tum.cit.aet.artemis.core.domain.User;
+import de.tum.cit.aet.artemis.communication.service.notifications.mails.dto.saml2_set_password_mail.SAML2SetPasswordMailRecipientDTO;

16-30: ⚠️ Potential issue

Update test to use SAML2SetPasswordMailRecipientDTO.

The test should use SAML2SetPasswordMailRecipientDTO instead of User entity to align with the PR's objective of using DTOs for mail recipients.

    @Test
    void testThatVariablesAreInjectedIntoTheTemplate() {
        // Arrange:
-       User recipient = createMinimalMailRecipientUser();
-       recipient.setLogin("test_login");
-       recipient.setResetKey("test_reset_key");
+       SAML2SetPasswordMailRecipientDTO recipient = new SAML2SetPasswordMailRecipientDTO(
+           "test_login",
+           "test_reset_key",
+           "en",
+           "[email protected]"
+       );
        String subject = createExpectedSubject(recipient, "email.saml.title");

        // Act:
        mailService.sendSAML2SetPasswordMail(recipient);

        // Assert:
        String capturedContent = getGeneratedEmailTemplateText(subject);
        assertThat(capturedContent).contains("test_login");
        assertThat(capturedContent).contains("test_reset_key");
    }

32-39: ⚠️ Potential issue

Update exception test to use SAML2SetPasswordMailRecipientDTO.

The test should use SAML2SetPasswordMailRecipientDTO instead of User entity to maintain consistency with the PR's objective.

    @Test
    void testThatExceptionIsThrownWhenResetKeyIsMissing() {
        // Arrange:
-       User recipient = createMinimalMailRecipientUser();
+       SAML2SetPasswordMailRecipientDTO recipient = new SAML2SetPasswordMailRecipientDTO(
+           "test_login",
+           null,
+           "en",
+           "[email protected]"
+       );

        // Act and Assert:
        assertThatThrownBy(() -> mailService.sendSAML2SetPasswordMail(recipient))
            .isInstanceOf(IllegalStateException.class)
            .hasMessage("Reset key is required");
    }
src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/ActivationMailTest.java (1)

35-36: 🛠️ Refactor suggestion

Update test to use ActivationMailRecipientDTO.

The test should use ActivationMailRecipientDTO to maintain consistency with the service layer changes.

-        User recipient = createMinimalMailRecipientUser();
-        recipient.setLogin("test_student");
+        ActivationMailRecipientDTO recipient = new ActivationMailRecipientDTO(
+            "test_student",
+            null,  // Missing activation key to trigger exception
+            "en",
+            "[email protected]"
+        );
src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/password_reset_mail/PasswordResetRecipientDTO.java (1)

22-27: ⚠️ Potential issue

Add null check in factory method.

The factory method should validate both the user object and ensure resetKey is present for password reset functionality.

     public static PasswordResetRecipientDTO of(User user) {
+        if (user == null) {
+            throw new IllegalArgumentException("User cannot be null");
+        }
         if (user.getResetKey() == null) {
             throw new IllegalStateException("Reset key is required");
         }
         return new PasswordResetRecipientDTO(user.getLangKey(), user.getEmail(), user.getLogin(), user.getResetKey());
     }
src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/PasswordResetMailTest.java (1)

12-30: 🛠️ Refactor suggestion

Update test to use PasswordResetRecipientDTO.

The test should use PasswordResetRecipientDTO instead of the User entity to align with the PR's objective of using DTOs for non-notification mails.

     @Test
     void testThatVariablesAreInjectedIntoTheTemplate() {
         // Arrange:
-        User recipient = createMinimalMailRecipientUser();
-        recipient.setLogin("test_login");
-        recipient.setResetKey("test_reset_key");
+        PasswordResetRecipientDTO recipient = new PasswordResetRecipientDTO(
+            "en",
+            "[email protected]",
+            "test_login",
+            "test_reset_key"
+        );
         String subject = createExpectedSubject(recipient, "email.reset.title");

         // Act:
         mailService.sendPasswordResetMail(recipient);

         // Assert:
         String capturedContent = getGeneratedEmailTemplateText(subject);
         assertThat(capturedContent).contains("test_login");
         assertThat(capturedContent).contains("test_reset_key");
     }
🧹 Nitpick comments (4)
src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/DataExportFailedMailTest.java (3)

23-24: Fix typo in variable name.

The variable name contains a typo: dataExporSubjecttUser should be dataExportSubjectUser.

-        User dataExporSubjecttUser = new User();
-        dataExporSubjecttUser.setLogin("test_subject");
+        User dataExportSubjectUser = new User();
+        dataExportSubjectUser.setLogin("test_subject");

17-39: Enhance test coverage and data quality.

While the test follows good practices with AAA pattern and clear assertions, consider these improvements:

  1. Use more realistic test data instead of test_* prefixes
  2. Add assertions for the complete HTML structure to ensure template integrity
  3. Consider testing different types of exceptions and their messages

41-70: Consider using parameterized tests for null checks.

The null check tests follow good practices but could be more concise using JUnit 5's @ParameterizedTest feature. This would reduce code duplication while maintaining the same coverage.

Example refactor:

@ParameterizedTest
@MethodSource("nullParameterProvider")
void testExceptionsThrownForNullParameters(User recipient, DataExport dataExport, Exception exception, Class<? extends Exception> expectedExceptionType, String expectedMessage) {
    assertThatThrownBy(() -> 
        mailService.sendDataExportFailedEmailToAdmin(recipient, dataExport, exception))
        .isInstanceOf(expectedExceptionType)
        .hasMessage(expectedMessage);
}

private static Stream<Arguments> nullParameterProvider() {
    User recipient = createMinimalMailRecipientUser();
    return Stream.of(
        Arguments.of(recipient, new DataExport(), null, IllegalArgumentException.class, "Parameters cannot be null"),
        Arguments.of(recipient, null, new Exception(), IllegalArgumentException.class, "Parameters cannot be null"),
        Arguments.of(recipient, new DataExport(), new Exception(), IllegalStateException.class, "DataExport user cannot be null")
    );
}
src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/password_reset_mail/PasswordResetRecipientDTO.java (1)

14-21: Improve Javadoc documentation.

The documentation needs the following improvements:

  • Fix formatting in @param tag (remove extra indentation)
  • Add @throws tag for IllegalArgumentException when user is null
     /**
      * Factory method to create a {@link PasswordResetRecipientDTO} instance from a {@link User}.
      *
      * @param user The user object used to populate the DTO.
-     *                 The {@code resetKey} field in the {@code User}.
+     *            The {@code resetKey} field in the {@code User}.
      * @return A new {@link PasswordResetRecipientDTO} containing the user's language key, email, login, and reset key.
      * @throws IllegalStateException if the {@code resetKey} field of {@code user} is {@code null}.
+     * @throws IllegalArgumentException if {@code user} is {@code null}.
      */
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e08757b and b913cf9.

📒 Files selected for processing (11)
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/activation_mail/ActivationMailRecipientDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/notifications/NotificationMailRecipientDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/password_reset_mail/PasswordResetRecipientDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/saml2_set_password_mail/SAML2SetPasswordMailRecipientDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/weekly_summary_mail/WeeklySummaryMailRecipientDTO.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/ActivationMailTest.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/DataExportFailedMailTest.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/DataExportSuccessfulMailTest.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/PasswordResetMailTest.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/SAML2SetPasswordMailTest.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/WeeklySummaryMailTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/weekly_summary_mail/WeeklySummaryMailRecipientDTO.java
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/activation_mail/ActivationMailRecipientDTO.java
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/WeeklySummaryMailTest.java
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/saml2_set_password_mail/SAML2SetPasswordMailRecipientDTO.java
  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/notifications/NotificationMailRecipientDTO.java
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/DataExportSuccessfulMailTest.java
🧰 Additional context used
📓 Path-based instructions (2)
`src/main/java/**/*.java`: naming:CamelCase; principles:{sin...

src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

  • src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/password_reset_mail/PasswordResetRecipientDTO.java
`src/test/java/**/*.java`: test_naming: descriptive; test_si...

src/test/java/**/*.java: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true

  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/PasswordResetMailTest.java
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/SAML2SetPasswordMailTest.java
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/DataExportFailedMailTest.java
  • src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/ActivationMailTest.java
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: Call Build Workflow / Build and Push Docker Image
  • GitHub Check: Call Build Workflow / Build .war artifact
  • GitHub Check: client-style
  • GitHub Check: server-tests
  • GitHub Check: server-style
  • GitHub Check: client-tests
  • GitHub Check: Analyse
🔇 Additional comments (3)
src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/DataExportFailedMailTest.java (1)

1-11: LGTM! Well-structured test class setup.

The class follows the coding guidelines with proper:

  • Use of AssertJ's assertThat style assertions
  • Clear and descriptive test class naming
  • Appropriate test inheritance hierarchy
src/test/java/de/tum/cit/aet/artemis/communication/notifications/service/mail/ActivationMailTest.java (1)

18-20: Update test to use ActivationMailRecipientDTO.

The test currently uses the User entity directly, which doesn't align with the PR's objective of using DTOs for mail services.

-        User recipient = createMinimalMailRecipientUser();
-        recipient.setLogin("test_student");
-        recipient.setActivationKey("test_key");
+        ActivationMailRecipientDTO recipient = new ActivationMailRecipientDTO(
+            "test_student",
+            "test_key",
+            "en",
+            "[email protected]"
+        );
src/main/java/de/tum/cit/aet/artemis/communication/service/notifications/mails/dto/password_reset_mail/PasswordResetRecipientDTO.java (1)

1-12: LGTM! Well-structured DTO declaration.

The record follows Java naming conventions, implements the appropriate interface, and uses specific imports. The @JsonInclude annotation helps minimize the JSON payload.

@helios-aet helios-aet bot temporarily deployed to artemis-test3.artemis.cit.tum.de February 19, 2025 15:14 Inactive
Copy link
Contributor

@MarkusPaulsen MarkusPaulsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good + Tested Notification Emails Functionality on TS3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
communication Pull requests that affect the corresponding module core Pull requests that affect the corresponding module ready for review server Pull requests that update Java code. (Added Automatically!) template tests
Projects
Status: Ready For Review
Development

Successfully merging this pull request may close these issues.

4 participants