diff --git a/src/main/java/com/flytrap/venusplanner/VenusPlannerApplication.java b/src/main/java/com/flytrap/venusplanner/VenusPlannerApplication.java index 58ce6d9..f68c37f 100644 --- a/src/main/java/com/flytrap/venusplanner/VenusPlannerApplication.java +++ b/src/main/java/com/flytrap/venusplanner/VenusPlannerApplication.java @@ -3,11 +3,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication @ConfigurationPropertiesScan -@EnableJpaAuditing public class VenusPlannerApplication { public static void main(String[] args) { diff --git a/src/test/java/com/flytrap/venusplanner/acceptance/AcceptanceTest.java b/src/test/java/com/flytrap/venusplanner/acceptance/AcceptanceTest.java new file mode 100644 index 0000000..2a703ff --- /dev/null +++ b/src/test/java/com/flytrap/venusplanner/acceptance/AcceptanceTest.java @@ -0,0 +1,91 @@ +package com.flytrap.venusplanner.acceptance; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.flytrap.venusplanner.acceptance.common.SessionCookie; +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import io.restassured.specification.RequestSpecification; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.jdbc.Sql; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(properties = {"auth.session.sessionName=login_session::"}) +@Sql("classpath:reset.sql") +public abstract class AcceptanceTest { + + @LocalServerPort + private int port; + + @Value("${server.servlet.session.cookie.name}") + protected String sessionCookieName; + + static { + GenericContainer redis = + new GenericContainer<>(DockerImageName.parse("redis:latest")).withExposedPorts(6379); + redis.start(); + System.setProperty("spring.data.redis.host", redis.getHost()); + System.setProperty("spring.data.redis.port", redis.getMappedPort(6379).toString()); + } + + @BeforeAll + void setUp() { + RestAssured.port = port; + } + + public SessionCookie 테스트_로그인(Long memberId) { + String sessionCookieValue = givenJsonRequest() + .body(memberId) + .when().post("/api/v1/test/sign-in") + .then().extract() + .cookie(sessionCookieName); + + return new SessionCookie(sessionCookieName, sessionCookieValue); + } + + protected static RequestSpecification givenJsonRequest() { + return RestAssured.given().log().all() + .accept(MediaType.APPLICATION_JSON_VALUE) + .contentType(MediaType.APPLICATION_JSON_VALUE); + } + + protected void 응답_코드는_200_OK_를_반환한다(ExtractableResponse response) { + 응답_상태코드_검증(response, HttpStatus.OK); + } + + protected void 응답_코드는_201_CREATED_를_반환한다(ExtractableResponse response) { + 응답_상태코드_검증(response, HttpStatus.CREATED); + } + + protected void 응답_코드는_400_BAD_REQUEST_를_반환한다(ExtractableResponse response) { + 응답_상태코드_검증(response, HttpStatus.BAD_REQUEST); + } + + protected void 응답_코드는_403_FORBIDDEN_를_반환한다(ExtractableResponse response) { + 응답_상태코드_검증(response, HttpStatus.FORBIDDEN); + } + + protected void 응답_코드는_404_NOT_FOUND_를_반환한다(ExtractableResponse response) { + 응답_상태코드_검증(response, HttpStatus.NOT_FOUND); + } + + protected void 응답_코드는_409_CONFLICT_를_반환한다(ExtractableResponse response) { + 응답_상태코드_검증(response, HttpStatus.CONFLICT); + } + + private void 응답_상태코드_검증(ExtractableResponse response, HttpStatus httpStatus) { + assertThat(response.statusCode()).isEqualTo(httpStatus.value()); + } +} diff --git a/src/test/java/com/flytrap/venusplanner/acceptance/common/SessionCookie.java b/src/test/java/com/flytrap/venusplanner/acceptance/common/SessionCookie.java new file mode 100644 index 0000000..e2bed4c --- /dev/null +++ b/src/test/java/com/flytrap/venusplanner/acceptance/common/SessionCookie.java @@ -0,0 +1,7 @@ +package com.flytrap.venusplanner.acceptance.common; + +public record SessionCookie( + String name, + String value +) { +} diff --git a/src/test/java/com/flytrap/venusplanner/acceptance/common/TestAuthMemberController.java b/src/test/java/com/flytrap/venusplanner/acceptance/common/TestAuthMemberController.java new file mode 100644 index 0000000..bf695b5 --- /dev/null +++ b/src/test/java/com/flytrap/venusplanner/acceptance/common/TestAuthMemberController.java @@ -0,0 +1,20 @@ +package com.flytrap.venusplanner.acceptance.common; + +import com.flytrap.venusplanner.global.auth.dto.SessionMember; +import jakarta.servlet.http.HttpSession; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class TestAuthMemberController { + + @Value("${auth.session.sessionName}") + private String sessionName; + + @PostMapping("/api/v1/test/sign-in") + public void signIn(@RequestBody Long memberId, HttpSession session) { + session.setAttribute(sessionName, new SessionMember(memberId)); + } +} diff --git a/src/test/java/com/flytrap/venusplanner/acceptance/join_request/fixture/JoinRequestFixture.java b/src/test/java/com/flytrap/venusplanner/acceptance/join_request/fixture/JoinRequestFixture.java new file mode 100644 index 0000000..ca770c5 --- /dev/null +++ b/src/test/java/com/flytrap/venusplanner/acceptance/join_request/fixture/JoinRequestFixture.java @@ -0,0 +1,74 @@ +package com.flytrap.venusplanner.acceptance.join_request.fixture; + +import com.flytrap.venusplanner.api.access.domain.Permission; +import com.flytrap.venusplanner.api.access.domain.Roll; +import com.flytrap.venusplanner.api.join_request.domain.JoinRequest; +import com.flytrap.venusplanner.api.join_request.domain.JoinRequestState; +import com.flytrap.venusplanner.api.member.domain.Member; +import com.flytrap.venusplanner.api.member.domain.OAuthPlatformType; +import com.flytrap.venusplanner.api.member_study.domain.MemberStudy; +import com.flytrap.venusplanner.api.study.domain.Study; + +public class JoinRequestFixture { + + public static Member 리더() { + return Member.builder() + .oauthPk("00000") + .oauthPlatformId(OAuthPlatformType.GITHUB.getId()) + .email("leader@venus.test") + .nickname("leader") + .profileImageUrl("https://test.com") + .build(); + } + + public static Member 멤버_01() { + return Member.builder() + .oauthPk("11111") + .oauthPlatformId(OAuthPlatformType.GITHUB.getId()) + .email("member01@venus.test") + .nickname("member01") + .profileImageUrl("https://test.com") + .build(); + } + + public static Study 알고리즘_스터디() { + return new Study("알고리즘 챌린지", "알고리즘 실력을 쌓아갑니다."); + } + + public static Study 파이썬_스터디() { + return new Study("Python 스터디", "최신 Python 트렌드를 공부하며 프로젝트를 함께 진행합니다."); + } + + public static MemberStudy 리더_멤버_스터디(Study study, Member leader) { + return MemberStudy.fromLeader(leader.getId(), study); + } + + public static MemberStudy 멤버_스터디(Study study, Member member) { + return MemberStudy.builder() + .memberId(member.getId()) + .study(study) + .rollId(Roll.MEMBER.getId()) + .permissionId(Permission.NONE.getId()) + .build(); + } + + public static JoinRequest 신규_가입_요청(Study study, Member member) { + return JoinRequest.create(study.getId(), member.getId()); + } + + public static JoinRequest 수락된_가입_요청(Study study, Member member) { + return JoinRequest.builder() + .studyId(study.getId()) + .memberId(member.getId()) + .state(JoinRequestState.ACCEPT) + .build(); + } + + public static JoinRequest 거절된_가입_요청(Study study, Member member) { + return JoinRequest.builder() + .studyId(study.getId()) + .memberId(member.getId()) + .state(JoinRequestState.REJECT) + .build(); + } +} diff --git a/src/test/java/com/flytrap/venusplanner/acceptance/join_request/step/JoinRequestStep.java b/src/test/java/com/flytrap/venusplanner/acceptance/join_request/step/JoinRequestStep.java new file mode 100644 index 0000000..5a868d2 --- /dev/null +++ b/src/test/java/com/flytrap/venusplanner/acceptance/join_request/step/JoinRequestStep.java @@ -0,0 +1,31 @@ +package com.flytrap.venusplanner.acceptance.join_request.step; + +import com.flytrap.venusplanner.acceptance.AcceptanceTest; +import com.flytrap.venusplanner.acceptance.common.SessionCookie; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; + +public class JoinRequestStep extends AcceptanceTest { + + public static ExtractableResponse 스터디_가입_요청(Long studyId, SessionCookie sessionCookie) { + return givenJsonRequest() + .cookie(sessionCookie.name(), sessionCookie.value()) + .when().post("/api/v1/studies/{studyId}/join-requests", studyId) + .then().log().all().extract(); + } + + public static ExtractableResponse 스터디_가입_요청_수락(Long studyId, Long requestId, SessionCookie sessionCookie) { + return givenJsonRequest() + .cookie(sessionCookie.name(), sessionCookie.value()) + .when().patch("/api/v1/studies/{studyId}/join-requests/{requestId}/accept", studyId, requestId) + .then().log().all().extract(); + } + + public static ExtractableResponse 스터디_가입_요청_거절(Long studyId, Long requestId, SessionCookie sessionCookie) { + return givenJsonRequest() + .cookie(sessionCookie.name(), sessionCookie.value()) + .when().patch("/api/v1/studies/{studyId}/join-requests/{requestId}/reject", studyId, requestId) + .then().log().all().extract(); + } + +} diff --git a/src/test/java/com/flytrap/venusplanner/acceptance/join_request/test/AcceptJoinRequestTest.java b/src/test/java/com/flytrap/venusplanner/acceptance/join_request/test/AcceptJoinRequestTest.java new file mode 100644 index 0000000..48a04a2 --- /dev/null +++ b/src/test/java/com/flytrap/venusplanner/acceptance/join_request/test/AcceptJoinRequestTest.java @@ -0,0 +1,146 @@ +package com.flytrap.venusplanner.acceptance.join_request.test; + +import static com.flytrap.venusplanner.acceptance.join_request.step.JoinRequestStep.스터디_가입_요청_수락; +import static com.flytrap.venusplanner.global.fixture.JoinRequestFixture.거절된_가입_요청; +import static com.flytrap.venusplanner.global.fixture.JoinRequestFixture.수락된_가입_요청; +import static com.flytrap.venusplanner.global.fixture.JoinRequestFixture.신규_가입_요청; +import static com.flytrap.venusplanner.global.fixture.MemberFixture.리더; +import static com.flytrap.venusplanner.global.fixture.MemberFixture.멤버_01; +import static com.flytrap.venusplanner.global.fixture.MemberStudyFixture.리더_멤버_스터디; +import static com.flytrap.venusplanner.global.fixture.StudyFixture.알고리즘_스터디; +import static com.flytrap.venusplanner.global.fixture.StudyFixture.파이썬_스터디; +import static org.assertj.core.api.Assertions.assertThat; + +import com.flytrap.venusplanner.acceptance.AcceptanceTest; +import com.flytrap.venusplanner.api.join_request.domain.JoinRequest; +import com.flytrap.venusplanner.api.join_request.domain.JoinRequestState; +import com.flytrap.venusplanner.api.join_request.infrastructure.repository.JoinRequestRepository; +import com.flytrap.venusplanner.api.member.infrastructure.repository.MemberRepository; +import com.flytrap.venusplanner.api.member_study.infrastructure.repository.MemberStudyRepository; +import com.flytrap.venusplanner.api.study.infrastructure.repository.StudyRepository; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@DisplayName("[인수테스트] 스터디 가입 요청 수락 케이스") +public class AcceptJoinRequestTest extends AcceptanceTest { + + @Autowired + MemberRepository memberRepository; + + @Autowired + StudyRepository studyRepository; + + @Autowired + MemberStudyRepository memberStudyRepository; + + @Autowired + JoinRequestRepository joinRequestRepository; + + @Test + void 스터디_리더는_스터디_가입_요청을_수락_할_수_있다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var study = studyRepository.save(알고리즘_스터디()); + var leaderMemberStudy = memberStudyRepository.save(리더_멤버_스터디(study, leader)); + var joinRequest = joinRequestRepository.save(신규_가입_요청(study, member)); + var sessionCookie = 테스트_로그인(leader.getId()); + + // when + var response = 스터디_가입_요청_수락( + study.getId(), joinRequest.getId(), sessionCookie); + + // then + 응답_코드는_200_OK_를_반환한다(response); + 응답_바디로_빈_응답을_반환한다(response); + 스터디_가입_요청이_수락_되었는지_검증(joinRequest); + } + + @Test + void 스터디_리더가_아닌_멤버는_스터디_가입_요청을_수락_할_수_없다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var study = studyRepository.save(알고리즘_스터디()); + var leaderMemberStudy = memberStudyRepository.save(리더_멤버_스터디(study, leader)); + var joinRequest = joinRequestRepository.save(신규_가입_요청(study, member)); + var sessionCookie = 테스트_로그인(member.getId()); + + // when + var response = 스터디_가입_요청_수락( + study.getId(), joinRequest.getId(), sessionCookie); + + // then + 응답_코드는_403_FORBIDDEN_를_반환한다(response); + } + + @Test + void 이미_수락된_스터디_가입_요청은_수락_할_수_없다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var study = studyRepository.save(알고리즘_스터디()); + var leaderMemberStudy = memberStudyRepository.save(리더_멤버_스터디(study, leader)); + var joinRequest = joinRequestRepository.save(수락된_가입_요청(study, member)); + var sessionCookie = 테스트_로그인(leader.getId()); + + // when + var response = 스터디_가입_요청_수락( + study.getId(), joinRequest.getId(), sessionCookie); + + // then + 응답_코드는_409_CONFLICT_를_반환한다(response); + } + + @Test + void 이미_거절된_스터디_가입_요청은_수락_할_수_없다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var study = studyRepository.save(알고리즘_스터디()); + var leaderMemberStudy = memberStudyRepository.save(리더_멤버_스터디(study, leader)); + var joinRequest = joinRequestRepository.save(거절된_가입_요청(study, member)); + var sessionCookie = 테스트_로그인(leader.getId()); + + // when + var response = 스터디_가입_요청_수락( + study.getId(), joinRequest.getId(), sessionCookie); + + // then + 응답_코드는_409_CONFLICT_를_반환한다(response); + } + + @Test + void 가입_요청시_URL의_JOIN_REQUEST가_STUDY에_포함되지_않으면_가입_요청은_수락_할_수_없다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var algorithmStudy = studyRepository.save(알고리즘_스터디()); + var pythonStudy = studyRepository.save(파이썬_스터디()); + var algorithmMemberStudyLeader = memberStudyRepository.save(리더_멤버_스터디(algorithmStudy, leader)); + var pythonMemberStudyLeader = memberStudyRepository.save(리더_멤버_스터디(pythonStudy, leader)); + var joinRequest = joinRequestRepository.save(신규_가입_요청(algorithmStudy, member)); + var sessionCookie = 테스트_로그인(leader.getId()); + + // when + var response = 스터디_가입_요청_수락( + pythonStudy.getId(), joinRequest.getId(), sessionCookie); + + // then + 응답_코드는_400_BAD_REQUEST_를_반환한다(response); + } + + private void 응답_바디로_빈_응답을_반환한다(ExtractableResponse response) { + assertThat(response.body().asString()).isEmpty(); + } + + private void 스터디_가입_요청이_수락_되었는지_검증(JoinRequest joinRequest) { + var created = joinRequestRepository.findById(joinRequest.getId()).get(); + + assertThat(created.getState()).isEqualTo(JoinRequestState.ACCEPT); + } + +} diff --git a/src/test/java/com/flytrap/venusplanner/acceptance/join_request/test/CreateJoinRequestTest.java b/src/test/java/com/flytrap/venusplanner/acceptance/join_request/test/CreateJoinRequestTest.java new file mode 100644 index 0000000..3fb8c7e --- /dev/null +++ b/src/test/java/com/flytrap/venusplanner/acceptance/join_request/test/CreateJoinRequestTest.java @@ -0,0 +1,125 @@ +package com.flytrap.venusplanner.acceptance.join_request.test; + +import static com.flytrap.venusplanner.acceptance.join_request.step.JoinRequestStep.스터디_가입_요청; +import static com.flytrap.venusplanner.global.fixture.JoinRequestFixture.수락된_가입_요청; +import static com.flytrap.venusplanner.global.fixture.JoinRequestFixture.신규_가입_요청; +import static com.flytrap.venusplanner.global.fixture.MemberFixture.리더; +import static com.flytrap.venusplanner.global.fixture.MemberFixture.멤버_01; +import static com.flytrap.venusplanner.global.fixture.MemberStudyFixture.리더_멤버_스터디; +import static com.flytrap.venusplanner.global.fixture.MemberStudyFixture.멤버_스터디; +import static com.flytrap.venusplanner.global.fixture.StudyFixture.알고리즘_스터디; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.flytrap.venusplanner.acceptance.AcceptanceTest; +import com.flytrap.venusplanner.api.join_request.domain.JoinRequestState; +import com.flytrap.venusplanner.api.join_request.infrastructure.repository.JoinRequestRepository; +import com.flytrap.venusplanner.api.member.domain.Member; +import com.flytrap.venusplanner.api.member.infrastructure.repository.MemberRepository; +import com.flytrap.venusplanner.api.member_study.infrastructure.repository.MemberStudyRepository; +import com.flytrap.venusplanner.api.study.domain.Study; +import com.flytrap.venusplanner.api.study.infrastructure.repository.StudyRepository; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@DisplayName("[인수테스트] 스터디 가입 요청 케이스") +public class CreateJoinRequestTest extends AcceptanceTest { + + @Autowired + MemberRepository memberRepository; + + @Autowired + StudyRepository studyRepository; + + @Autowired + MemberStudyRepository memberStudyRepository; + + @Autowired + JoinRequestRepository joinRequestRepository; + + @Test + void 유저는_스터디에_가입_요청을_할_수_있다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var study = studyRepository.save(알고리즘_스터디()); + var leaderMemberStudy = memberStudyRepository.save(리더_멤버_스터디(study, leader)); + var sessionCookie = 테스트_로그인(member.getId()); + + // when + var response = 스터디_가입_요청(study.getId(), sessionCookie); + + // then + 응답_코드는_201_CREATED_를_반환한다(response); + 응답_바디로_생성된_가입_요청의_ID를_반환한다(response); + 스터디_가입_요청이_생성되었는지_검증(response, study, member); + } + + @Test + void 존재하지_않는_스터디에_가입_요청을_보낼_수_없다() { + // given + var member = memberRepository.save(멤버_01()); + var sessionCookie = 테스트_로그인(member.getId()); + + // when + var response = 스터디_가입_요청(999L, sessionCookie); + + // then + 응답_코드는_404_NOT_FOUND_를_반환한다(response); + } + + @Test + void 이미_가입_요청된_상태의_스터디에_가입_요청을_보낼_수_없다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var study = studyRepository.save(알고리즘_스터디()); + var leaderMemberStudy = memberStudyRepository.save(리더_멤버_스터디(study, leader)); + var joinRequest = joinRequestRepository.save(신규_가입_요청(study, member)); + var sessionCookie = 테스트_로그인(member.getId()); + + // when + var response = 스터디_가입_요청(study.getId(), sessionCookie); + + // then + 응답_코드는_409_CONFLICT_를_반환한다(response); + } + + @Test + void 이미_가입된_스터디에_가입_요청을_보낼_수_없다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var study = studyRepository.save(알고리즘_스터디()); + var leaderMemberStudy = memberStudyRepository.save(리더_멤버_스터디(study, leader)); + var memberStudy = memberStudyRepository.save(멤버_스터디(study, member)); + var joinRequest = joinRequestRepository.save(수락된_가입_요청(study, member)); + var sessionCookie = 테스트_로그인(member.getId()); + + // when + var response = 스터디_가입_요청(study.getId(), sessionCookie); + + // then + 응답_코드는_409_CONFLICT_를_반환한다(response); + } + + private void 응답_바디로_생성된_가입_요청의_ID를_반환한다(ExtractableResponse response) { + assertThat(response.body().jsonPath().getLong("id")) + .isNotNull(); + } + + private void 스터디_가입_요청이_생성되었는지_검증(ExtractableResponse response, Study study, Member member) { + var createdId = response.body().jsonPath().getLong("id"); + var created = joinRequestRepository.findById(createdId).get(); + + assertAll( + () -> assertThat(created.getStudyId()).isEqualTo(study.getId()), + () -> assertThat(created.getMemberId()).isEqualTo(member.getId()), + () -> assertThat(created.getState()).isEqualTo(JoinRequestState.WAIT) + ); + } + +} diff --git a/src/test/java/com/flytrap/venusplanner/acceptance/join_request/test/RejectJoinRequestTest.java b/src/test/java/com/flytrap/venusplanner/acceptance/join_request/test/RejectJoinRequestTest.java new file mode 100644 index 0000000..67d0183 --- /dev/null +++ b/src/test/java/com/flytrap/venusplanner/acceptance/join_request/test/RejectJoinRequestTest.java @@ -0,0 +1,146 @@ +package com.flytrap.venusplanner.acceptance.join_request.test; + +import static com.flytrap.venusplanner.acceptance.join_request.step.JoinRequestStep.스터디_가입_요청_거절; +import static com.flytrap.venusplanner.global.fixture.JoinRequestFixture.거절된_가입_요청; +import static com.flytrap.venusplanner.global.fixture.JoinRequestFixture.수락된_가입_요청; +import static com.flytrap.venusplanner.global.fixture.JoinRequestFixture.신규_가입_요청; +import static com.flytrap.venusplanner.global.fixture.MemberFixture.리더; +import static com.flytrap.venusplanner.global.fixture.MemberFixture.멤버_01; +import static com.flytrap.venusplanner.global.fixture.MemberStudyFixture.리더_멤버_스터디; +import static com.flytrap.venusplanner.global.fixture.StudyFixture.알고리즘_스터디; +import static com.flytrap.venusplanner.global.fixture.StudyFixture.파이썬_스터디; +import static org.assertj.core.api.Assertions.assertThat; + +import com.flytrap.venusplanner.acceptance.AcceptanceTest; +import com.flytrap.venusplanner.api.join_request.domain.JoinRequest; +import com.flytrap.venusplanner.api.join_request.domain.JoinRequestState; +import com.flytrap.venusplanner.api.join_request.infrastructure.repository.JoinRequestRepository; +import com.flytrap.venusplanner.api.member.infrastructure.repository.MemberRepository; +import com.flytrap.venusplanner.api.member_study.infrastructure.repository.MemberStudyRepository; +import com.flytrap.venusplanner.api.study.infrastructure.repository.StudyRepository; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@DisplayName("[인수테스트] 스터디 가입 요청 거절 케이스") +public class RejectJoinRequestTest extends AcceptanceTest { + + @Autowired + MemberRepository memberRepository; + + @Autowired + StudyRepository studyRepository; + + @Autowired + MemberStudyRepository memberStudyRepository; + + @Autowired + JoinRequestRepository joinRequestRepository; + + @Test + void 스터디_리더는_스터디_가입_요청을_거절_할_수_있다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var study = studyRepository.save(알고리즘_스터디()); + var leaderMemberStudy = memberStudyRepository.save(리더_멤버_스터디(study, leader)); + var joinRequest = joinRequestRepository.save(신규_가입_요청(study, member)); + var sessionCookie = 테스트_로그인(leader.getId()); + + // when + var response = 스터디_가입_요청_거절( + study.getId(), joinRequest.getId(), sessionCookie); + + // then + 응답_코드는_200_OK_를_반환한다(response); + 응답_바디로_빈_응답을_반환한다(response); + 스터디_가입_요청이_거절_되었는지_검증(joinRequest); + } + + @Test + void 스터디_리더가_아닌_멤버는_스터디_가입_요청을_거절_할_수_없다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var study = studyRepository.save(알고리즘_스터디()); + var leaderMemberStudy = memberStudyRepository.save(리더_멤버_스터디(study, leader)); + var joinRequest = joinRequestRepository.save(신규_가입_요청(study, member)); + var sessionCookie = 테스트_로그인(member.getId()); + + // when + var response = 스터디_가입_요청_거절( + study.getId(), joinRequest.getId(), sessionCookie); + + // then + 응답_코드는_403_FORBIDDEN_를_반환한다(response); + } + + @Test + void 이미_수락된_스터디_가입_요청은_거절_할_수_없다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var study = studyRepository.save(알고리즘_스터디()); + var leaderMemberStudy = memberStudyRepository.save(리더_멤버_스터디(study, leader)); + var joinRequest = joinRequestRepository.save(수락된_가입_요청(study, member)); + var sessionCookie = 테스트_로그인(leader.getId()); + + // when + var response = 스터디_가입_요청_거절( + study.getId(), joinRequest.getId(), sessionCookie); + + // then + 응답_코드는_409_CONFLICT_를_반환한다(response); + } + + @Test + void 이미_거절된_스터디_가입_요청은_거절_할_수_없다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var study = studyRepository.save(알고리즘_스터디()); + var leaderMemberStudy = memberStudyRepository.save(리더_멤버_스터디(study, leader)); + var joinRequest = joinRequestRepository.save(거절된_가입_요청(study, member)); + var sessionCookie = 테스트_로그인(leader.getId()); + + // when + var response = 스터디_가입_요청_거절( + study.getId(), joinRequest.getId(), sessionCookie); + + // then + 응답_코드는_409_CONFLICT_를_반환한다(response); + } + + @Test + void 가입_요청시_URL의_JOIN_REQUEST가_STUDY에_포함되지_않으면_가입_요청은_거절_할_수_없다() { + // given + var leader = memberRepository.save(리더()); + var member = memberRepository.save(멤버_01()); + var algorithmStudy = studyRepository.save(알고리즘_스터디()); + var pythonStudy = studyRepository.save(파이썬_스터디()); + var algorithmMemberStudyLeader = memberStudyRepository.save(리더_멤버_스터디(algorithmStudy, leader)); + var pythonMemberStudyLeader = memberStudyRepository.save(리더_멤버_스터디(pythonStudy, leader)); + var joinRequest = joinRequestRepository.save(신규_가입_요청(algorithmStudy, member)); + var sessionCookie = 테스트_로그인(leader.getId()); + + // when + var response = 스터디_가입_요청_거절( + pythonStudy.getId(), joinRequest.getId(), sessionCookie); + + // then + 응답_코드는_400_BAD_REQUEST_를_반환한다(response); + } + + private void 응답_바디로_빈_응답을_반환한다(ExtractableResponse response) { + assertThat(response.body().asString()).isEmpty(); + } + + private void 스터디_가입_요청이_거절_되었는지_검증(JoinRequest joinRequest) { + var created = joinRequestRepository.findById(joinRequest.getId()).get(); + + assertThat(created.getState()).isEqualTo(JoinRequestState.REJECT); + } + +} diff --git a/src/test/java/com/flytrap/venusplanner/global/AcceptanceTest.java b/src/test/java/com/flytrap/venusplanner/global/AcceptanceTest.java deleted file mode 100644 index 5d4267a..0000000 --- a/src/test/java/com/flytrap/venusplanner/global/AcceptanceTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.flytrap.venusplanner.global; - -import static org.assertj.core.api.Assertions.assertThat; - -import io.restassured.RestAssured; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import io.restassured.specification.RequestSpecification; -import org.junit.jupiter.api.BeforeEach; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.test.context.jdbc.Sql; - -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -@Sql("classpath:reset.sql") -public abstract class AcceptanceTest { - - @LocalServerPort - private int port; - - @BeforeEach - void setUp() { - RestAssured.port = port; - } - - // TODO:로그인 로직 - - protected static RequestSpecification givenJsonRequest() { - return RestAssured.given().log().all() - .accept(MediaType.APPLICATION_JSON_VALUE) - .contentType(MediaType.APPLICATION_JSON_VALUE); - } - - protected void 응답_상태코드_검증(ExtractableResponse response, HttpStatus httpStatus) { - assertThat(response.statusCode()).isEqualTo(httpStatus.value()); - } -} diff --git a/src/test/java/com/flytrap/venusplanner/global/fixture/JoinRequestFixture.java b/src/test/java/com/flytrap/venusplanner/global/fixture/JoinRequestFixture.java new file mode 100644 index 0000000..70fe851 --- /dev/null +++ b/src/test/java/com/flytrap/venusplanner/global/fixture/JoinRequestFixture.java @@ -0,0 +1,29 @@ +package com.flytrap.venusplanner.global.fixture; + +import com.flytrap.venusplanner.api.join_request.domain.JoinRequest; +import com.flytrap.venusplanner.api.join_request.domain.JoinRequestState; +import com.flytrap.venusplanner.api.member.domain.Member; +import com.flytrap.venusplanner.api.study.domain.Study; + +public class JoinRequestFixture { + + public static JoinRequest 신규_가입_요청(Study study, Member member) { + return JoinRequest.create(study.getId(), member.getId()); + } + + public static JoinRequest 수락된_가입_요청(Study study, Member member) { + return JoinRequest.builder() + .studyId(study.getId()) + .memberId(member.getId()) + .state(JoinRequestState.ACCEPT) + .build(); + } + + public static JoinRequest 거절된_가입_요청(Study study, Member member) { + return JoinRequest.builder() + .studyId(study.getId()) + .memberId(member.getId()) + .state(JoinRequestState.REJECT) + .build(); + } +} diff --git a/src/test/java/com/flytrap/venusplanner/global/fixture/MemberFixture.java b/src/test/java/com/flytrap/venusplanner/global/fixture/MemberFixture.java new file mode 100644 index 0000000..83dcc50 --- /dev/null +++ b/src/test/java/com/flytrap/venusplanner/global/fixture/MemberFixture.java @@ -0,0 +1,27 @@ +package com.flytrap.venusplanner.global.fixture; + +import com.flytrap.venusplanner.api.member.domain.Member; +import com.flytrap.venusplanner.api.member.domain.OAuthPlatformType; + +public class MemberFixture { + + public static Member 리더() { + return Member.builder() + .oauthPk("00000") + .oauthPlatformId(OAuthPlatformType.GITHUB.getId()) + .email("leader@venus.test") + .nickname("leader") + .profileImageUrl("https://test.com") + .build(); + } + + public static Member 멤버_01() { + return Member.builder() + .oauthPk("11111") + .oauthPlatformId(OAuthPlatformType.GITHUB.getId()) + .email("member01@venus.test") + .nickname("member01") + .profileImageUrl("https://test.com") + .build(); + } +} diff --git a/src/test/java/com/flytrap/venusplanner/global/fixture/MemberStudyFixture.java b/src/test/java/com/flytrap/venusplanner/global/fixture/MemberStudyFixture.java new file mode 100644 index 0000000..3d783be --- /dev/null +++ b/src/test/java/com/flytrap/venusplanner/global/fixture/MemberStudyFixture.java @@ -0,0 +1,22 @@ +package com.flytrap.venusplanner.global.fixture; + +import com.flytrap.venusplanner.api.access.domain.Permission; +import com.flytrap.venusplanner.api.access.domain.Roll; +import com.flytrap.venusplanner.api.member.domain.Member; +import com.flytrap.venusplanner.api.member_study.domain.MemberStudy; +import com.flytrap.venusplanner.api.study.domain.Study; + +public class MemberStudyFixture { + public static MemberStudy 리더_멤버_스터디(Study study, Member leader) { + return MemberStudy.fromLeader(leader.getId(), study); + } + + public static MemberStudy 멤버_스터디(Study study, Member member) { + return MemberStudy.builder() + .memberId(member.getId()) + .study(study) + .rollId(Roll.MEMBER.getId()) + .permissionId(Permission.NONE.getId()) + .build(); + } +} diff --git a/src/test/java/com/flytrap/venusplanner/global/fixture/StudyFixture.java b/src/test/java/com/flytrap/venusplanner/global/fixture/StudyFixture.java new file mode 100644 index 0000000..76816f6 --- /dev/null +++ b/src/test/java/com/flytrap/venusplanner/global/fixture/StudyFixture.java @@ -0,0 +1,14 @@ +package com.flytrap.venusplanner.global.fixture; + +import com.flytrap.venusplanner.api.study.domain.Study; + +public class StudyFixture { + + public static Study 알고리즘_스터디() { + return new Study("알고리즘 챌린지", "알고리즘 실력을 쌓아갑니다."); + } + + public static Study 파이썬_스터디() { + return new Study("Python 스터디", "최신 Python 트렌드를 공부하며 프로젝트를 함께 진행합니다."); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 4abc536..cfe7ec4 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -1,3 +1,9 @@ +server: + servlet: + session: + cookie: + name: JSESSIONID + spring: datasource: driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver @@ -11,3 +17,7 @@ spring: show_sql: true format_sql: true highlight_sql: true + +auth: + session: + session-name: "login_session::" \ No newline at end of file diff --git a/src/test/resources/reset.sql b/src/test/resources/reset.sql index 88c0ad3..ffa2826 100644 --- a/src/test/resources/reset.sql +++ b/src/test/resources/reset.sql @@ -11,3 +11,5 @@ TRUNCATE TABLE plan; TRUNCATE TABLE plan_category; TRUNCATE TABLE recurring_option; + +TRUNCATE TABLE join_request;