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 c048362e..cf7442a3 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.NoArgsConstructor; import lombok.ToString; import org.apache.commons.lang3.StringUtils; +import org.springframework.util.CollectionUtils; /** Represents the mentor members of the community. */ @Getter @@ -73,13 +74,22 @@ public Mentor( this.profileStatus = profileStatus; this.skills = skills; - this.spokenLanguages = spokenLanguages.stream().map(StringUtils::capitalize).toList(); + 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 (CollectionUtils.isEmpty(languages)) { + 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; + } + } } 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 2c437b89..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 @@ -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; @@ -83,46 +84,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) { - final 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) { @@ -134,24 +125,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 (!CollectionUtils.isEmpty(candidate)) { + return candidate; } + return existing != null ? existing : List.of(); } } diff --git a/src/main/java/com/wcc/platform/repository/postgres/mentorship/PostgresMenteeSectionRepository.java b/src/main/java/com/wcc/platform/repository/postgres/mentorship/PostgresMenteeSectionRepository.java index 90fadc63..a55a45b9 100644 --- a/src/main/java/com/wcc/platform/repository/postgres/mentorship/PostgresMenteeSectionRepository.java +++ b/src/main/java/com/wcc/platform/repository/postgres/mentorship/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 = - "UPDATE mentor_availability SET month_num = ?, hours = ? WHERE mentor_id = ?"; + 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,8 +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 final JdbcTemplate jdbc; /** Inserts the mentee section details for the mentor. */ @@ -58,7 +58,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); } @@ -116,7 +117,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/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/domain/platform/mentorship/MentorDtoTest.java b/src/test/java/com/wcc/platform/domain/platform/mentorship/MentorDtoTest.java index eb384b8a..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 @@ -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,205 @@ 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()); + } + + @Test + void testMergeShouldThrowExceptionIfMentorIsNull() { + mentorDto = MentorDto.mentorDtoBuilder().build(); + + InvalidMentorException exception = + assertThrows(InvalidMentorException.class, () -> mentorDto.merge(null)); + + assertEquals("Cannot merge with null mentor", exception.getMessage()); + } } diff --git a/src/test/java/com/wcc/platform/factories/SetupMentorFactories.java b/src/test/java/com/wcc/platform/factories/SetupMentorFactories.java index 22d4b5af..20219e28 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, 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..fe4ad09d --- /dev/null +++ b/src/test/java/com/wcc/platform/repository/postgres/PostgresMenteeSectionRepositoryTest.java @@ -0,0 +1,158 @@ +package com.wcc.platform.repository.postgres; + +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; +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 testUpdateMenteeSectionSuccess() { + + 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(eq(DELETE_MENTOR_TYPES), eq(mentorId)); + + verify(jdbc, times(2)).update(eq(INSERT_MENTOR_TYPES), eq(mentorId), anyInt()); + + verify(jdbc, times(2)) + .update( + eq(UPDATE_AVAILABILITY), + anyInt(), // month_num + anyInt(), // hours + eq(mentorId)); + } + + @Test + void testUpdateMenteeSectionUpdatesTextFields() { + menteeSecRepo.updateMenteeSection(menteeSection, mentorId); + + verify(jdbc) + .update( + eq(UPDATE_MENTEE_SECTION), + eq("Ideal mentee description UPDATED"), + eq("Additional info UPDATED"), + eq(mentorId)); + } + + @Test + void testUpdateMenteeSectionDeletesOldMentorshipTypes() { + menteeSecRepo.updateMenteeSection(menteeSection, mentorId); + + verify(jdbc).update(eq(DELETE_MENTOR_TYPES), eq(mentorId)); + } + + @Test + void testUpdateMenteeSectionInsertsAllMentorshipTypes() { + menteeSecRepo.updateMenteeSection(menteeSection, mentorId); + + verify(jdbc) + .update( + eq(INSERT_MENTOR_TYPES), + eq(mentorId), + eq(MentorshipType.LONG_TERM.getMentorshipTypeId())); + + verify(jdbc) + .update( + eq(INSERT_MENTOR_TYPES), eq(mentorId), eq(MentorshipType.AD_HOC.getMentorshipTypeId())); + } + + @Test + void testUpdateMenteeSectionUpdatesAllAvailability() { + menteeSecRepo.updateMenteeSection(menteeSection, mentorId); + + verify(jdbc) + .update(contains(UPDATE_AVAILABILITY), eq(Month.JANUARY.getValue()), eq(2), eq(mentorId)); + + verify(jdbc) + .update(contains(UPDATE_AVAILABILITY), eq(Month.FEBRUARY.getValue()), eq(3), eq(mentorId)); + } + + @Test + void testUpdateMenteeSectionWithEmptyMentorshipTypes() { + 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(eq(DELETE_MENTOR_TYPES), eq(mentorId)); + + verify(jdbc, never()).update(eq(INSERT_MENTOR_TYPES), anyLong(), anyInt()); + } + + @Test + void testUpdateMenteeSectionWithEmptyAvailability() { + 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_AVAILABILITY), anyInt(), anyInt(), anyLong()); + } + + @Test + void testUpdateMenteeSectionVerifiesAllSqlStatements() { + menteeSecRepo.updateMenteeSection(menteeSection, mentorId); + + verify(jdbc).update(eq(UPDATE_MENTEE_SECTION), anyString(), anyString(), 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()); + } +}