diff --git a/build.gradle b/build.gradle index 41f022be..83ee3c7d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,65 +1,67 @@ plugins { - id 'java' - id 'org.springframework.boot' version '3.1.4' - id 'io.spring.dependency-management' version '1.1.3' + id 'java' + id 'org.springframework.boot' version '3.1.4' + id 'io.spring.dependency-management' version '1.1.3' } group = 'com' version = '0.0.1-SNAPSHOT' java { - sourceCompatibility = '17' + sourceCompatibility = '17' } configurations { - compileOnly { - extendsFrom annotationProcessor - } + compileOnly { + extendsFrom annotationProcessor + } } repositories { - mavenCentral() + mavenCentral() } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-amqp' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-data-redis' - implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-webflux' - implementation 'org.springframework.boot:spring-boot-starter-validation' - compileOnly 'org.projectlombok:lombok' - runtimeOnly 'com.mysql:mysql-connector-j' - annotationProcessor 'org.projectlombok:lombok' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'io.projectreactor:reactor-test' - testImplementation 'org.springframework.amqp:spring-rabbit-test' - testImplementation 'org.springframework.security:spring-security-test' + implementation 'org.springframework.boot:spring-boot-starter-amqp' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-webflux' + implementation 'org.springframework.boot:spring-boot-starter-validation' + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'com.mysql:mysql-connector-j' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'io.projectreactor:reactor-test' + testImplementation 'org.springframework.amqp:spring-rabbit-test' + testImplementation 'org.springframework.security:spring-security-test' + testCompileOnly 'org.springframework.security:spring-security-test' + testAnnotationProcessor 'org.springframework.security:spring-security-test' - implementation 'io.jsonwebtoken:jjwt-api:0.11.5' - implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' - implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' - //jwt 추가 + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' + implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' + //jwt 추가 - // https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' - //swagger 추가 + // https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' + //swagger 추가 - // Spring boot 3.x이상에서 QueryDsl 패키지를 정의하는 방법 - implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' - annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" - annotationProcessor "jakarta.annotation:jakarta.annotation-api" - annotationProcessor "jakarta.persistence:jakarta.persistence-api" + // Spring boot 3.x이상에서 QueryDsl 패키지를 정의하는 방법 + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" - // aws s3 - implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + // aws s3 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' // https://mvnrepository.com/artifact/org.mockito/mockito-core - testImplementation 'org.mockito:mockito-core:5.6.0' + testImplementation 'org.mockito:mockito-core:5.6.0' } tasks.named('test') { - useJUnitPlatform() + useJUnitPlatform() } \ No newline at end of file diff --git a/src/main/java/com/postgraduate/domain/auth/application/usecase/jwt/JwtUseCase.java b/src/main/java/com/postgraduate/domain/auth/application/usecase/jwt/JwtUseCase.java index 6a021173..67ac334d 100644 --- a/src/main/java/com/postgraduate/domain/auth/application/usecase/jwt/JwtUseCase.java +++ b/src/main/java/com/postgraduate/domain/auth/application/usecase/jwt/JwtUseCase.java @@ -1,6 +1,7 @@ package com.postgraduate.domain.auth.application.usecase.jwt; import com.postgraduate.domain.auth.application.dto.res.JwtTokenResponse; +import com.postgraduate.domain.senior.exception.NoneSeniorException; import com.postgraduate.domain.user.domain.entity.User; import com.postgraduate.domain.user.domain.entity.constant.Role; import com.postgraduate.domain.user.exception.DeletedUserException; @@ -32,7 +33,7 @@ public JwtTokenResponse signIn(User user) { return adminToken(user); return userToken(user); } - + public void logout(User user) { jwtUtils.makeExpired(user.getUserId()); } @@ -52,7 +53,8 @@ public JwtTokenResponse changeUser(User user) { } public JwtTokenResponse changeSenior(User user) { - checkDelete(user); + if (!SENIOR.equals(user.getRole())) + throw new NoneSeniorException(); return seniorToken(user); } diff --git a/src/test/java/com/postgraduate/IntegrationTest.java b/src/test/java/com/postgraduate/IntegrationTest.java new file mode 100644 index 00000000..1d4ba382 --- /dev/null +++ b/src/test/java/com/postgraduate/IntegrationTest.java @@ -0,0 +1,38 @@ +package com.postgraduate; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +@Disabled +public class IntegrationTest { + + @Autowired + protected MockMvc mvc; + @Autowired + private WebApplicationContext ctx; + + @BeforeEach + void setUp() { + mvc = MockMvcBuilders + .webAppContextSetup(ctx) + .apply(springSecurity()) + .alwaysExpect(status().isOk()) + .alwaysExpect(content().contentType(MediaType.APPLICATION_JSON)) + .build(); + } +} diff --git a/src/test/java/com/postgraduate/domain/auth/presentation/AuthControllerTest.java b/src/test/java/com/postgraduate/domain/auth/presentation/AuthControllerTest.java new file mode 100644 index 00000000..249924e7 --- /dev/null +++ b/src/test/java/com/postgraduate/domain/auth/presentation/AuthControllerTest.java @@ -0,0 +1,282 @@ +package com.postgraduate.domain.auth.presentation; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.postgraduate.IntegrationTest; +import com.postgraduate.domain.auth.application.dto.req.*; +import com.postgraduate.domain.auth.application.dto.res.KakaoUserInfoResponse; +import com.postgraduate.domain.auth.application.usecase.oauth.kakao.KakaoAccessTokenUseCase; +import com.postgraduate.domain.senior.presentation.constant.SeniorResponseCode; +import com.postgraduate.domain.senior.presentation.constant.SeniorResponseMessage; +import com.postgraduate.domain.user.domain.entity.User; +import com.postgraduate.domain.user.domain.repository.UserRepository; +import com.postgraduate.domain.wish.domain.entity.Wish; +import com.postgraduate.domain.wish.domain.entity.constant.Status; +import com.postgraduate.domain.wish.domain.repository.WishRepository; +import com.postgraduate.global.config.redis.RedisRepository; +import com.postgraduate.global.config.security.jwt.util.JwtUtils; +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.http.MediaType; + +import java.util.Optional; + +import static com.postgraduate.domain.auth.presentation.constant.AuthResponseCode.*; +import static com.postgraduate.domain.auth.presentation.constant.AuthResponseMessage.*; +import static com.postgraduate.domain.senior.presentation.constant.SeniorResponseCode.SENIOR_CREATE; +import static com.postgraduate.domain.senior.presentation.constant.SeniorResponseMessage.CREATE_SENIOR; +import static com.postgraduate.domain.user.domain.entity.constant.Role.SENIOR; +import static com.postgraduate.domain.user.domain.entity.constant.Role.USER; +import static com.postgraduate.domain.user.presentation.constant.UserResponseCode.USER_NOT_FOUND; +import static com.postgraduate.domain.user.presentation.constant.UserResponseMessage.NOT_FOUND_USER; +import static java.time.LocalDateTime.now; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class AuthControllerTest extends IntegrationTest { + private static final String AUTHORIZATION = "Authorization"; + private static final String BEARER = "Bearer "; + @Autowired + private JwtUtils jwtUtil; + @Autowired + private ObjectMapper objectMapper; + @MockBean + private KakaoAccessTokenUseCase kakaoAccessTokenUseCase; + @Autowired + private UserRepository userRepository; + @Autowired + private WishRepository wishRepository; + @MockBean + RedisRepository redisRepository; + private User user; + private final Long anonymousUserSocialId = 2L; + + + @BeforeEach + void setUp() { + user = new User(0L, 1L, "mail", "후배", "011", "profile", 0, USER, true, now(), now(), false); + userRepository.save(user); + } + + @Test + @DisplayName("회원이 로그인한다.") + void authLoginByUser() throws Exception { + Wish wish = new Wish(0L, "major", "field", true, user, Status.MATCHED); + wishRepository.save(wish); + + CodeRequest codeRequest = new CodeRequest("code"); + String request = objectMapper.writeValueAsString(codeRequest); + + when(kakaoAccessTokenUseCase.getAccessToken(codeRequest)) + .thenReturn(new KakaoUserInfoResponse(1L, any())); + + mvc.perform(post("/auth/login/KAKAO") + .content(request) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(AUTH_ALREADY.getCode())) + .andExpect(jsonPath("$.message").value(SUCCESS_AUTH.getMessage())) + .andExpect(jsonPath("$.data.accessToken").exists()) + .andExpect(jsonPath("$.data.socialId").doesNotExist()); + } + + @Test + @DisplayName("비회원이 로그인한다.") + void authLoginByAnonymousUser() throws Exception { + CodeRequest codeRequest = new CodeRequest("code"); + String request = objectMapper.writeValueAsString(codeRequest); + + when(kakaoAccessTokenUseCase.getAccessToken(codeRequest)) + .thenReturn(new KakaoUserInfoResponse(anonymousUserSocialId, any())); + + mvc.perform(post("/auth/login/KAKAO") + .content(request) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(AUTH_NONE.getCode())) + .andExpect(jsonPath("$.message").value(NOT_REGISTERED_USER.getMessage())) + .andExpect(jsonPath("$.data.accessToken").doesNotExist()) + .andExpect(jsonPath("$.data.socialId").value(anonymousUserSocialId)); + } + + @Test + @DisplayName("대학생이 회원가입 한다.") + void signUpUser() throws Exception { + authLoginByAnonymousUser(); + + String request = objectMapper.writeValueAsString( + new SignUpRequest(anonymousUserSocialId, "01012345678", "nickname", + true, "major", "field", true) + ); + + mvc.perform(post("/auth/user/signup") + .content(request) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(AUTH_CREATE.getCode())) + .andExpect(jsonPath("$.message").value(SUCCESS_AUTH.getMessage())) + .andExpect(jsonPath("$.data.accessToken").exists()) + .andExpect(jsonPath("$.data.role").value(USER.name())); + } + + @Test + @DisplayName("대학원생이 대학생으로 변경한다.") + void changeUserToken() throws Exception { + Wish wish = new Wish(0L, "major", "field", true, user, Status.MATCHED); + wishRepository.save(wish); + + String token = jwtUtil.generateAccessToken(user.getUserId(), SENIOR); + + mvc.perform(post("/auth/user/token") + .header(AUTHORIZATION, BEARER + token)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(AUTH_CREATE.getCode())) + .andExpect(jsonPath("$.message").value(SUCCESS_AUTH.getMessage())) + .andExpect(jsonPath("$.data.accessToken").exists()) + .andExpect(jsonPath("$.data.role").value(USER.name())); + } + + @Test + @DisplayName("대학생으로 가입하지 않은 경우 대학생으로 변경할 수 없다.") + void changeUserTokenWithoutWish() throws Exception { + String token = jwtUtil.generateAccessToken(user.getUserId(), SENIOR); + + mvc.perform(post("/auth/user/token") + .header(AUTHORIZATION, BEARER + token)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(USER_NOT_FOUND.getCode())) + .andExpect(jsonPath("$.message").value(NOT_FOUND_USER.getMessage())); + } + + @Test + @DisplayName("선배가 후배로 추가 가입합니다.") + void changeUser() throws Exception { + String seniorAccessToken = jwtUtil.generateAccessToken(user.getUserId(), SENIOR); + + String request = objectMapper.writeValueAsString( + new UserChangeRequest("major", "field", true) + ); + + mvc.perform(post("/auth/user/change") + .header(AUTHORIZATION, BEARER + seniorAccessToken) + .content(request) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(AUTH_CREATE.getCode())) + .andExpect(jsonPath("$.message").value(SUCCESS_AUTH.getMessage())) + .andExpect(jsonPath("$.data.accessToken").exists()) + .andExpect(jsonPath("$.data.role").value(USER.name())); + } + + @Test + @DisplayName("선배가 회원가입한다.") + void singUpSenior() throws Exception { + authLoginByAnonymousUser(); + + String request = objectMapper.writeValueAsString( + new SeniorSignUpRequest(anonymousUserSocialId, "01012345678", "nickname", + true, "전공", "서울대학교", "교수", "연구실", + "AI", "키워드", "certification") + ); + + mvc.perform(post("/auth/senior/signup") + .content(request) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(SENIOR_CREATE.getCode())) + .andExpect(jsonPath("$.message").value(CREATE_SENIOR.getMessage())) + .andExpect(jsonPath("$.data.accessToken").exists()) + .andExpect(jsonPath("$.data.role").value(SENIOR.name())); + } + + @Test + @DisplayName("후배가 선배로 추가 가입합니다.") + void changeSenior() throws Exception { + String userAccessToken = jwtUtil.generateAccessToken(user.getUserId(), USER); + + String request = objectMapper.writeValueAsString( + new SeniorChangeRequest("major", "field", "교수", "연구실", + "AI", "키워드", "certification") + ); + + mvc.perform(post("/auth/senior/change") + .header(AUTHORIZATION, BEARER + userAccessToken) + .content(request) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(SENIOR_CREATE.getCode())) + .andExpect(jsonPath("$.message").value(CREATE_SENIOR.getMessage())) + .andExpect(jsonPath("$.data.accessToken").exists()) + .andExpect(jsonPath("$.data.role").value(SENIOR.name())); + } + + @Test + @DisplayName("대학생이 대학원생으로 변경한다.") + void changeSeniorToken() throws Exception { + User senior = new User(0L, 2L, "mail", "선배", "011", "profile", 0, SENIOR, true, now(), now(), false); + userRepository.save(senior); + + String token = jwtUtil.generateAccessToken(senior.getUserId(), USER); + + mvc.perform(post("/auth/senior/token") + .header(AUTHORIZATION, BEARER + token)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(AUTH_CREATE.getCode())) + .andExpect(jsonPath("$.message").value(SUCCESS_AUTH.getMessage())) + .andExpect(jsonPath("$.data.accessToken").exists()) + .andExpect(jsonPath("$.data.role").value(SENIOR.name())); + } + + @Test + @DisplayName("대학원생으로 가입하지 않은 경우 대학원생으로 변경할 수 없다.") + void changeSeniorTokenWithoutWish() throws Exception { + String token = jwtUtil.generateAccessToken(user.getUserId(), USER); + + mvc.perform(post("/auth/senior/token") + .header(AUTHORIZATION, BEARER + token)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(SeniorResponseCode.NONE_SENIOR.getCode())) + .andExpect(jsonPath("$.message").value(SeniorResponseMessage.NONE_SENIOR.getMessage())); + } + + @Test + @DisplayName("토큰을 재발급한다.") + void refresh() throws Exception { + Wish wish = new Wish(0L, "major", "field", true, user, Status.MATCHED); + wishRepository.save(wish); + + String refreshToken = jwtUtil.generateRefreshToken(user.getUserId(), USER); + when(redisRepository.getValues(any())).thenReturn(Optional.of(refreshToken)); + + mvc.perform(post("/auth/refresh") + .header(AUTHORIZATION, BEARER + refreshToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(AUTH_UPDATE.getCode())) + .andExpect(jsonPath("$.message").value(SUCCESS_REGENERATE_TOKEN.getMessage())) + .andExpect(jsonPath("$.data.accessToken").exists()) + .andExpect(jsonPath("$.data.role").value(USER.name())); + } + + @Test + @DisplayName("로그아웃한다.") + void logout() throws Exception { + String accessToken = jwtUtil.generateAccessToken(user.getUserId(), USER); + + mvc.perform(post("/auth/logout") + .header(AUTHORIZATION, BEARER + accessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(AUTH_DELETE.getCode())) + .andExpect(jsonPath("$.message").value(LOGOUT_USER.getMessage())); + } +} \ No newline at end of file diff --git a/src/test/java/com/postgraduate/domain/mentoring/presentation/MentoringControllerTest.java b/src/test/java/com/postgraduate/domain/mentoring/presentation/MentoringControllerTest.java new file mode 100644 index 00000000..01d3d1d2 --- /dev/null +++ b/src/test/java/com/postgraduate/domain/mentoring/presentation/MentoringControllerTest.java @@ -0,0 +1,357 @@ +package com.postgraduate.domain.mentoring.presentation; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.postgraduate.IntegrationTest; +import com.postgraduate.domain.mentoring.application.dto.req.MentoringApplyRequest; +import com.postgraduate.domain.mentoring.application.dto.req.MentoringDateRequest; +import com.postgraduate.domain.mentoring.domain.entity.Mentoring; +import com.postgraduate.domain.mentoring.domain.entity.constant.Status; +import com.postgraduate.domain.mentoring.domain.repository.MentoringRepository; +import com.postgraduate.domain.mentoring.presentation.constant.MentoringResponseCode; +import com.postgraduate.domain.mentoring.presentation.constant.MentoringResponseMessage; +import com.postgraduate.domain.payment.domain.entity.Payment; +import com.postgraduate.domain.payment.domain.repository.PaymentRepository; +import com.postgraduate.domain.refuse.application.dto.req.MentoringRefuseRequest; +import com.postgraduate.domain.salary.domain.entity.Salary; +import com.postgraduate.domain.salary.domain.repository.SalaryRepository; +import com.postgraduate.domain.senior.domain.entity.Info; +import com.postgraduate.domain.senior.domain.entity.Profile; +import com.postgraduate.domain.senior.domain.entity.Senior; +import com.postgraduate.domain.senior.domain.repository.SeniorRepository; +import com.postgraduate.domain.user.domain.entity.User; +import com.postgraduate.domain.user.domain.entity.constant.Role; +import com.postgraduate.domain.user.domain.repository.UserRepository; +import com.postgraduate.global.config.security.jwt.util.JwtUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; + +import java.time.LocalDate; + +import static com.postgraduate.domain.mentoring.presentation.constant.MentoringResponseCode.*; +import static com.postgraduate.domain.mentoring.presentation.constant.MentoringResponseMessage.*; +import static com.postgraduate.domain.payment.domain.entity.constant.Status.DONE; +import static com.postgraduate.domain.salary.util.SalaryUtil.getSalaryDate; +import static com.postgraduate.domain.senior.domain.entity.constant.Status.WAITING; +import static java.time.LocalDateTime.now; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class MentoringControllerTest extends IntegrationTest { + private static final String AUTHORIZATION = "Authorization"; + private static final String BEARER = "Bearer "; + @Autowired + private ObjectMapper objectMapper; + @Autowired + private JwtUtils jwtUtil; + @Autowired + private UserRepository userRepository; + @Autowired + private SeniorRepository seniorRepository; + @Autowired + private MentoringRepository mentoringRepository; + @Autowired + private SalaryRepository salaryRepository; + @Autowired + private PaymentRepository paymentRepository; + private User user; + private Senior senior; + private String userAccessToken; + private String seniorAccessToken; + + @BeforeEach + void setUp() { + user = new User(0L, 1L, "mail", "후배", "011", "profile", 0, Role.USER, true, now(), now(), false); + userRepository.save(user); + + User userOfSenior = new User(0L, 2L, "mail", "선배", "012", "profile", 0, Role.SENIOR, true, now(), now(), false); + userRepository.save(userOfSenior); + + Info info = new Info("major", "서울대학교", "교수님", "키워드1,키워드2", "랩실", "인공지능", false, false, "인공지능,키워드1,키워드2"); + Profile profile = new Profile("저는요", "한줄소개", "대상", "chatLink", 40); + senior = new Senior(0L, userOfSenior, "certification", WAITING, 0, info, profile, now(), now()); + seniorRepository.save(senior); + + userAccessToken = jwtUtil.generateAccessToken(user.getUserId(), Role.USER); + seniorAccessToken = jwtUtil.generateAccessToken(userOfSenior.getUserId(), Role.SENIOR); + } + + @ParameterizedTest + @EnumSource(value = Status.class, names = {"WAITING", "DONE"}) + @DisplayName("대학생이 확정대기 및 완료 상태의 멘토링 목록을 조회한다") + void getWaitingMentorings(Status status) throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date", 40, status, now(), now()); + mentoringRepository.save(mentoring); + + mvc.perform(get("/mentoring/me/{status}", status.name().toLowerCase()) + .header(AUTHORIZATION, BEARER + userAccessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_FIND.getCode())) + .andExpect(jsonPath("$.message").value(GET_MENTORING_LIST_INFO.getMessage())) + .andExpect(jsonPath("$.data.mentoringInfos[0].mentoringId").value(mentoring.getMentoringId())) + .andExpect(jsonPath("$.data.mentoringInfos[0].chatLink").doesNotExist()); + } + + @Test + @DisplayName("대학생이 예정된 멘토링 목록을 조회한다") + void getExpectedMentorings() throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date", 40, Status.EXPECTED, now(), now()); + mentoringRepository.save(mentoring); + + mvc.perform(get("/mentoring/me/expected") + .header(AUTHORIZATION, BEARER + userAccessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_FIND.getCode())) + .andExpect(jsonPath("$.message").value(GET_MENTORING_LIST_INFO.getMessage())) + .andExpect(jsonPath("$.data.mentoringInfos[0].mentoringId").value(mentoring.getMentoringId())); + } + + @ParameterizedTest + @EnumSource(value = Status.class, names = {"WAITING", "EXPECTED"}) + @DisplayName("대학생이 멘토링을 상세조회한다.") + void getMentoringDetail(Status status) throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date", 40, status, now(), now()); + mentoringRepository.save(mentoring); + + mvc.perform(get("/mentoring/me/{mentoringId}", mentoring.getMentoringId()) + .header(AUTHORIZATION, BEARER + userAccessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_FIND.getCode())) + .andExpect(jsonPath("$.message").value(GET_MENTORING_DETAIL_INFO.getMessage())) + .andExpect(jsonPath("$.data.seniorId").value(senior.getSeniorId())); + } + + + @ParameterizedTest + @EnumSource(value = Status.class, names = {"DONE", "CANCEL", "REFUSE"}) + @DisplayName("대학생의 완료, 취소, 거절 상태의 멘토링은 상세조회되지 않는다.") + void getDoneMentoringDetail(Status status) throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date", 40, status, now(), now()); + mentoringRepository.save(mentoring); + + mvc.perform(get("/mentoring/me/{mentoringId}", mentoring.getMentoringId()) + .header(AUTHORIZATION, BEARER + userAccessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(DETAIL_NOT_FOUND.getCode())) + .andExpect(jsonPath("$.message").value(NOT_FOUND_DETAIL.getMessage())); + } + + @Test + @DisplayName("대학생이 멘토링을 신청한다.") + void applyMentoring() throws Exception { + String request = objectMapper.writeValueAsString(new MentoringApplyRequest(senior.getSeniorId(), "topic", "question", "date1,date2,date3")); + + mvc.perform(post("/mentoring/applying") + .header(AUTHORIZATION, BEARER + userAccessToken) + .content(request) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_CREATE.getCode())) + .andExpect(jsonPath("$.message").value(CREATE_MENTORING.getMessage())); + } + + @ParameterizedTest + @ValueSource(strings = {"date1", "date1,date2", "date1,date2,date3,date4"}) + @DisplayName("날짜가 3개가 아니라면 멘토링을 신청할 수 없다.") + void applyMentoringWithoutThreeDates(String date) throws Exception { + String request = objectMapper.writeValueAsString(new MentoringApplyRequest(senior.getSeniorId(), "topic", "question", date)); + + mvc.perform(post("/mentoring/applying") + .header(AUTHORIZATION, BEARER + userAccessToken) + .content(request) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MentoringResponseCode.INVALID_DATE.getCode())) + .andExpect(jsonPath("$.message").value(MentoringResponseMessage.INVALID_DATE.getMessage())); + + } + + @Test + @DisplayName("대학생이 멘토링을 완료한다.") + void updateMentoringDone() throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date", 40, Status.EXPECTED, now(), now()); + mentoringRepository.save(mentoring); + + Salary salary = new Salary(0L, false, senior, null, 10000, getSalaryDate(), now(), null, null, null); + salaryRepository.save(salary); + + mvc.perform(patch("/mentoring/me/{mentoringId}/done", mentoring.getMentoringId()) + .header(AUTHORIZATION, BEARER + userAccessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_UPDATE.getCode())) + .andExpect(jsonPath("$.message").value(UPDATE_MENTORING.getMessage())); + } + + @ParameterizedTest + @EnumSource(value = Status.class, names = {"WAITING", "DONE", "CANCEL", "REFUSE"}) + @DisplayName("진행예정이 아닌 멘토링의 경우 완료할 수 없다.") + void updateMentoringDoneWithoutExpected(Status status) throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date", 40, status, now(), now()); + mentoringRepository.save(mentoring); + + Salary salary = new Salary(0L, false, senior, null, 10000, getSalaryDate(), now(), null, null, null); + salaryRepository.save(salary); + + mvc.perform(patch("/mentoring/me/{mentoringId}/done", mentoring.getMentoringId()) + .header(AUTHORIZATION, BEARER + userAccessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_NOT_EXPECTED.getCode())) + .andExpect(jsonPath("$.message").value(NOT_EXPECTED_MENTORING.getMessage())); + } + + @Test + @DisplayName("대학생이 멘토링을 취소한다.") + void updateMentoringCancel() throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date", 40, Status.WAITING, now(), now()); + mentoringRepository.save(mentoring); + + mvc.perform(patch("/mentoring/me/{mentoringId}/cancel", mentoring.getMentoringId()) + .header(AUTHORIZATION, BEARER + userAccessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_UPDATE.getCode())) + .andExpect(jsonPath("$.message").value(UPDATE_MENTORING.getMessage())); + } + + @ParameterizedTest + @EnumSource(value = Status.class, names = {"EXPECTED", "DONE", "CANCEL", "REFUSE"}) + @DisplayName("멘토링이 확정대기 상태가 아니라면 취소할 수 없다.") + void updateMentoringCancelWithoutWaiting(Status status) throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date", 40, status, now(), now()); + mentoringRepository.save(mentoring); + + mvc.perform(patch("/mentoring/me/{mentoringId}/cancel", mentoring.getMentoringId()) + .header(AUTHORIZATION, BEARER + userAccessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_NOT_WAITING.getCode())) + .andExpect(jsonPath("$.message").value(NOT_WAITING_MENTORING.getMessage())); + } + + @ParameterizedTest + @EnumSource(value = Status.class, names = {"WAITING", "EXPECTED", "DONE"}) + @DisplayName("대학원생이 멘토링 목록을 조회한다.") + void getSeniorMentorings(Status status) throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date", 40, status, now(), now()); + mentoringRepository.save(mentoring); + + Salary salary = new Salary(0L, false, senior, null, 10000, LocalDate.now(), now(), null, null, null); + salaryRepository.save(salary); + + Payment payment = new Payment(0L, mentoring, salary, 10000, "cardAuthNumber", "cardReceipt", now(), null, DONE); + paymentRepository.save(payment); + + mvc.perform(get("/mentoring/senior/me/{status}", status.name().toLowerCase()) + .header(AUTHORIZATION, BEARER + seniorAccessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_FIND.getCode())) + .andExpect(jsonPath("$.message").value(GET_MENTORING_LIST_INFO.getMessage())) + .andExpect(jsonPath("$.data.seniorMentoringInfos[0].mentoringId").value(mentoring.getMentoringId())); + } + + @ParameterizedTest + @EnumSource(value = Status.class, names = {"WAITING", "EXPECTED"}) + @DisplayName("대학원생이 멘토링을 상세조회합니다.") + void getSeniorMentoringDetails(Status status) throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date", 40, status, now(), now()); + mentoringRepository.save(mentoring); + + mvc.perform(get("/mentoring/senior/me/{mentoringId}", mentoring.getMentoringId()) + .header(AUTHORIZATION, BEARER + seniorAccessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_FIND.getCode())) + .andExpect(jsonPath("$.message").value(GET_MENTORING_DETAIL_INFO.getMessage())) + .andExpect(jsonPath("$.data.nickName").value("후배")); + + } + + @ParameterizedTest + @EnumSource(value = Status.class, names = {"DONE", "CANCEL", "REFUSE"}) + @DisplayName("대학원생의 완료, 취소, 거절 상태의 멘토링은 상세조회되지 않는다.") + void doNotGetMentoringDetails(Status status) throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date", 40, status, now(), now()); + mentoringRepository.save(mentoring); + + mvc.perform(get("/mentoring/senior/me/{mentoringId}", mentoring.getMentoringId()) + .header(AUTHORIZATION, BEARER + seniorAccessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(DETAIL_NOT_FOUND.getCode())) + .andExpect(jsonPath("$.message").value(NOT_FOUND_DETAIL.getMessage())); + } + + @Test + @DisplayName("대학원생이 멘토링을 수락한다.") + void updateSeniorMentoringExpected() throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date1,date2,date3", 40, Status.WAITING, now(), now()); + mentoringRepository.save(mentoring); + + String request = objectMapper.writeValueAsString(new MentoringDateRequest("date1")); + mvc.perform(patch("/mentoring/senior/me/{mentoringId}/expected", mentoring.getMentoringId()) + .header(AUTHORIZATION, BEARER + seniorAccessToken) + .content(request) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_UPDATE.getCode())) + .andExpect(jsonPath("$.message").value(UPDATE_MENTORING.getMessage())); + } + + @ParameterizedTest + @EnumSource(value = Status.class, names = {"EXPECTED", "DONE", "CANCEL", "REFUSE"}) + @DisplayName("멘토링이 확정대기 상태가 아니라면 수락할 수 없다.") + void updateSeniorMentoringExpectedWithoutWaiting(Status status) throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date1,date2,date3", 40, status, now(), now()); + mentoringRepository.save(mentoring); + + String request = objectMapper.writeValueAsString(new MentoringDateRequest("date1")); + mvc.perform(patch("/mentoring/senior/me/{mentoringId}/expected", mentoring.getMentoringId()) + .header(AUTHORIZATION, BEARER + seniorAccessToken) + .content(request) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_NOT_WAITING.getCode())) + .andExpect(jsonPath("$.message").value(NOT_WAITING_MENTORING.getMessage())); + } + + @Test + @DisplayName("대학원생이 멘토링을 거절한다.") + void updateSeniorMentoringRefuse() throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date1,date2,date3", 40, Status.WAITING, now(), now()); + mentoringRepository.save(mentoring); + + String request = objectMapper.writeValueAsString(new MentoringRefuseRequest("reason")); + mvc.perform(patch("/mentoring/senior/me/{mentoringId}/refuse", mentoring.getMentoringId()) + .header(AUTHORIZATION, BEARER + seniorAccessToken) + .content(request) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_UPDATE.getCode())) + .andExpect(jsonPath("$.message").value(UPDATE_MENTORING.getMessage())); + } + + @ParameterizedTest + @EnumSource(value = Status.class, names = {"EXPECTED", "DONE", "CANCEL", "REFUSE"}) + @DisplayName("멘토링이 확정대기 상태가 아니라면 거절할 수 없다.") + void updateSeniorMentoringRefuseWithoutWaiting(Status status) throws Exception { + Mentoring mentoring = new Mentoring(0L, user, senior, "topic", "question", "date", 40, status, now(), now()); + mentoringRepository.save(mentoring); + + String request = objectMapper.writeValueAsString(new MentoringRefuseRequest("reason")); + mvc.perform(patch("/mentoring/senior/me/{mentoringId}/refuse", mentoring.getMentoringId()) + .header(AUTHORIZATION, BEARER + seniorAccessToken) + .content(request) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(MENTORING_NOT_WAITING.getCode())) + .andExpect(jsonPath("$.message").value(NOT_WAITING_MENTORING.getMessage())); + } +} \ No newline at end of file