Skip to content
This repository was archived by the owner on Jan 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ public enum ErrorStatus implements BaseErrorCode {
_ONLY_STUDY_MEMBER_CAN_ACCESS_SCHEDULE(HttpStatus.FORBIDDEN, "STUDY4015", "스터디 멤버만 일정에 접근할 수 있습니다."),
_ONLY_STUDY_MEMBER_CAN_ACCESS_MEMBERS(HttpStatus.FORBIDDEN, "STUDY4016", "스터디 멤버만 회원 목록에 접근할 수 있습니다."),
_ALREADY_STUDY_MEMBER(HttpStatus.BAD_REQUEST, "STUDY4017", "이미 스터디 멤버입니다."),
_STUDY_OWNER_ONLY_CAN_TERMINATE(HttpStatus.BAD_REQUEST, "STUDY4018", "스터디장만 스터디를 종료할 수 있습니다."),
_STUDY_ALREADY_TERMINATED(HttpStatus.BAD_REQUEST, "STUDY4019", "이미 종료된 스터디입니다."),

//스터디 게시글 관련 에러
_STUDY_POST_NOT_FOUND(HttpStatus.NOT_FOUND, "POST4001", "스터디 게시글을 찾을 수 없습니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public StudyWithdrawalResponseDTO.WithdrawalDTO withdrawFromStudy(Long studyId)
.orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND));

// 참여가 승인되지 않은 스터디는 탈퇴할 수 없음
if (memberStudy.getStatus().equals(ApplicationStatus.APPLIED)) {
if (!memberStudy.getStatus().equals(ApplicationStatus.APPROVED)) {
throw new StudyHandler(ErrorStatus._STUDY_NOT_APPROVED);
}
// 스터디장은 스터디를 탈퇴할 수 없음
Expand All @@ -107,10 +107,24 @@ public StudyWithdrawalResponseDTO.WithdrawalDTO withdrawFromStudy(Long studyId)
*/
public StudyTerminationResponseDTO.TerminationDTO terminateStudy(Long studyId) {

// Authorization
Long memberId = SecurityUtils.getCurrentUserId();
memberRepository.findById(memberId)
.orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND));
Study study = studyRepository.findById(studyId)
.orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_NOT_FOUND));
MemberStudy memberStudy = memberStudyRepository.findByMemberIdAndStudyIdAndStatus(memberId, studyId, ApplicationStatus.APPROVED)
.orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_FOUND));

// 스터디장이 아니면 스터디를 종료할 수 없음
if (memberStudy.getIsOwned().equals(false)) {
throw new StudyHandler(ErrorStatus._STUDY_OWNER_ONLY_CAN_TERMINATE);
}

// 스터디장 확인 로직 빠짐
// 이미 종료된 스터디는 종료할 수 없음
if (study.getStatus().equals(Status.OFF)) {
throw new StudyHandler(ErrorStatus._STUDY_ALREADY_TERMINATED);
}

study.setStatus(Status.OFF);
studyRepository.save(study);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.spot.service.memberstudy;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
Expand All @@ -8,6 +9,8 @@

import com.example.spot.api.exception.handler.StudyHandler;
import com.example.spot.domain.Member;
import com.example.spot.domain.enums.ApplicationStatus;
import com.example.spot.domain.enums.Status;
import com.example.spot.domain.mapping.MemberStudy;
import com.example.spot.domain.study.Study;
import com.example.spot.domain.study.ToDoList;
Expand All @@ -21,6 +24,9 @@
import java.time.LocalDate;
import java.util.Collections;
import java.util.Optional;

import com.example.spot.web.dto.memberstudy.response.StudyTerminationResponseDTO;
import com.example.spot.web.dto.memberstudy.response.StudyWithdrawalResponseDTO;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
Expand Down Expand Up @@ -89,6 +95,199 @@ void init() {
SecurityContextHolder.setContext(securityContext);
}

/* ---------------------------- 진행중인 스터디 관련 메서드 ---------------------------- */

@Test
@DisplayName("스터디 탈퇴 - (성공)")
void withdrawFromStudy_Success() {

// given
member = Member.builder()
.id(1L)
.name("회원1")
.build();
study = Study.builder()
.id(1L)
.title("스터디")
.build();
memberStudy = MemberStudy.builder()
.member(member)
.study(study)
.isOwned(false)
.status(ApplicationStatus.APPROVED)
.build();

when(memberRepository.findById(1L)).thenReturn(Optional.of(member));
when(studyRepository.findById(1L)).thenReturn(Optional.of(study));
when(memberStudyRepository.findByMemberIdAndStudyIdAndStatus(1L, 1L, ApplicationStatus.APPROVED))
.thenReturn(Optional.of(memberStudy));

// when
StudyWithdrawalResponseDTO.WithdrawalDTO result = memberStudyCommandService.withdrawFromStudy(1L);

// then
assertNotNull(result);
assertThat(result.getStudyId()).isEqualTo(1L);
assertThat(result.getStudyName()).isEqualTo("스터디");
assertThat(result.getMemberId()).isEqualTo(1L);
assertThat(result.getMemberName()).isEqualTo("회원1");
}

@Test
@DisplayName("스터디 탈퇴 - 스터디 회원이 아닌 경우 (실패)")
void withdrawFromStudy_NotStudyMember_Fail() {

// given
member = Member.builder()
.id(1L)
.name("회원1")
.build();
study = Study.builder()
.id(1L)
.title("스터디")
.build();
memberStudy = MemberStudy.builder()
.member(member)
.study(study)
.isOwned(false)
.status(ApplicationStatus.APPLIED)
.build();

when(memberRepository.findById(1L)).thenReturn(Optional.of(member));
when(studyRepository.findById(1L)).thenReturn(Optional.of(study));
when(memberStudyRepository.findByMemberIdAndStudyIdAndStatus(1L, 1L, ApplicationStatus.APPROVED))
.thenReturn(Optional.empty());

// when & then
assertThrows(StudyHandler.class, () -> memberStudyCommandService.withdrawFromStudy(1L));
}

@Test
@DisplayName("스터디 탈퇴 - 스터디장인 경우 (실패)")
void withdrawFromStudy_StudyOwner_Fail() {

// given
member = Member.builder()
.id(1L)
.name("회원1")
.build();
study = Study.builder()
.id(1L)
.title("스터디")
.build();
memberStudy = MemberStudy.builder()
.member(member)
.study(study)
.isOwned(true)
.status(ApplicationStatus.APPROVED)
.build();

when(memberRepository.findById(1L)).thenReturn(Optional.of(member));
when(studyRepository.findById(1L)).thenReturn(Optional.of(study));
when(memberStudyRepository.findByMemberIdAndStudyIdAndStatus(1L, 1L, ApplicationStatus.APPROVED))
.thenReturn(Optional.of(memberStudy));

// when & then
assertThrows(StudyHandler.class, () -> memberStudyCommandService.withdrawFromStudy(1L));
}

@Test
@DisplayName("스터디 종료 - (성공)")
void terminateStudy_Success() {

// given
member = Member.builder()
.id(1L)
.name("회원1")
.build();
study = Study.builder()
.id(1L)
.title("스터디")
.status(Status.ON)
.build();
memberStudy = MemberStudy.builder()
.member(member)
.study(study)
.isOwned(true)
.status(ApplicationStatus.APPROVED)
.build();

when(memberRepository.findById(1L)).thenReturn(Optional.of(member));
when(studyRepository.findById(1L)).thenReturn(Optional.of(study));
when(memberStudyRepository.findByMemberIdAndStudyIdAndStatus(1L, 1L, ApplicationStatus.APPROVED))
.thenReturn(Optional.of(memberStudy));

// when
StudyTerminationResponseDTO.TerminationDTO result = memberStudyCommandService.terminateStudy(1L);

// then
assertNotNull(result);
assertThat(result.getStudyId()).isEqualTo(1L);
assertThat(result.getStudyName()).isEqualTo("스터디");
assertThat(result.getStatus()).isEqualTo(Status.OFF);
}

@Test
@DisplayName("스터디 종료 - 스터디장이 아닌 경우 (실패)")
void terminateStudy_NotStudyOwner_Fail() {

// given
member = Member.builder()
.id(1L)
.name("회원1")
.build();
study = Study.builder()
.id(1L)
.title("스터디")
.status(Status.ON)
.build();
memberStudy = MemberStudy.builder()
.member(member)
.study(study)
.isOwned(false)
.status(ApplicationStatus.APPROVED)
.build();

when(memberRepository.findById(1L)).thenReturn(Optional.of(member));
when(studyRepository.findById(1L)).thenReturn(Optional.of(study));
when(memberStudyRepository.findByMemberIdAndStudyIdAndStatus(1L, 1L, ApplicationStatus.APPROVED))
.thenReturn(Optional.of(memberStudy));

// when & then
assertThrows(StudyHandler.class, () -> memberStudyCommandService.terminateStudy(1L));
}

@Test
@DisplayName("스터디 종료 - 진행중인 스터디가 아닌 경우 (실패)")
void terminateStudy_AlreadyTerminated_Fail() {

// given
member = Member.builder()
.id(1L)
.name("회원1")
.build();
study = Study.builder()
.id(1L)
.title("스터디")
.status(Status.OFF)
.build();
memberStudy = MemberStudy.builder()
.member(member)
.study(study)
.isOwned(false)
.status(ApplicationStatus.APPROVED)
.build();

when(memberRepository.findById(1L)).thenReturn(Optional.of(member));
when(studyRepository.findById(1L)).thenReturn(Optional.of(study));
when(memberStudyRepository.findByMemberIdAndStudyIdAndStatus(1L, 1L, ApplicationStatus.APPROVED))
.thenReturn(Optional.of(memberStudy));

// when & then
assertThrows(StudyHandler.class, () -> memberStudyCommandService.terminateStudy(1L));
}


/* ---------------------------- To-Do 생성 관련 메서드 ---------------------------- */

@Test
Expand Down