diff --git a/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java b/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java index fbd110f9d..f3de315b1 100644 --- a/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java +++ b/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java @@ -119,6 +119,11 @@ public enum ErrorCode { MENTORING_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "해당 멘토링 신청을 찾을 수 없습니다."), UNAUTHORIZED_MENTORING(HttpStatus.FORBIDDEN.value(), "멘토링 권한이 없습니다."), MENTORING_ALREADY_CONFIRMED(HttpStatus.BAD_REQUEST.value(), "이미 승인 또는 거절된 멘토링입니다."), + MENTOR_APPLICATION_ALREADY_EXISTED(HttpStatus.CONFLICT.value(),"멘토 승격 요청이 이미 존재합니다."), + INVALID_EXCHANGE_STATUS_FOR_MENTOR(HttpStatus.BAD_REQUEST.value(), "멘토 승격 지원 가능한 교환학생 상태가 아닙니다."), + UNIVERSITY_ID_REQUIRED_FOR_CATALOG(HttpStatus.BAD_REQUEST.value(), "목록에서 학교를 선택한 경우 학교 정보가 필요합니다."), + UNIVERSITY_ID_MUST_BE_NULL_FOR_OTHER(HttpStatus.BAD_REQUEST.value(), "기타 학교를 선택한 경우 학교 정보를 입력할 수 없습니다."), + INVALID_UNIVERSITY_SELECT_TYPE(HttpStatus.BAD_REQUEST.value(), "지원하지 않는 학교 선택 방식입니다."), // socket UNAUTHORIZED_SUBSCRIBE(HttpStatus.FORBIDDEN.value(), "구독 권한이 없습니다."), diff --git a/src/main/java/com/example/solidconnection/mentor/controller/MentorApplicationController.java b/src/main/java/com/example/solidconnection/mentor/controller/MentorApplicationController.java new file mode 100644 index 000000000..4f85e5621 --- /dev/null +++ b/src/main/java/com/example/solidconnection/mentor/controller/MentorApplicationController.java @@ -0,0 +1,32 @@ +package com.example.solidconnection.mentor.controller; + +import com.example.solidconnection.common.resolver.AuthorizedUser; +import com.example.solidconnection.mentor.dto.MentorApplicationRequest; +import com.example.solidconnection.mentor.service.MentorApplicationService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RequiredArgsConstructor +@RequestMapping("/mentees") +@RestController +public class MentorApplicationController { + + private final MentorApplicationService mentorApplicationService; + + @PostMapping("/mentor-applications") + public ResponseEntity requestMentorApplication( + @AuthorizedUser long siteUserId, + @Valid @RequestPart("mentorApplicationRequest") MentorApplicationRequest mentorApplicationRequest, + @RequestParam("file") MultipartFile file + ) { + mentorApplicationService.submitMentorApplication(siteUserId, mentorApplicationRequest, file); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java b/src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java new file mode 100644 index 000000000..8f800dcff --- /dev/null +++ b/src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java @@ -0,0 +1,109 @@ +package com.example.solidconnection.mentor.domain; + +import com.example.solidconnection.common.BaseEntity; +import com.example.solidconnection.common.exception.CustomException; +import com.example.solidconnection.common.exception.ErrorCode; +import com.example.solidconnection.siteuser.domain.ExchangeStatus; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Check; + +@Entity +@Getter +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Check( + name = "chk_ma_university_select_rule", + constraints = """ + (university_select_type = 'CATALOG' AND university_id IS NOT NULL) OR + (university_select_type = 'OTHER' AND university_id IS NULL) + """ +) +public class MentorApplication extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private long siteUserId; + + @Column(nullable = false, name = "country_code") + private String countryCode; + + @Column + private Long universityId; + + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private UniversitySelectType universitySelectType; + + @Column(nullable = false, name = "mentor_proof_url", length = 500) + private String mentorProofUrl; + + private String rejectedReason; + + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private ExchangeStatus exchangeStatus; + + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private MentorApplicationStatus mentorApplicationStatus = MentorApplicationStatus.PENDING; + + private static final Set ALLOWED = + Collections.unmodifiableSet(EnumSet.of(ExchangeStatus.STUDYING_ABROAD, ExchangeStatus.AFTER_EXCHANGE)); + + public MentorApplication( + long siteUserId, + String countryCode, + Long universityId, + UniversitySelectType universitySelectType, + String mentorProofUrl, + ExchangeStatus exchangeStatus + ) { + validateExchangeStatus(exchangeStatus); + validateUniversitySelection(universitySelectType, universityId); + + this.siteUserId = siteUserId; + this.countryCode = countryCode; + this.universityId = universityId; + this.universitySelectType = universitySelectType; + this.mentorProofUrl = mentorProofUrl; + this.exchangeStatus = exchangeStatus; + } + + private void validateUniversitySelection(UniversitySelectType universitySelectType, Long universityId) { + switch (universitySelectType) { + case CATALOG -> { + if(universityId == null) { + throw new CustomException(ErrorCode.UNIVERSITY_ID_REQUIRED_FOR_CATALOG); + } + } + case OTHER -> { + if(universityId != null) { + throw new CustomException(ErrorCode.UNIVERSITY_ID_MUST_BE_NULL_FOR_OTHER); + } + } + default -> throw new CustomException(ErrorCode.INVALID_UNIVERSITY_SELECT_TYPE); + } + } + + private void validateExchangeStatus(ExchangeStatus exchangeStatus) { + if(!ALLOWED.contains(exchangeStatus)) { + throw new CustomException(ErrorCode.INVALID_EXCHANGE_STATUS_FOR_MENTOR); + } + } +} diff --git a/src/main/java/com/example/solidconnection/mentor/domain/MentorApplicationStatus.java b/src/main/java/com/example/solidconnection/mentor/domain/MentorApplicationStatus.java new file mode 100644 index 000000000..e562e02cf --- /dev/null +++ b/src/main/java/com/example/solidconnection/mentor/domain/MentorApplicationStatus.java @@ -0,0 +1,9 @@ +package com.example.solidconnection.mentor.domain; + +public enum MentorApplicationStatus { + + PENDING, + APPROVED, + REJECTED, + ; +} diff --git a/src/main/java/com/example/solidconnection/mentor/domain/UniversitySelectType.java b/src/main/java/com/example/solidconnection/mentor/domain/UniversitySelectType.java new file mode 100644 index 000000000..92c3622a1 --- /dev/null +++ b/src/main/java/com/example/solidconnection/mentor/domain/UniversitySelectType.java @@ -0,0 +1,7 @@ +package com.example.solidconnection.mentor.domain; + +public enum UniversitySelectType { + + CATALOG, + OTHER +} diff --git a/src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java b/src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java new file mode 100644 index 000000000..da6d206ab --- /dev/null +++ b/src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java @@ -0,0 +1,14 @@ +package com.example.solidconnection.mentor.dto; + +import com.example.solidconnection.mentor.domain.UniversitySelectType; +import com.example.solidconnection.siteuser.domain.ExchangeStatus; +import com.fasterxml.jackson.annotation.JsonProperty; + +public record MentorApplicationRequest( + @JsonProperty("preparationStatus") + ExchangeStatus exchangeStatus, + UniversitySelectType universitySelectType, + String country, + Long universityId +) { +} diff --git a/src/main/java/com/example/solidconnection/mentor/repository/MentorApplicationRepository.java b/src/main/java/com/example/solidconnection/mentor/repository/MentorApplicationRepository.java new file mode 100644 index 000000000..d03b53892 --- /dev/null +++ b/src/main/java/com/example/solidconnection/mentor/repository/MentorApplicationRepository.java @@ -0,0 +1,11 @@ +package com.example.solidconnection.mentor.repository; + +import com.example.solidconnection.mentor.domain.MentorApplication; +import com.example.solidconnection.mentor.domain.MentorApplicationStatus; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MentorApplicationRepository extends JpaRepository { + + boolean existsBySiteUserIdAndMentorApplicationStatusIn(long siteUserId, List mentorApplicationStatuses); +} diff --git a/src/main/java/com/example/solidconnection/mentor/service/MentorApplicationService.java b/src/main/java/com/example/solidconnection/mentor/service/MentorApplicationService.java new file mode 100644 index 000000000..d0716a156 --- /dev/null +++ b/src/main/java/com/example/solidconnection/mentor/service/MentorApplicationService.java @@ -0,0 +1,58 @@ +package com.example.solidconnection.mentor.service; + +import com.example.solidconnection.common.exception.CustomException; +import com.example.solidconnection.mentor.domain.MentorApplication; +import com.example.solidconnection.mentor.domain.MentorApplicationStatus; +import com.example.solidconnection.mentor.dto.MentorApplicationRequest; +import com.example.solidconnection.mentor.repository.MentorApplicationRepository; +import com.example.solidconnection.s3.domain.ImgType; +import com.example.solidconnection.s3.dto.UploadedFileUrlResponse; +import com.example.solidconnection.s3.service.S3Service; +import com.example.solidconnection.siteuser.domain.SiteUser; +import com.example.solidconnection.siteuser.repository.SiteUserRepository; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_APPLICATION_ALREADY_EXISTED; +import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND; + +@Service +@RequiredArgsConstructor +@Slf4j +public class MentorApplicationService { + + private final MentorApplicationRepository mentorApplicationRepository; + private final SiteUserRepository siteUserRepository; + private final S3Service s3Service; + + @Transactional + public void submitMentorApplication( + long siteUserId, + MentorApplicationRequest mentorApplicationRequest, + MultipartFile file + ) { + if (mentorApplicationRepository.existsBySiteUserIdAndMentorApplicationStatusIn( + siteUserId, + List.of(MentorApplicationStatus.PENDING, MentorApplicationStatus.APPROVED)) + ) { + throw new CustomException(MENTOR_APPLICATION_ALREADY_EXISTED); + } + + SiteUser siteUser = siteUserRepository.findById(siteUserId) + .orElseThrow(() -> new CustomException(USER_NOT_FOUND)); + UploadedFileUrlResponse uploadedFile = s3Service.uploadFile(file, ImgType.MENTOR_PROOF); + MentorApplication mentorApplication = new MentorApplication( + siteUser.getId(), + mentorApplicationRequest.country(), + mentorApplicationRequest.universityId(), + mentorApplicationRequest.universitySelectType(), + uploadedFile.fileUrl(), + mentorApplicationRequest.exchangeStatus() + ); + mentorApplicationRepository.save(mentorApplication); + } +} diff --git a/src/main/java/com/example/solidconnection/s3/domain/ImgType.java b/src/main/java/com/example/solidconnection/s3/domain/ImgType.java index 50ac78b30..b26d5fc10 100644 --- a/src/main/java/com/example/solidconnection/s3/domain/ImgType.java +++ b/src/main/java/com/example/solidconnection/s3/domain/ImgType.java @@ -4,7 +4,14 @@ @Getter public enum ImgType { - PROFILE("profile"), GPA("gpa"), LANGUAGE_TEST("language"), COMMUNITY("community"), NEWS("news"), CHAT("chat"); + PROFILE("profile"), + GPA("gpa"), + LANGUAGE_TEST("language"), + COMMUNITY("community"), + NEWS("news"), + CHAT("chat"), + MENTOR_PROOF("mentor-proof"), + ; private final String type; diff --git a/src/main/resources/db/migration/V35__add_mentor_application_table.sql b/src/main/resources/db/migration/V35__add_mentor_application_table.sql new file mode 100644 index 000000000..be07fe901 --- /dev/null +++ b/src/main/resources/db/migration/V35__add_mentor_application_table.sql @@ -0,0 +1,20 @@ +CREATE TABLE mentor_application +( + id BIGINT NOT NULL AUTO_INCREMENT, + site_user_id BIGINT, + country_code VARCHAR(255), + university_id BIGINT, + university_select_type enum ('CATALOG','OTHER') not null, + mentor_proof_url VARCHAR(500) NOT NULL, + rejected_reason VARCHAR(255), + exchange_status enum('AFTER_EXCHANGE','STUDYING_ABROAD') NOT NULL, + mentor_application_status enum('APPROVED','PENDING','REJECTED') NOT NULL, + created_at DATETIME(6), + updated_at DATETIME(6), + PRIMARY KEY (id), + CONSTRAINT fk_mentor_application_site_user FOREIGN KEY (site_user_id) REFERENCES site_user (id), + CONSTRAINT chk_ma_university_select_rule CHECK ( + (university_select_type = 'CATALOG' AND university_id IS NOT NULL) OR + (university_select_type = 'OTHER' AND university_id IS NULL) + ) +) ENGINE=InnoDB diff --git a/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixture.java b/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixture.java new file mode 100644 index 000000000..918db7ef4 --- /dev/null +++ b/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixture.java @@ -0,0 +1,66 @@ +package com.example.solidconnection.mentor.fixture; + +import com.example.solidconnection.mentor.domain.MentorApplication; +import com.example.solidconnection.mentor.domain.MentorApplicationStatus; +import com.example.solidconnection.mentor.domain.UniversitySelectType; +import com.example.solidconnection.siteuser.domain.ExchangeStatus; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.test.context.TestComponent; + +@TestComponent +@RequiredArgsConstructor +public class MentorApplicationFixture { + + private final MentorApplicationFixtureBuilder mentorApplicationFixtureBuilder; + + private static final String DEFAULT_COUNTRY_CODE = "US"; + private static final String DEFAULT_PROOF_URL = "/mentor-proof.pdf"; + private static final ExchangeStatus DEFAULT_EXCHANGE_STATUS = ExchangeStatus.AFTER_EXCHANGE; + + public MentorApplication 대기중_멘토신청( + long siteUserId, + UniversitySelectType selectType, + Long universityId + ) { + return mentorApplicationFixtureBuilder.mentorApplication() + .siteUserId(siteUserId) + .countryCode(DEFAULT_COUNTRY_CODE) + .universityId(universityId) + .universitySelectType(selectType) + .mentorProofUrl(DEFAULT_PROOF_URL) + .exchangeStatus(DEFAULT_EXCHANGE_STATUS) + .create(); + } + + public MentorApplication 승인된_멘토신청( + long siteUserId, + UniversitySelectType selectType, + Long universityId + ){ + return mentorApplicationFixtureBuilder.mentorApplication() + .siteUserId(siteUserId) + .countryCode(DEFAULT_COUNTRY_CODE) + .universityId(universityId) + .universitySelectType(selectType) + .mentorProofUrl(DEFAULT_PROOF_URL) + .exchangeStatus(DEFAULT_EXCHANGE_STATUS) + .mentorApplicationStatus(MentorApplicationStatus.APPROVED) + .create(); + } + + public MentorApplication 거절된_멘토신청( + long siteUserId, + UniversitySelectType selectType, + Long universityId + ){ + return mentorApplicationFixtureBuilder.mentorApplication() + .siteUserId(siteUserId) + .countryCode(DEFAULT_COUNTRY_CODE) + .universityId(universityId) + .universitySelectType(selectType) + .mentorProofUrl(DEFAULT_PROOF_URL) + .exchangeStatus(DEFAULT_EXCHANGE_STATUS) + .mentorApplicationStatus(MentorApplicationStatus.REJECTED) + .create(); + } +} diff --git a/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixtureBuilder.java b/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixtureBuilder.java new file mode 100644 index 000000000..fc6fe547d --- /dev/null +++ b/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixtureBuilder.java @@ -0,0 +1,77 @@ +package com.example.solidconnection.mentor.fixture; + +import com.example.solidconnection.mentor.domain.MentorApplication; +import com.example.solidconnection.mentor.domain.MentorApplicationStatus; +import com.example.solidconnection.mentor.domain.UniversitySelectType; +import com.example.solidconnection.mentor.repository.MentorApplicationRepository; +import com.example.solidconnection.siteuser.domain.ExchangeStatus; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.test.context.TestComponent; +import org.springframework.test.util.ReflectionTestUtils; + +@TestComponent +@RequiredArgsConstructor +public class MentorApplicationFixtureBuilder { + + private final MentorApplicationRepository mentorApplicationRepository; + + private long siteUserId; + private String countryCode = "US"; + private Long universityId = null; + private UniversitySelectType universitySelectType = UniversitySelectType.OTHER; + private String mentorProofUrl = "/mentor-proof.pdf"; + private ExchangeStatus exchangeStatus = ExchangeStatus.AFTER_EXCHANGE; + private MentorApplicationStatus mentorApplicationStatus = MentorApplicationStatus.PENDING; + + public MentorApplicationFixtureBuilder mentorApplication() { + return new MentorApplicationFixtureBuilder(mentorApplicationRepository); + } + + public MentorApplicationFixtureBuilder siteUserId(long siteUserId) { + this.siteUserId = siteUserId; + return this; + } + + public MentorApplicationFixtureBuilder countryCode(String countryCode) { + this.countryCode = countryCode; + return this; + } + + public MentorApplicationFixtureBuilder universityId(Long universityId) { + this.universityId = universityId; + return this; + } + + public MentorApplicationFixtureBuilder universitySelectType(UniversitySelectType universitySelectType) { + this.universitySelectType = universitySelectType; + return this; + } + + public MentorApplicationFixtureBuilder mentorProofUrl(String mentorProofUrl) { + this.mentorProofUrl = mentorProofUrl; + return this; + } + + public MentorApplicationFixtureBuilder exchangeStatus(ExchangeStatus exchangeStatus) { + this.exchangeStatus = exchangeStatus; + return this; + } + + public MentorApplicationFixtureBuilder mentorApplicationStatus(MentorApplicationStatus mentorApplicationStatus) { + this.mentorApplicationStatus = mentorApplicationStatus; + return this; + } + + public MentorApplication create() { + MentorApplication mentorApplication = new MentorApplication( + siteUserId, + countryCode, + universityId, + universitySelectType, + mentorProofUrl, + exchangeStatus + ); + ReflectionTestUtils.setField(mentorApplication, "mentorApplicationStatus", mentorApplicationStatus); + return mentorApplicationRepository.save(mentorApplication); + } +} diff --git a/src/test/java/com/example/solidconnection/mentor/service/MentorApplicationServiceTest.java b/src/test/java/com/example/solidconnection/mentor/service/MentorApplicationServiceTest.java new file mode 100644 index 000000000..aaf63b600 --- /dev/null +++ b/src/test/java/com/example/solidconnection/mentor/service/MentorApplicationServiceTest.java @@ -0,0 +1,196 @@ +package com.example.solidconnection.mentor.service; + +import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_APPLICATION_ALREADY_EXISTED; +import static com.example.solidconnection.common.exception.ErrorCode.UNIVERSITY_ID_MUST_BE_NULL_FOR_OTHER; +import static com.example.solidconnection.common.exception.ErrorCode.UNIVERSITY_ID_REQUIRED_FOR_CATALOG; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.mockito.BDDMockito.given; + +import com.example.solidconnection.common.exception.CustomException; +import com.example.solidconnection.mentor.domain.MentorApplicationStatus; +import com.example.solidconnection.mentor.domain.UniversitySelectType; +import com.example.solidconnection.mentor.dto.MentorApplicationRequest; +import com.example.solidconnection.mentor.fixture.MentorApplicationFixture; +import com.example.solidconnection.mentor.repository.MentorApplicationRepository; +import com.example.solidconnection.s3.domain.ImgType; +import com.example.solidconnection.s3.dto.UploadedFileUrlResponse; +import com.example.solidconnection.s3.service.S3Service; +import com.example.solidconnection.siteuser.domain.ExchangeStatus; +import com.example.solidconnection.siteuser.domain.SiteUser; +import com.example.solidconnection.siteuser.fixture.SiteUserFixture; +import com.example.solidconnection.support.TestContainerSpringBootTest; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.mock.web.MockMultipartFile; + +@TestContainerSpringBootTest +@DisplayName("멘토 승격 신청 서비스 테스트") +public class MentorApplicationServiceTest { + + @Autowired + private MentorApplicationService mentorApplicationService; + + @Autowired + private MentorApplicationRepository mentorApplicationRepository; + + @Autowired + private SiteUserFixture siteUserFixture; + + @Autowired + private MentorApplicationFixture mentorApplicationFixture; + + @MockBean + private S3Service s3Service; + + private SiteUser user; + + @BeforeEach + void setUp() { + user = siteUserFixture.사용자(); + } + + @Test + void 멘토_승격_신청을_등록한다() { + // given + UniversitySelectType universitySelectType = UniversitySelectType.CATALOG; + Long universityId = 1L; + MentorApplicationRequest request = createMentorApplicationRequest(universitySelectType, universityId); + MockMultipartFile file = createMentorProofFile(); + String fileUrl = "/mentor-proof.pdf"; + given(s3Service.uploadFile(file, ImgType.MENTOR_PROOF)) + .willReturn(new UploadedFileUrlResponse(fileUrl)); + + // when + mentorApplicationService.submitMentorApplication(user.getId(), request, file); + + // then + assertThat(mentorApplicationRepository.existsBySiteUserIdAndMentorApplicationStatusIn(user.getId(), List.of(MentorApplicationStatus.PENDING, MentorApplicationStatus.APPROVED))).isEqualTo(true); + } + + @Test + void universityId_가_null_로_들어와도_멘토_승격_신청을_등록한다() { + // given + UniversitySelectType universitySelectType = UniversitySelectType.OTHER; + Long universityId = null; + MentorApplicationRequest request = createMentorApplicationRequest(universitySelectType, universityId); + MockMultipartFile file = createMentorProofFile(); + String fileUrl = "/mentor-proof.pdf"; + given(s3Service.uploadFile(file, ImgType.MENTOR_PROOF)) + .willReturn(new UploadedFileUrlResponse(fileUrl)); + + // when + mentorApplicationService.submitMentorApplication(user.getId(), request, file); + + // then + assertThat(mentorApplicationRepository.existsBySiteUserIdAndMentorApplicationStatusIn(user.getId(),List.of(MentorApplicationStatus.PENDING, MentorApplicationStatus.APPROVED))).isEqualTo(true); + } + + @Test + void 대학_선택_타입이_CATALOG_인데_universityId가_null_이면_예외가_발생한다(){ + // given + UniversitySelectType universitySelectType = UniversitySelectType.CATALOG; + Long universityId = null; + MentorApplicationRequest request = createMentorApplicationRequest(universitySelectType, universityId); + MockMultipartFile file = createMentorProofFile(); + String fileUrl = "/mentor-proof.pdf"; + given(s3Service.uploadFile(file, ImgType.MENTOR_PROOF)) + .willReturn(new UploadedFileUrlResponse(fileUrl)); + + // when & then + assertThatCode(() -> mentorApplicationService.submitMentorApplication(user.getId(), request, file)) + .isInstanceOf(CustomException.class) + .hasMessage(UNIVERSITY_ID_REQUIRED_FOR_CATALOG.getMessage()); + } + + @Test + void 대학_선택_타입이_OTHER_인데_universityId가_존재하면_예외가_발생한다(){ + // given + UniversitySelectType universitySelectType = UniversitySelectType.OTHER; + Long universityId = 1L; + MentorApplicationRequest request = createMentorApplicationRequest(universitySelectType, universityId); + MockMultipartFile file = createMentorProofFile(); + String fileUrl = "/mentor-proof.pdf"; + given(s3Service.uploadFile(file, ImgType.MENTOR_PROOF)) + .willReturn(new UploadedFileUrlResponse(fileUrl)); + + // when & then + assertThatCode(() -> mentorApplicationService.submitMentorApplication(user.getId(), request, file)) + .isInstanceOf(CustomException.class) + .hasMessage(UNIVERSITY_ID_MUST_BE_NULL_FOR_OTHER.getMessage()); + } + + @Test + void 이미_PENDING_상태인_멘토_승격_요청이_존재할_때_중복으로_멘토_승격_신청_시_예외가_발생한다() { + // given + mentorApplicationFixture.대기중_멘토신청(user.getId(), UniversitySelectType.CATALOG, 1L); + + UniversitySelectType universitySelectType = UniversitySelectType.CATALOG; + Long universityId = 1L; + MentorApplicationRequest request = createMentorApplicationRequest(universitySelectType, universityId); + MockMultipartFile file = createMentorProofFile(); + + // when & then + assertThatCode(() -> mentorApplicationService.submitMentorApplication(user.getId(), request, file)) + .isInstanceOf(CustomException.class) + .hasMessage(MENTOR_APPLICATION_ALREADY_EXISTED.getMessage()); + } + + @Test + void 이미_APPROVE_상태인_멘토_승격_요청이_존재할_때_중복으로_멘토_승격_신청_시_예외가_발생한다() { + // given + mentorApplicationFixture.승인된_멘토신청(user.getId(), UniversitySelectType.CATALOG, 1L); + + UniversitySelectType universitySelectType = UniversitySelectType.CATALOG; + Long universityId = 1L; + MentorApplicationRequest request = createMentorApplicationRequest(universitySelectType, universityId); + MockMultipartFile file = createMentorProofFile(); + + // when & then + assertThatCode(() -> mentorApplicationService.submitMentorApplication(user.getId(), request, file)) + .isInstanceOf(CustomException.class) + .hasMessage(MENTOR_APPLICATION_ALREADY_EXISTED.getMessage()); + } + + @Test + void 이미_REJECTED_상태인_멘토_승격_요청이_존재할_때_멘토_신청이_등록된다() { + // given + mentorApplicationFixture.거절된_멘토신청(user.getId(), UniversitySelectType.CATALOG, 1L); + + UniversitySelectType universitySelectType = UniversitySelectType.CATALOG; + Long universityId = 1L; + MentorApplicationRequest request = createMentorApplicationRequest(universitySelectType, universityId); + MockMultipartFile file = createMentorProofFile(); + String fileUrl = "/mentor-proof.pdf"; + given(s3Service.uploadFile(file, ImgType.MENTOR_PROOF)) + .willReturn(new UploadedFileUrlResponse(fileUrl)); + + // when + mentorApplicationService.submitMentorApplication(user.getId(), request, file); + + // then + assertThat(mentorApplicationRepository.existsBySiteUserIdAndMentorApplicationStatusIn(user.getId(),List.of(MentorApplicationStatus.PENDING, MentorApplicationStatus.APPROVED))).isEqualTo(true); + } + + private MockMultipartFile createMentorProofFile() { + return new MockMultipartFile( + "image", + "test.jpg", + "image/jpeg", + "test image content".getBytes() + ); + } + + private MentorApplicationRequest createMentorApplicationRequest(UniversitySelectType universitySelectType, Long universityId) { + return new MentorApplicationRequest( + ExchangeStatus.AFTER_EXCHANGE, + universitySelectType, + "US", + universityId + ); + } +}