From 971bb9de9a1c5b71d53d2760c97536405fc7182a Mon Sep 17 00:00:00 2001 From: nycotin Date: Sat, 17 Jan 2026 23:16:24 +0100 Subject: [PATCH 01/20] Fix update of mentorship types in mentee section --- .../repository/postgres/PostgresMenteeSectionRepository.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepository.java b/src/main/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepository.java index 75a2e705..3215ad05 100644 --- a/src/main/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepository.java +++ b/src/main/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepository.java @@ -43,6 +43,8 @@ public class PostgresMenteeSectionRepository implements MenteeSectionRepository "INSERT INTO mentor_availability (mentor_id, month_num, hours) VALUES (?, ?, ?)"; private static final String INSERT_MENTOR_TYPES = "INSERT INTO mentor_mentorship_types (mentor_id, mentorship_type) VALUES (?, ?)"; + private static final String DELETE_MENTOR_TYPES = + "DELETE FROM mentor_mentorship_types WHERE mentor_id = ?"; private final JdbcTemplate jdbc; /** Inserts the mentee section details for the mentor. */ @@ -58,7 +60,8 @@ public void insertMenteeSection(final MenteeSection menteeSec, final Long member */ public void updateMenteeSection(final MenteeSection menteeSec, final Long mentorId) { jdbc.update(UPDATE_MENTEE_SECTION, menteeSec.idealMentee(), menteeSec.additional(), mentorId); - jdbc.update(UPDATE_MENTOR_TYPE, menteeSec.mentorshipType(), mentorId); + jdbc.update(DELETE_MENTOR_TYPES, mentorId); + insertMentorshipTypes(menteeSec, mentorId); updateAvailability(menteeSec, mentorId); } From b68e92f09daf00afdf94ecb62c6e90aeabb93a6a Mon Sep 17 00:00:00 2001 From: nycotin Date: Sat, 17 Jan 2026 23:18:54 +0100 Subject: [PATCH 02/20] Change parameter validator for Country object --- .../com/wcc/platform/domain/platform/mentorship/MentorDto.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java b/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java index c7157aac..e09e562b 100644 --- a/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java +++ b/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java @@ -49,7 +49,7 @@ public MentorDto( @NotBlank final String position, @NotBlank @Email final String email, @NotBlank final String slackDisplayName, - @NotBlank final Country country, + @NotNull final Country country, @NotBlank final String city, final String companyName, final List images, From af31354b6f7f58c5012dcefeeebd8314c55d04fb Mon Sep 17 00:00:00 2001 From: nycotin Date: Sat, 17 Jan 2026 23:29:58 +0100 Subject: [PATCH 03/20] Add validation to prevent data loss --- .../com/wcc/platform/domain/platform/member/MemberDto.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/wcc/platform/domain/platform/member/MemberDto.java b/src/main/java/com/wcc/platform/domain/platform/member/MemberDto.java index 5941cda8..bac70f4e 100644 --- a/src/main/java/com/wcc/platform/domain/platform/member/MemberDto.java +++ b/src/main/java/com/wcc/platform/domain/platform/member/MemberDto.java @@ -3,6 +3,7 @@ import com.wcc.platform.domain.cms.attributes.Country; import com.wcc.platform.domain.cms.attributes.Image; import com.wcc.platform.domain.platform.SocialNetwork; +import com.wcc.platform.domain.platform.mentorship.MentorDto; import com.wcc.platform.domain.platform.type.MemberType; import java.util.List; import lombok.AllArgsConstructor; @@ -39,6 +40,12 @@ public class MemberDto { * @return Updated member */ public Member merge(final Member member) { + if (this instanceof MentorDto) { + throw new IllegalStateException( + "MemberDto.merge() should not be called on MentorDto. " + + "This is likely a programming error - ensure the correct type is used."); + } + return member.toBuilder() .id(member.getId()) .fullName(getFullName()) From ad5e31a802a093fb06c7574240a67c881b137325 Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 11:29:32 +0100 Subject: [PATCH 04/20] Add unit tests for mentee section update repo --- .../PostgresMenteeSectionRepository.java | 2 +- .../PostgresMenteeSectionRepositoryTest.java | 173 ++++++++++++++++++ 2 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java diff --git a/src/main/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepository.java b/src/main/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepository.java index 3215ad05..8f200146 100644 --- a/src/main/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepository.java +++ b/src/main/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepository.java @@ -119,7 +119,7 @@ private void insertMentorshipTypes(final MenteeSection ms, final Long memberId) private void updateAvailability(final MenteeSection ms, final Long memberId) { for (final MentorMonthAvailability a : ms.availability()) { - jdbc.update(UPDATE_AVAILABILITY, memberId, a.month().getValue(), a.hours()); + jdbc.update(UPDATE_AVAILABILITY, a.month().getValue(), a.hours(), memberId); } } } diff --git a/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java b/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java new file mode 100644 index 00000000..996ff1a8 --- /dev/null +++ b/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java @@ -0,0 +1,173 @@ +package com.wcc.platform.repository.postgres; + +import static com.wcc.platform.repository.postgres.PostgresMenteeSectionRepository.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import com.wcc.platform.domain.cms.pages.mentorship.MenteeSection; +import com.wcc.platform.domain.cms.pages.mentorship.MentorMonthAvailability; +import com.wcc.platform.domain.platform.mentorship.MentorshipType; +import java.time.Month; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.jdbc.core.JdbcTemplate; + +@ExtendWith(MockitoExtension.class) +class PostgresMenteeSectionRepositoryTest { + + @Mock private JdbcTemplate jdbc; + + @InjectMocks private PostgresMenteeSectionRepository menteeSecRepo; + + private MenteeSection menteeSection; + private Long mentorId; + + @BeforeEach + void setUp() { + mentorId = 1L; + + List mentorshipTypes = List.of(MentorshipType.LONG_TERM, MentorshipType.AD_HOC); + + List availability = + List.of( + new MentorMonthAvailability(Month.JANUARY, 2), + new MentorMonthAvailability(Month.FEBRUARY, 3)); + + menteeSection = + new MenteeSection( + mentorshipTypes, + availability, + "Ideal mentee description UPDATED", + "Additional info UPDATED"); + } + + @Test + void testUpdateMenteeSection_Success() { + + menteeSecRepo.updateMenteeSection(menteeSection, mentorId); + + verify(jdbc, times(1)) + .update( + eq(UPDATE_MENTEE_SECTION), + eq("Ideal mentee description UPDATED"), + eq("Additional info UPDATED"), + eq(mentorId)); + + verify(jdbc, times(1)).update(contains("DELETE FROM mentor_mentorship_types"), eq(mentorId)); + + verify(jdbc, times(2)) + .update(contains("INSERT INTO mentor_mentorship_types"), eq(mentorId), anyInt()); + + verify(jdbc, times(2)) + .update( + contains("UPDATE mentor_availability"), + anyInt(), // month_num + anyInt(), // hours + eq(mentorId)); + } + + @Test + void testUpdateMenteeSection_UpdatesTextFields() { + menteeSecRepo.updateMenteeSection(menteeSection, mentorId); + + verify(jdbc) + .update( + eq(UPDATE_MENTEE_SECTION), + eq("Ideal mentee description UPDATED"), + eq("Additional info UPDATED"), + eq(mentorId)); + } + + @Test + void testUpdateMenteeSection_DeletesOldMentorshipTypes() { + menteeSecRepo.updateMenteeSection(menteeSection, mentorId); + + verify(jdbc) + .update(contains("DELETE FROM mentor_mentorship_types WHERE mentor_id = ?"), eq(mentorId)); + } + + @Test + void testUpdateMenteeSection_InsertsAllMentorshipTypes() { + menteeSecRepo.updateMenteeSection(menteeSection, mentorId); + + verify(jdbc) + .update( + contains("INSERT INTO mentor_mentorship_types"), + eq(mentorId), + eq(MentorshipType.LONG_TERM.getMentorshipTypeId())); + + verify(jdbc) + .update( + contains("INSERT INTO mentor_mentorship_types"), + eq(mentorId), + eq(MentorshipType.AD_HOC.getMentorshipTypeId())); + } + + @Test + void testUpdateMenteeSection_UpdatesAllAvailability() { + menteeSecRepo.updateMenteeSection(menteeSection, mentorId); + + verify(jdbc) + .update( + contains("UPDATE mentor_availability"), + eq(Month.JANUARY.getValue()), + eq(2), + eq(mentorId)); + + verify(jdbc) + .update( + contains("UPDATE mentor_availability"), + eq(Month.FEBRUARY.getValue()), + eq(3), + eq(mentorId)); + } + + @Test + void testUpdateMenteeSection_WithEmptyMentorshipTypes() { + MenteeSection emptyTypesSection = + new MenteeSection( + List.of(), // Empty mentorship types + List.of(new MentorMonthAvailability(Month.APRIL, 1)), + "Ideal mentee", + "Additional"); + + menteeSecRepo.updateMenteeSection(emptyTypesSection, mentorId); + + verify(jdbc, times(1)).update(contains("DELETE FROM mentor_mentorship_types"), eq(mentorId)); + + verify(jdbc, never()) + .update(contains("INSERT INTO mentor_mentorship_types"), anyLong(), anyInt()); + } + + @Test + void testUpdateMenteeSection_WithEmptyAvailability() { + MenteeSection emptyAvailabilitySection = + new MenteeSection( + List.of(MentorshipType.AD_HOC), + List.of(), // Empty availability + "Ideal mentee", + "Additional"); + + menteeSecRepo.updateMenteeSection(emptyAvailabilitySection, mentorId); + + verify(jdbc, never()) + .update(contains("UPDATE mentor_availability"), anyInt(), anyInt(), anyLong()); + } + + @Test + void testUpdateMenteeSection_VerifiesAllSqlStatements() { + menteeSecRepo.updateMenteeSection(menteeSection, mentorId); + + verify(jdbc).update(eq(UPDATE_MENTEE_SECTION), anyString(), anyString(), anyLong()); + verify(jdbc).update(contains("DELETE FROM mentor_mentorship_types"), anyLong()); + verify(jdbc, atLeastOnce()) + .update(contains("INSERT INTO mentor_mentorship_types"), anyLong(), anyInt()); + verify(jdbc, atLeastOnce()) + .update(contains("UPDATE mentor_availability"), anyInt(), anyInt(), anyLong()); + } +} From c0208ec989c9bf7e54fbd06985bf7ac32af92183 Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 11:31:17 +0100 Subject: [PATCH 05/20] Remove unnecessary validation --- .../com/wcc/platform/domain/platform/member/MemberDto.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/com/wcc/platform/domain/platform/member/MemberDto.java b/src/main/java/com/wcc/platform/domain/platform/member/MemberDto.java index bac70f4e..5941cda8 100644 --- a/src/main/java/com/wcc/platform/domain/platform/member/MemberDto.java +++ b/src/main/java/com/wcc/platform/domain/platform/member/MemberDto.java @@ -3,7 +3,6 @@ import com.wcc.platform.domain.cms.attributes.Country; import com.wcc.platform.domain.cms.attributes.Image; import com.wcc.platform.domain.platform.SocialNetwork; -import com.wcc.platform.domain.platform.mentorship.MentorDto; import com.wcc.platform.domain.platform.type.MemberType; import java.util.List; import lombok.AllArgsConstructor; @@ -40,12 +39,6 @@ public class MemberDto { * @return Updated member */ public Member merge(final Member member) { - if (this instanceof MentorDto) { - throw new IllegalStateException( - "MemberDto.merge() should not be called on MentorDto. " - + "This is likely a programming error - ensure the correct type is used."); - } - return member.toBuilder() .id(member.getId()) .fullName(getFullName()) From 08593feddabd5c6a7c16a678208e42d1260b5393 Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 11:33:06 +0100 Subject: [PATCH 06/20] Change paramenter to Mentor object to avoid data loss --- .../domain/platform/mentorship/MentorDto.java | 67 ++++++++----------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java b/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java index e09e562b..89aed705 100644 --- a/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java +++ b/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java @@ -4,9 +4,7 @@ import com.wcc.platform.domain.cms.attributes.Image; import com.wcc.platform.domain.cms.pages.mentorship.FeedbackSection; import com.wcc.platform.domain.cms.pages.mentorship.MenteeSection; -import com.wcc.platform.domain.exceptions.InvalidMentorException; import com.wcc.platform.domain.platform.SocialNetwork; -import com.wcc.platform.domain.platform.member.Member; import com.wcc.platform.domain.platform.member.MemberDto; import com.wcc.platform.domain.platform.member.ProfileStatus; import com.wcc.platform.domain.resource.MentorResource; @@ -85,51 +83,42 @@ public MentorDto( } /** - * Merge this DTO with an existing Mentor entity. + * Merges the current Mentor instance with the attributes of the provided Mentor instance. + * Combines properties from both instances into a new Mentor object, giving precedence + * to non-null values in the provided Mentor instance while retaining existing values + * where the provided values are null or empty. * - * @param member the existing mentor to merge with - * @return Updated mentor - * @throws InvalidMentorException if member is null - * @throws IllegalArgumentException if member is not a Mentor instance + * @param mentor the Mentor object containing updated attributes to merge with the current instance + * @return a new Mentor object created by merging attributes from the current instance and the provided instance */ - @Override - public Member merge(final Member member) { - if (member == null) { - throw new InvalidMentorException("Cannot merge with null mentor"); - } - if (!(member instanceof Mentor existingMentor)) { - throw new InvalidMentorException( - "Expected Mentor instance but got: " + member.getClass().getSimpleName()); - } + public Mentor merge(final Mentor mentor) { + var member = super.merge(mentor); final Mentor.MentorBuilder builder = Mentor.mentorBuilder() - .id(existingMentor.getId()) - .fullName(mergeString(this.getFullName(), existingMentor.getFullName())) - .position(mergeString(this.getPosition(), existingMentor.getPosition())) - .email(mergeString(this.getEmail(), existingMentor.getEmail())) - .slackDisplayName( - mergeString(this.getSlackDisplayName(), existingMentor.getSlackDisplayName())) - .country(mergeNullable(this.getCountry(), existingMentor.getCountry())) - .profileStatus(mergeNullable(this.profileStatus, existingMentor.getProfileStatus())) - .bio(mergeString(this.bio, existingMentor.getBio())) - .skills(mergeNullable(this.skills, existingMentor.getSkills())) - .menteeSection(mergeNullable(this.menteeSection, existingMentor.getMenteeSection())); - - mergeOptionalString(this.getCity(), existingMentor.getCity(), builder::city); - - mergeOptionalString( - this.getCompanyName(), existingMentor.getCompanyName(), builder::companyName); - - builder.network(mergeCollection(this.getNetwork(), existingMentor.getNetwork())); + .id(member.getId()) + .fullName(mergeString(this.getFullName(), member.getFullName())) + .position(mergeString(this.getPosition(), member.getPosition())) + .email(mergeString(this.getEmail(), member.getEmail())) + .slackDisplayName(mergeString(this.getSlackDisplayName(), member.getSlackDisplayName())) + .country(mergeNullable(this.getCountry(), member.getCountry())) + .profileStatus(mergeNullable(this.profileStatus, mentor.getProfileStatus())) + .bio(mergeString(this.bio, mentor.getBio())) + .skills(mergeNullable(this.skills, mentor.getSkills())) + .menteeSection(mergeNullable(this.menteeSection, mentor.getMenteeSection())); + + mergeOptionalString(this.getCity(), member.getCity(), builder::city); + + mergeOptionalString(this.getCompanyName(), member.getCompanyName(), builder::companyName); + + builder.network(mergeCollection(this.getNetwork(), member.getNetwork())); builder.spokenLanguages( - mergeCollection(this.getSpokenLanguages(), existingMentor.getSpokenLanguages())); - builder.images(mergeCollection(this.getImages(), existingMentor.getImages())); + mergeCollection(this.getSpokenLanguages(), mentor.getSpokenLanguages())); + builder.images(mergeCollection(this.getImages(), member.getImages())); - mergeOptional( - this.feedbackSection, existingMentor.getFeedbackSection(), builder::feedbackSection); + mergeOptional(this.feedbackSection, mentor.getFeedbackSection(), builder::feedbackSection); - mergeOptional(this.resources, existingMentor.getResources(), builder::resources); + mergeOptional(this.resources, mentor.getResources(), builder::resources); return builder.build(); } From fe89ec400a62340c98c015ba995bad3b9955cc6f Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 12:02:36 +0100 Subject: [PATCH 07/20] Change test naming according to pmdAll --- .../PostgresMenteeSectionRepositoryTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java b/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java index 996ff1a8..dbb85c4a 100644 --- a/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java +++ b/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java @@ -47,7 +47,7 @@ void setUp() { } @Test - void testUpdateMenteeSection_Success() { + void testUpdateMenteeSectionSuccess() { menteeSecRepo.updateMenteeSection(menteeSection, mentorId); @@ -72,7 +72,7 @@ void testUpdateMenteeSection_Success() { } @Test - void testUpdateMenteeSection_UpdatesTextFields() { + void testUpdateMenteeSectionUpdatesTextFields() { menteeSecRepo.updateMenteeSection(menteeSection, mentorId); verify(jdbc) @@ -84,7 +84,7 @@ void testUpdateMenteeSection_UpdatesTextFields() { } @Test - void testUpdateMenteeSection_DeletesOldMentorshipTypes() { + void testUpdateMenteeSectionDeletesOldMentorshipTypes() { menteeSecRepo.updateMenteeSection(menteeSection, mentorId); verify(jdbc) @@ -92,7 +92,7 @@ void testUpdateMenteeSection_DeletesOldMentorshipTypes() { } @Test - void testUpdateMenteeSection_InsertsAllMentorshipTypes() { + void testUpdateMenteeSectionInsertsAllMentorshipTypes() { menteeSecRepo.updateMenteeSection(menteeSection, mentorId); verify(jdbc) @@ -109,7 +109,7 @@ void testUpdateMenteeSection_InsertsAllMentorshipTypes() { } @Test - void testUpdateMenteeSection_UpdatesAllAvailability() { + void testUpdateMenteeSectionUpdatesAllAvailability() { menteeSecRepo.updateMenteeSection(menteeSection, mentorId); verify(jdbc) @@ -128,7 +128,7 @@ void testUpdateMenteeSection_UpdatesAllAvailability() { } @Test - void testUpdateMenteeSection_WithEmptyMentorshipTypes() { + void testUpdateMenteeSectionWithEmptyMentorshipTypes() { MenteeSection emptyTypesSection = new MenteeSection( List.of(), // Empty mentorship types @@ -145,7 +145,7 @@ void testUpdateMenteeSection_WithEmptyMentorshipTypes() { } @Test - void testUpdateMenteeSection_WithEmptyAvailability() { + void testUpdateMenteeSectionWithEmptyAvailability() { MenteeSection emptyAvailabilitySection = new MenteeSection( List.of(MentorshipType.AD_HOC), @@ -160,7 +160,7 @@ void testUpdateMenteeSection_WithEmptyAvailability() { } @Test - void testUpdateMenteeSection_VerifiesAllSqlStatements() { + void testUpdateMenteeSectionVerifiesAllSqlStatements() { menteeSecRepo.updateMenteeSection(menteeSection, mentorId); verify(jdbc).update(eq(UPDATE_MENTEE_SECTION), anyString(), anyString(), anyLong()); From 06a444b0d386b26428179f3c0d6028c7ceabf152 Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 15:12:24 +0100 Subject: [PATCH 08/20] Change spokenLanguages definition to avoid null instead of empty list --- .../com/wcc/platform/domain/platform/mentorship/Mentor.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java b/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java index e4a6a052..14b02915 100644 --- a/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java +++ b/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java @@ -20,7 +20,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; -import org.apache.commons.lang3.StringUtils; /** Represents the mentor members of the community. */ @Getter @@ -74,7 +73,7 @@ public Mentor( this.profileStatus = profileStatus; this.skills = skills; - this.spokenLanguages = spokenLanguages.stream().map(StringUtils::capitalize).toList(); + this.spokenLanguages = spokenLanguages != null ? spokenLanguages : List.of(); this.bio = bio; this.menteeSection = menteeSection; this.feedbackSection = feedbackSection; From a5ed5af55f6bdc672b7ed3629ae7c4252fe3b29a Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 15:12:59 +0100 Subject: [PATCH 09/20] Remove reference to Member merge, simplified checks --- .../domain/platform/mentorship/MentorDto.java | 83 +++++++------------ 1 file changed, 30 insertions(+), 53 deletions(-) diff --git a/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java b/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java index 89aed705..b2e63a17 100644 --- a/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java +++ b/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java @@ -4,6 +4,7 @@ import com.wcc.platform.domain.cms.attributes.Image; import com.wcc.platform.domain.cms.pages.mentorship.FeedbackSection; import com.wcc.platform.domain.cms.pages.mentorship.MenteeSection; +import com.wcc.platform.domain.exceptions.InvalidMentorException; import com.wcc.platform.domain.platform.SocialNetwork; import com.wcc.platform.domain.platform.member.MemberDto; import com.wcc.platform.domain.platform.member.ProfileStatus; @@ -19,7 +20,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; -import org.springframework.util.CollectionUtils; /** Represents the mentor members of the community. */ @Getter @@ -83,44 +83,36 @@ public MentorDto( } /** - * Merges the current Mentor instance with the attributes of the provided Mentor instance. - * Combines properties from both instances into a new Mentor object, giving precedence - * to non-null values in the provided Mentor instance while retaining existing values - * where the provided values are null or empty. + * Merges this DTO with an existing Mentor entity. Non-null/non-blank DTO values override existing + * values; otherwise existing values are retained. * - * @param mentor the Mentor object containing updated attributes to merge with the current instance - * @return a new Mentor object created by merging attributes from the current instance and the provided instance + * @param mentor the existing mentor to merge with + * @return merged Mentor with updated fields */ public Mentor merge(final Mentor mentor) { - var member = super.merge(mentor); - - final Mentor.MentorBuilder builder = - Mentor.mentorBuilder() - .id(member.getId()) - .fullName(mergeString(this.getFullName(), member.getFullName())) - .position(mergeString(this.getPosition(), member.getPosition())) - .email(mergeString(this.getEmail(), member.getEmail())) - .slackDisplayName(mergeString(this.getSlackDisplayName(), member.getSlackDisplayName())) - .country(mergeNullable(this.getCountry(), member.getCountry())) - .profileStatus(mergeNullable(this.profileStatus, mentor.getProfileStatus())) - .bio(mergeString(this.bio, mentor.getBio())) - .skills(mergeNullable(this.skills, mentor.getSkills())) - .menteeSection(mergeNullable(this.menteeSection, mentor.getMenteeSection())); - - mergeOptionalString(this.getCity(), member.getCity(), builder::city); - - mergeOptionalString(this.getCompanyName(), member.getCompanyName(), builder::companyName); - - builder.network(mergeCollection(this.getNetwork(), member.getNetwork())); - builder.spokenLanguages( - mergeCollection(this.getSpokenLanguages(), mentor.getSpokenLanguages())); - builder.images(mergeCollection(this.getImages(), member.getImages())); - - mergeOptional(this.feedbackSection, mentor.getFeedbackSection(), builder::feedbackSection); - - mergeOptional(this.resources, mentor.getResources(), builder::resources); + if (mentor == null) { + throw new InvalidMentorException("Cannot merge with null mentor"); + } - return builder.build(); + return Mentor.mentorBuilder() + .id(mentor.getId()) + .fullName(mergeString(this.getFullName(), mentor.getFullName())) + .position(mergeString(this.getPosition(), mentor.getPosition())) + .email(mergeString(this.getEmail(), mentor.getEmail())) + .slackDisplayName(mergeString(this.getSlackDisplayName(), mentor.getSlackDisplayName())) + .city(mergeString(this.getCity(), mentor.getCity())) + .companyName(mergeString(this.getCompanyName(), mentor.getCompanyName())) + .country(mergeNullable(this.getCountry(), mentor.getCountry())) + .profileStatus(mergeNullable(this.getProfileStatus(), mentor.getProfileStatus())) + .bio(mergeString(this.getBio(), mentor.getBio())) + .skills(mergeNullable(this.getSkills(), mentor.getSkills())) + .menteeSection(mergeNullable(this.getMenteeSection(), mentor.getMenteeSection())) + .feedbackSection(mergeNullable(this.getFeedbackSection(), mentor.getFeedbackSection())) + .resources(mergeNullable(this.getResources(), mentor.getResources())) + .network(mergeCollection(this.getNetwork(), mentor.getNetwork())) + .spokenLanguages(mergeCollection(this.getSpokenLanguages(), mentor.getSpokenLanguages())) + .images(mergeCollection(this.getImages(), mentor.getImages())) + .build(); } private String mergeString(final String candidate, final String existing) { @@ -132,24 +124,9 @@ private T mergeNullable(final T candidate, final T existing) { } private List mergeCollection(final List candidate, final List existing) { - return CollectionUtils.isEmpty(candidate) ? existing : candidate; - } - - private void mergeOptionalString( - final String candidate, - final String existing, - final java.util.function.Consumer setter) { - - if (StringUtils.isNotBlank(candidate) || StringUtils.isNotBlank(existing)) { - setter.accept(mergeString(candidate, existing)); - } - } - - private void mergeOptional( - final T candidate, final T existing, final java.util.function.Consumer setter) { - - if (candidate != null || existing != null) { - setter.accept(mergeNullable(candidate, existing)); + if (candidate != null && !candidate.isEmpty()) { + return candidate; } + return existing != null ? existing : List.of(); } } From 261507084ba916216e870e3863db794b5e5bfb8e Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 15:13:25 +0100 Subject: [PATCH 10/20] Add unit tests for MentorDto logic --- .../platform/mentorship/MentorDtoTest.java | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) diff --git a/src/test/java/com/wcc/platform/domain/platform/mentorship/MentorDtoTest.java b/src/test/java/com/wcc/platform/domain/platform/mentorship/MentorDtoTest.java index eb384b8a..f86bb7f9 100644 --- a/src/test/java/com/wcc/platform/domain/platform/mentorship/MentorDtoTest.java +++ b/src/test/java/com/wcc/platform/domain/platform/mentorship/MentorDtoTest.java @@ -1,13 +1,66 @@ package com.wcc.platform.domain.platform.mentorship; +import static com.wcc.platform.domain.platform.SocialNetworkType.GITHUB; +import static com.wcc.platform.domain.platform.SocialNetworkType.LINKEDIN; +import static com.wcc.platform.factories.SetupMentorFactories.createMentorDtoTest; +import static com.wcc.platform.factories.SetupMentorFactories.createMentorTest; import static org.junit.jupiter.api.Assertions.*; +import com.wcc.platform.domain.cms.attributes.Country; +import com.wcc.platform.domain.cms.attributes.Image; +import com.wcc.platform.domain.cms.attributes.ImageType; +import com.wcc.platform.domain.cms.attributes.Languages; +import com.wcc.platform.domain.cms.attributes.MentorshipFocusArea; +import com.wcc.platform.domain.cms.attributes.TechnicalArea; +import com.wcc.platform.domain.cms.pages.mentorship.MenteeSection; +import com.wcc.platform.domain.cms.pages.mentorship.MentorMonthAvailability; +import com.wcc.platform.domain.exceptions.InvalidMentorException; +import com.wcc.platform.domain.platform.SocialNetwork; import com.wcc.platform.domain.platform.member.ProfileStatus; +import com.wcc.platform.domain.platform.type.MemberType; +import java.time.Month; import java.util.List; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class MentorDtoTest { + private Mentor existingMentor; + private MentorDto mentorDto; + + @BeforeEach + void setUp() { + existingMentor = createMentorTest("Original Name"); + existingMentor = + Mentor.mentorBuilder() + .id(existingMentor.getId()) + .fullName("Original Name") + .position("Original Position") + .email("original@example.com") + .slackDisplayName("@original") + .country(new Country("US", "United States")) + .city("Original City") + .companyName("Original Company") + .images(List.of(new Image("profile.jpg", "Profile Image", ImageType.MOBILE))) + .network(List.of(new SocialNetwork(LINKEDIN, "https://linkedin.com/in/original"))) + .profileStatus(ProfileStatus.PENDING) + .spokenLanguages(List.of("English", "Spanish")) + .bio("Original bio") + .skills( + new Skills( + 10, + List.of(TechnicalArea.BACKEND, TechnicalArea.FRONTEND), + List.of(Languages.JAVA, Languages.PYTHON), + List.of(MentorshipFocusArea.CHANGE_SPECIALISATION))) + .menteeSection( + new MenteeSection( + List.of(MentorshipType.LONG_TERM), + List.of(new MentorMonthAvailability(Month.JANUARY, 2)), + "Original ideal mentee", + "Original additional info")) + .build(); + } + @Test void shouldReturnTrueForEqualMentorDtos() { MentorDto mentor1 = MentorDto.mentorDtoBuilder().id(1L).build(); @@ -42,4 +95,195 @@ void shouldReturnStringOfMentor() { assertEquals(expected, mentor.toString()); } + + @Test + void testMergeShouldUpdateAllFields() { + MentorDto template = createMentorDtoTest(null, MemberType.MENTOR); + + mentorDto = + MentorDto.mentorDtoBuilder() + .id(1L) + .fullName("Updated Name") + .position("Updated Position") + .email("updated@example.com") + .slackDisplayName("@updated") + .country(new Country("CA", "Canada")) + .city("Updated City") + .companyName("Updated Company") + .images(List.of(new Image("new.jpg", "New Image", ImageType.DESKTOP))) + .network(List.of(new SocialNetwork(GITHUB, "https://github.com/new"))) + .profileStatus(ProfileStatus.ACTIVE) + .spokenLanguages(List.of("French", "German")) + .bio("Updated bio") + .skills( + new Skills( + 5, + List.of(TechnicalArea.FULLSTACK), + List.of(Languages.JAVASCRIPT), + List.of(MentorshipFocusArea.CHANGE_SPECIALISATION))) + .menteeSection( + new MenteeSection( + List.of(MentorshipType.AD_HOC), + List.of(new MentorMonthAvailability(Month.JUNE, 3)), + "New ideal mentee", + "New additional info")) + .build(); + + Mentor result = mentorDto.merge(existingMentor); + + assertEquals(1L, result.getId()); + assertEquals("Updated Name", result.getFullName()); + assertEquals("Updated Position", result.getPosition()); + assertEquals("updated@example.com", result.getEmail()); + assertEquals("@updated", result.getSlackDisplayName()); + assertEquals("CA", result.getCountry().countryCode()); + assertEquals("Updated City", result.getCity()); + assertEquals("Updated Company", result.getCompanyName()); + assertEquals(ProfileStatus.ACTIVE, result.getProfileStatus()); + assertEquals("Updated bio", result.getBio()); + assertEquals(List.of("French", "German"), result.getSpokenLanguages()); + assertEquals(5, result.getSkills().yearsExperience()); + assertEquals("New ideal mentee", result.getMenteeSection().idealMentee()); + } + + @Test + void testMergeShouldRetainExistingValuesWhenDtoFieldsAreNull() { + mentorDto = + MentorDto.mentorDtoBuilder().fullName("Updated Name").email("updated@example.com").build(); + + Mentor result = mentorDto.merge(existingMentor); + + assertEquals("Updated Name", result.getFullName()); + assertEquals("updated@example.com", result.getEmail()); + + assertEquals("Original Position", result.getPosition()); + assertEquals("@original", result.getSlackDisplayName()); + assertEquals("US", result.getCountry().countryCode()); + assertEquals("Original City", result.getCity()); + assertEquals("Original Company", result.getCompanyName()); + assertEquals(ProfileStatus.PENDING, result.getProfileStatus()); + assertEquals("Original bio", result.getBio()); + assertEquals(List.of("English", "Spanish"), result.getSpokenLanguages()); + assertEquals(10, result.getSkills().yearsExperience()); + } + + @Test + void testMergeShouldRetainsExistingValuesWhenDtoStringsAreBlank() { + mentorDto = + MentorDto.mentorDtoBuilder() + .fullName("") + .position(" ") + .email("updated@example.com") + .bio(null) + .build(); + + Mentor result = mentorDto.merge(existingMentor); + + assertEquals("Original Name", result.getFullName()); + assertEquals("Original Position", result.getPosition()); + assertEquals("updated@example.com", result.getEmail()); + assertEquals("Original bio", result.getBio()); + } + + @Test + void testMergeShouldRetainExistingValuesWhenDtoCollectionsAreEmpty() { + mentorDto = + MentorDto.mentorDtoBuilder() + .fullName("Updated Name") + .images(List.of()) + .network(List.of()) + .spokenLanguages(List.of()) + .build(); + + Mentor result = mentorDto.merge(existingMentor); + + assertEquals("Updated Name", result.getFullName()); + assertEquals(1, result.getImages().size()); + assertEquals(1, result.getNetwork().size()); + assertEquals(LINKEDIN, result.getNetwork().getFirst().type()); + assertEquals(2, result.getSpokenLanguages().size()); + } + + @Test + void testMergeShouldSetOptionalFieldsOnlyWhenAtLeastOneValueExists() { + Mentor mentorWithNullOptionals = + Mentor.mentorBuilder() + .id(1L) + .fullName("Name") + .position("Position") + .email("email@test.com") + .slackDisplayName("@slack") + .country(new Country("US", "United States")) + .images(List.of(new Image("img.jpg", "Image", ImageType.DESKTOP))) + .profileStatus(ProfileStatus.ACTIVE) + .bio("Bio") + .skills(new Skills(5, List.of(), List.of(), List.of())) + .menteeSection(new MenteeSection(List.of(), List.of(), "ideal", "additional")) + .spokenLanguages(null) + .city(null) + .companyName(null) + .network(List.of()) + .build(); + + mentorDto = MentorDto.mentorDtoBuilder().fullName("Updated Name").build(); + + Mentor result = mentorDto.merge(mentorWithNullOptionals); + + assertNull(result.getCity()); + assertNull(result.getCompanyName()); + assertNotNull(result.getSpokenLanguages()); + assertTrue(result.getSpokenLanguages().isEmpty()); + } + + @Test + void testMergeShouldThrowExceptionWhenMemberIsNull() { + mentorDto = MentorDto.mentorDtoBuilder().fullName("Name").build(); + + InvalidMentorException exception = + assertThrows(InvalidMentorException.class, () -> mentorDto.merge(null)); + + assertEquals("Cannot merge with null mentor", exception.getMessage()); + } + + @Test + void testMergeShouldUpdateSkillsCorrectly() { + Skills newSkills = + new Skills( + 15, + List.of(TechnicalArea.DISTRIBUTED_SYSTEMS), + List.of(Languages.KOTLIN), + List.of(MentorshipFocusArea.GROW_BEYOND_SENIOR)); + + mentorDto = MentorDto.mentorDtoBuilder().skills(newSkills).build(); + + Mentor result = mentorDto.merge(existingMentor); + + assertEquals(15, result.getSkills().yearsExperience()); + assertEquals(1, result.getSkills().areas().size()); + assertEquals(TechnicalArea.DISTRIBUTED_SYSTEMS, result.getSkills().areas().getFirst()); + assertEquals(1, result.getSkills().languages().size()); + assertEquals(Languages.KOTLIN, result.getSkills().languages().getFirst()); + } + + @Test + void testMergeShouldUpdateMenteeSectionCorrectly() { + MenteeSection newMenteeSection = + new MenteeSection( + List.of(MentorshipType.AD_HOC, MentorshipType.LONG_TERM), + List.of( + new MentorMonthAvailability(Month.MARCH, 4), + new MentorMonthAvailability(Month.APRIL, 5)), + "Updated ideal mentee", + "Updated additional"); + + mentorDto = MentorDto.mentorDtoBuilder().menteeSection(newMenteeSection).build(); + + Mentor result = mentorDto.merge(existingMentor); + + assertEquals("Updated ideal mentee", result.getMenteeSection().idealMentee()); + assertEquals("Updated additional", result.getMenteeSection().additional()); + assertEquals(2, result.getMenteeSection().mentorshipType().size()); + assertEquals(2, result.getMenteeSection().availability().size()); + assertEquals(Month.MARCH, result.getMenteeSection().availability().getFirst().month()); + } } From 751824cc66b583c45ddb5e16bf40c072dd8606dc Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 15:21:22 +0100 Subject: [PATCH 11/20] Remove unnecessary Mentor casting --- src/main/java/com/wcc/platform/service/MentorshipService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/wcc/platform/service/MentorshipService.java b/src/main/java/com/wcc/platform/service/MentorshipService.java index c2f5cd4a..86b05529 100644 --- a/src/main/java/com/wcc/platform/service/MentorshipService.java +++ b/src/main/java/com/wcc/platform/service/MentorshipService.java @@ -193,7 +193,7 @@ public Member updateMentor(final Long mentorId, final MentorDto mentorDto) { final Optional mentorOptional = mentorRepository.findById(mentorId); final var mentor = mentorOptional.orElseThrow(() -> new MemberNotFoundException(mentorId)); - final Mentor updatedMentor = (Mentor) mentorDto.merge(mentor); + final Mentor updatedMentor = mentorDto.merge(mentor); return mentorRepository.update(mentorId, updatedMentor); } } From 0be3a16066ebaa5caa06f58dfb42a3c3dffa5f13 Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 18:25:15 +0100 Subject: [PATCH 12/20] Remove unused variable --- .../postgres/PostgresMenteeSectionRepository.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepository.java b/src/main/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepository.java index 8f200146..9b2e95e5 100644 --- a/src/main/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepository.java +++ b/src/main/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepository.java @@ -26,10 +26,12 @@ public class PostgresMenteeSectionRepository implements MenteeSectionRepository public static final String UPDATE_MENTEE_SECTION = "UPDATE mentor_mentee_section SET ideal_mentee = ?, additional = ? WHERE mentor_id = ?"; - public static final String UPDATE_MENTOR_TYPE = - "UPDATE mentor_mentorship_types SET mentorship_type = ? WHERE mentor_id = ?"; - private static final String UPDATE_AVAILABILITY = + public static final String UPDATE_AVAILABILITY = "UPDATE mentor_availability SET " + "month_num = ?, " + "hours = ? " + "WHERE mentor_id = ?"; + public static final String INSERT_MENTOR_TYPES = + "INSERT INTO mentor_mentorship_types (mentor_id, mentorship_type) VALUES (?, ?)"; + public static final String DELETE_MENTOR_TYPES = + "DELETE FROM mentor_mentorship_types WHERE mentor_id = ?"; private static final String SQL_BASE = "SELECT ideal_mentee, additional, created_at, updated_at " + "FROM mentor_mentee_section WHERE mentor_id = ?"; @@ -41,10 +43,6 @@ public class PostgresMenteeSectionRepository implements MenteeSectionRepository "INSERT INTO mentor_mentee_section (mentor_id, ideal_mentee, additional) VALUES (?, ?, ?)"; private static final String INSERT_AVAILABILITY = "INSERT INTO mentor_availability (mentor_id, month_num, hours) VALUES (?, ?, ?)"; - private static final String INSERT_MENTOR_TYPES = - "INSERT INTO mentor_mentorship_types (mentor_id, mentorship_type) VALUES (?, ?)"; - private static final String DELETE_MENTOR_TYPES = - "DELETE FROM mentor_mentorship_types WHERE mentor_id = ?"; private final JdbcTemplate jdbc; /** Inserts the mentee section details for the mentor. */ From 571fc8e0ae0e8e3c008c107e563d24d974ee73f4 Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 18:25:47 +0100 Subject: [PATCH 13/20] Change SQL statements into variables --- .../PostgresMenteeSectionRepositoryTest.java | 44 ++++++------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java b/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java index dbb85c4a..752e137c 100644 --- a/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java +++ b/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java @@ -58,14 +58,13 @@ void testUpdateMenteeSectionSuccess() { eq("Additional info UPDATED"), eq(mentorId)); - verify(jdbc, times(1)).update(contains("DELETE FROM mentor_mentorship_types"), eq(mentorId)); + verify(jdbc, times(1)).update(eq(DELETE_MENTOR_TYPES), eq(mentorId)); - verify(jdbc, times(2)) - .update(contains("INSERT INTO mentor_mentorship_types"), eq(mentorId), anyInt()); + verify(jdbc, times(2)).update(eq(INSERT_MENTOR_TYPES), eq(mentorId), anyInt()); verify(jdbc, times(2)) .update( - contains("UPDATE mentor_availability"), + eq(UPDATE_AVAILABILITY), anyInt(), // month_num anyInt(), // hours eq(mentorId)); @@ -87,8 +86,7 @@ void testUpdateMenteeSectionUpdatesTextFields() { void testUpdateMenteeSectionDeletesOldMentorshipTypes() { menteeSecRepo.updateMenteeSection(menteeSection, mentorId); - verify(jdbc) - .update(contains("DELETE FROM mentor_mentorship_types WHERE mentor_id = ?"), eq(mentorId)); + verify(jdbc).update(eq(DELETE_MENTOR_TYPES), eq(mentorId)); } @Test @@ -97,15 +95,13 @@ void testUpdateMenteeSectionInsertsAllMentorshipTypes() { verify(jdbc) .update( - contains("INSERT INTO mentor_mentorship_types"), + eq(INSERT_MENTOR_TYPES), eq(mentorId), eq(MentorshipType.LONG_TERM.getMentorshipTypeId())); verify(jdbc) .update( - contains("INSERT INTO mentor_mentorship_types"), - eq(mentorId), - eq(MentorshipType.AD_HOC.getMentorshipTypeId())); + eq(INSERT_MENTOR_TYPES), eq(mentorId), eq(MentorshipType.AD_HOC.getMentorshipTypeId())); } @Test @@ -113,18 +109,10 @@ void testUpdateMenteeSectionUpdatesAllAvailability() { menteeSecRepo.updateMenteeSection(menteeSection, mentorId); verify(jdbc) - .update( - contains("UPDATE mentor_availability"), - eq(Month.JANUARY.getValue()), - eq(2), - eq(mentorId)); + .update(contains(UPDATE_AVAILABILITY), eq(Month.JANUARY.getValue()), eq(2), eq(mentorId)); verify(jdbc) - .update( - contains("UPDATE mentor_availability"), - eq(Month.FEBRUARY.getValue()), - eq(3), - eq(mentorId)); + .update(contains(UPDATE_AVAILABILITY), eq(Month.FEBRUARY.getValue()), eq(3), eq(mentorId)); } @Test @@ -138,10 +126,9 @@ void testUpdateMenteeSectionWithEmptyMentorshipTypes() { menteeSecRepo.updateMenteeSection(emptyTypesSection, mentorId); - verify(jdbc, times(1)).update(contains("DELETE FROM mentor_mentorship_types"), eq(mentorId)); + verify(jdbc, times(1)).update(eq(DELETE_MENTOR_TYPES), eq(mentorId)); - verify(jdbc, never()) - .update(contains("INSERT INTO mentor_mentorship_types"), anyLong(), anyInt()); + verify(jdbc, never()).update(eq(INSERT_MENTOR_TYPES), anyLong(), anyInt()); } @Test @@ -155,8 +142,7 @@ void testUpdateMenteeSectionWithEmptyAvailability() { menteeSecRepo.updateMenteeSection(emptyAvailabilitySection, mentorId); - verify(jdbc, never()) - .update(contains("UPDATE mentor_availability"), anyInt(), anyInt(), anyLong()); + verify(jdbc, never()).update(contains(UPDATE_AVAILABILITY), anyInt(), anyInt(), anyLong()); } @Test @@ -164,10 +150,8 @@ void testUpdateMenteeSectionVerifiesAllSqlStatements() { menteeSecRepo.updateMenteeSection(menteeSection, mentorId); verify(jdbc).update(eq(UPDATE_MENTEE_SECTION), anyString(), anyString(), anyLong()); - verify(jdbc).update(contains("DELETE FROM mentor_mentorship_types"), anyLong()); - verify(jdbc, atLeastOnce()) - .update(contains("INSERT INTO mentor_mentorship_types"), anyLong(), anyInt()); - verify(jdbc, atLeastOnce()) - .update(contains("UPDATE mentor_availability"), anyInt(), anyInt(), anyLong()); + verify(jdbc).update(eq(DELETE_MENTOR_TYPES), anyLong()); + verify(jdbc, atLeastOnce()).update(eq(INSERT_MENTOR_TYPES), anyLong(), anyInt()); + verify(jdbc, atLeastOnce()).update(eq(UPDATE_AVAILABILITY), anyInt(), anyInt(), anyLong()); } } From bc69661c35ebf29cfa89cc843793693bf845c3f2 Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 18:26:16 +0100 Subject: [PATCH 14/20] Add test for checking InvalidMentorException --- .../domain/platform/mentorship/MentorDtoTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/com/wcc/platform/domain/platform/mentorship/MentorDtoTest.java b/src/test/java/com/wcc/platform/domain/platform/mentorship/MentorDtoTest.java index f86bb7f9..02e18c28 100644 --- a/src/test/java/com/wcc/platform/domain/platform/mentorship/MentorDtoTest.java +++ b/src/test/java/com/wcc/platform/domain/platform/mentorship/MentorDtoTest.java @@ -286,4 +286,14 @@ void testMergeShouldUpdateMenteeSectionCorrectly() { assertEquals(2, result.getMenteeSection().availability().size()); assertEquals(Month.MARCH, result.getMenteeSection().availability().getFirst().month()); } + + @Test + void testMergeShouldThrowExceptionIfMentorIsNull() { + mentorDto = MentorDto.mentorDtoBuilder().build(); + + InvalidMentorException exception = + assertThrows(InvalidMentorException.class, () -> mentorDto.merge(null)); + + assertEquals("Cannot merge with null mentor", exception.getMessage()); + } } From 5ba52960e3c558d71a72964ee69b086e6dfb2d2e Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 19:20:28 +0100 Subject: [PATCH 15/20] Add normalizer for capitalization in Builder --- .../domain/platform/mentorship/Mentor.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java b/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java index 14b02915..0b95e1eb 100644 --- a/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java +++ b/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java @@ -20,6 +20,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; +import org.apache.commons.lang3.StringUtils; /** Represents the mentor members of the community. */ @Getter @@ -73,13 +74,22 @@ public Mentor( this.profileStatus = profileStatus; this.skills = skills; - this.spokenLanguages = spokenLanguages != null ? spokenLanguages : List.of(); + this.spokenLanguages = normalizeLanguages(spokenLanguages); this.bio = bio; this.menteeSection = menteeSection; this.feedbackSection = feedbackSection; this.resources = resources; } + /** Checks for empty or null and returns capitalized list of string. */ + private static List normalizeLanguages(final List languages) { + if (languages == null || languages.isEmpty()) { + return List.of(); + } + + return languages.stream().filter(StringUtils::isNotBlank).map(StringUtils::capitalize).toList(); + } + /** * Converts this Mentor entity and an optional active MentorshipCycle into a MentorDto * representation. @@ -136,4 +146,12 @@ private MentorDtoBuilder buildFromMentor(final Mentor mentor) { .feedbackSection(mentor.getFeedbackSection()) .resources(mentor.getResources()); } + + /** Lombok builder hook to enforce normalization. */ + public static class MentorBuilder { + public MentorBuilder spokenLanguages(final List spokenLanguages) { + this.spokenLanguages = normalizeLanguages(spokenLanguages); + return this; + } + } } From 883b18d24a3ad85c9a2c516bed6ff9e820c144e1 Mon Sep 17 00:00:00 2001 From: nycotin Date: Sun, 18 Jan 2026 19:21:39 +0100 Subject: [PATCH 16/20] Add test for checking capitalization of languages, adjusted spokenLanguages input in MentorTest for test --- .../com/wcc/platform/domain/platform/MentorTest.java | 10 +++++++++- .../wcc/platform/factories/SetupMentorFactories.java | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/wcc/platform/domain/platform/MentorTest.java b/src/test/java/com/wcc/platform/domain/platform/MentorTest.java index f6e05ebf..5b68af0f 100644 --- a/src/test/java/com/wcc/platform/domain/platform/MentorTest.java +++ b/src/test/java/com/wcc/platform/domain/platform/MentorTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import com.wcc.platform.domain.platform.mentorship.Mentor; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -43,11 +44,18 @@ void testToString() { final var expected = "Mentor(profileStatus=ACTIVE, skills=Skills[yearsExperience=2, areas=[Backend, Frontend], " + "languages=[Javascript], mentorshipFocus=[Grow from beginner to mid-level]], " - + "spokenLanguages=[English], bio=Mentor bio, " + + "spokenLanguages=[English, Spanish, German], bio=Mentor bio, " + "menteeSection=MenteeSection[mentorshipType=[Long-Term], " + "availability=[MentorMonthAvailability[month=APRIL, hours=2]], " + "idealMentee=ideal mentee description, " + "additional=additional], feedbackSection=null, resources=null)"; assertEquals(expected, mentor.toString()); } + + @Test + void testSpokenLanguagesAreCapitalized() { + var mentor = createMentorTest(1L, "test name", "test@email.com"); + + assertEquals(List.of("English", "Spanish", "German"), mentor.getSpokenLanguages()); + } } diff --git a/src/test/java/com/wcc/platform/factories/SetupMentorFactories.java b/src/test/java/com/wcc/platform/factories/SetupMentorFactories.java index 3577f500..1c9e5f93 100644 --- a/src/test/java/com/wcc/platform/factories/SetupMentorFactories.java +++ b/src/test/java/com/wcc/platform/factories/SetupMentorFactories.java @@ -52,7 +52,7 @@ public static Mentor createMentorTest( .images(member.getImages()) .profileStatus(ProfileStatus.ACTIVE) .bio("Mentor bio") - .spokenLanguages(List.of("English")) + .spokenLanguages(List.of("english", "spanish", "german")) .skills( new Skills( 2, From 8cc43ed7f5d1f9e5d81897cbdd1d1c00cf0dc86e Mon Sep 17 00:00:00 2001 From: Nicol Tincani <110698252+nycotin@users.noreply.github.com> Date: Wed, 21 Jan 2026 20:42:09 +0100 Subject: [PATCH 17/20] Update src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java Co-authored-by: Adriana Zencke Zimmermann --- .../com/wcc/platform/domain/platform/mentorship/Mentor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java b/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java index 0b95e1eb..3d891067 100644 --- a/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java +++ b/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java @@ -83,7 +83,7 @@ public Mentor( /** Checks for empty or null and returns capitalized list of string. */ private static List normalizeLanguages(final List languages) { - if (languages == null || languages.isEmpty()) { + if (CollectionUtils.isEmpty(languages)) { return List.of(); } From 3fab175f4d450bb4b932f9e0eb6e41eab082ac59 Mon Sep 17 00:00:00 2001 From: Nicol Tincani <110698252+nycotin@users.noreply.github.com> Date: Wed, 21 Jan 2026 20:42:22 +0100 Subject: [PATCH 18/20] Update src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java Co-authored-by: Adriana Zencke Zimmermann --- .../com/wcc/platform/domain/platform/mentorship/MentorDto.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java b/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java index b2e63a17..3de5acb4 100644 --- a/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java +++ b/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java @@ -124,7 +124,7 @@ private T mergeNullable(final T candidate, final T existing) { } private List mergeCollection(final List candidate, final List existing) { - if (candidate != null && !candidate.isEmpty()) { + if (!CollectionUtils.isEmpty(candidate)) { return candidate; } return existing != null ? existing : List.of(); From 09ebeb852a3c8791a378383a8c83f01190bbaacf Mon Sep 17 00:00:00 2001 From: nycotin Date: Wed, 21 Jan 2026 20:51:18 +0100 Subject: [PATCH 19/20] Add missing imports --- .../java/com/wcc/platform/domain/platform/mentorship/Mentor.java | 1 + .../com/wcc/platform/domain/platform/mentorship/MentorDto.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java b/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java index 3d891067..291067a2 100644 --- a/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java +++ b/src/main/java/com/wcc/platform/domain/platform/mentorship/Mentor.java @@ -21,6 +21,7 @@ import lombok.NoArgsConstructor; import lombok.ToString; import org.apache.commons.lang3.StringUtils; +import org.springframework.util.CollectionUtils; /** Represents the mentor members of the community. */ @Getter diff --git a/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java b/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java index 3de5acb4..953d5f0b 100644 --- a/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java +++ b/src/main/java/com/wcc/platform/domain/platform/mentorship/MentorDto.java @@ -20,6 +20,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; +import org.springframework.util.CollectionUtils; /** Represents the mentor members of the community. */ @Getter From 8b99f892af8816bd5c3207ef00d906eeeb1c9f1a Mon Sep 17 00:00:00 2001 From: nycotin Date: Mon, 26 Jan 2026 08:04:51 +0100 Subject: [PATCH 20/20] Add missing import --- .../postgres/PostgresMenteeSectionRepositoryTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java b/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java index 752e137c..fe4ad09d 100644 --- a/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java +++ b/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java @@ -1,12 +1,13 @@ package com.wcc.platform.repository.postgres; -import static com.wcc.platform.repository.postgres.PostgresMenteeSectionRepository.*; +import static com.wcc.platform.repository.postgres.mentorship.PostgresMenteeSectionRepository.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import com.wcc.platform.domain.cms.pages.mentorship.MenteeSection; import com.wcc.platform.domain.cms.pages.mentorship.MentorMonthAvailability; import com.wcc.platform.domain.platform.mentorship.MentorshipType; +import com.wcc.platform.repository.postgres.mentorship.PostgresMenteeSectionRepository; import java.time.Month; import java.util.List; import org.junit.jupiter.api.BeforeEach;