diff --git a/src/main/java/com/somemore/domains/InterestCenter.java b/src/main/java/com/somemore/domains/InterestCenter.java deleted file mode 100644 index 4c35d5119..000000000 --- a/src/main/java/com/somemore/domains/InterestCenter.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.somemore.domains; - -import jakarta.persistence.*; -import lombok.Getter; -import lombok.Setter; - -import java.util.UUID; - -@Getter -@Setter -@Entity -@Table(name = "interest_center") -public class InterestCenter { - @Id - @GeneratedValue(strategy = GenerationType.UUID) - private UUID id; - -} \ No newline at end of file diff --git a/src/main/java/com/somemore/global/common/response/ApiResponse.java b/src/main/java/com/somemore/global/common/response/ApiResponse.java index ebdd60c3a..6b00a6c07 100644 --- a/src/main/java/com/somemore/global/common/response/ApiResponse.java +++ b/src/main/java/com/somemore/global/common/response/ApiResponse.java @@ -15,14 +15,10 @@ public static ApiResponse ok(int status, T data, String message) { return new ApiResponse<>(status, message, data); } - public static ApiResponse ok(String message) { + public static ApiResponse ok(String message) { return new ApiResponse<>(200, message, ""); } - public static ApiResponse error(int code, String message) { - return new ApiResponse<>(code, message, ""); - } - public ApiResponse(int code, String message, T data) { this.code = code; this.message = message; diff --git a/src/main/java/com/somemore/global/exception/DuplicateException.java b/src/main/java/com/somemore/global/exception/DuplicateException.java new file mode 100644 index 000000000..0a752c189 --- /dev/null +++ b/src/main/java/com/somemore/global/exception/DuplicateException.java @@ -0,0 +1,8 @@ +package com.somemore.global.exception; + +public class DuplicateException extends RuntimeException{ + + public DuplicateException(final String message) { + super(message); + } +} diff --git a/src/main/java/com/somemore/global/exception/ExceptionMessage.java b/src/main/java/com/somemore/global/exception/ExceptionMessage.java index 43a1be574..1aa5d8fdd 100644 --- a/src/main/java/com/somemore/global/exception/ExceptionMessage.java +++ b/src/main/java/com/somemore/global/exception/ExceptionMessage.java @@ -21,6 +21,8 @@ public enum ExceptionMessage { FILE_SIZE_EXCEEDED("파일 크기가 허용된 한도를 초과했습니다."), EMPTY_FILE("파일이 존재하지 않습니다."), INSTANTIATION_NOT_ALLOWED("인스턴스화 할 수 없는 클래스 입니다."), + CANNOT_CANCEL_DELETED_INTEREST_CENTER("이미 삭제된 관심 기관은 취소할 수 없습니다."), + DUPLICATE_INTEREST_CENTER("이미 관심 표시한 기관입니다.") ; private final String message; diff --git a/src/main/java/com/somemore/global/handler/GlobalExceptionHandler.java b/src/main/java/com/somemore/global/handler/GlobalExceptionHandler.java index 3f6cc0736..48fb90dfc 100644 --- a/src/main/java/com/somemore/global/handler/GlobalExceptionHandler.java +++ b/src/main/java/com/somemore/global/handler/GlobalExceptionHandler.java @@ -1,17 +1,14 @@ package com.somemore.global.handler; - import com.somemore.global.exception.BadRequestException; +import com.somemore.global.exception.DuplicateException; import com.somemore.global.exception.ImageUploadException; -import org.springframework.data.crossstore.ChangeSetPersister; import org.springframework.http.HttpStatus; import org.springframework.http.ProblemDetail; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; - @RestControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { @@ -39,4 +36,13 @@ ProblemDetail handleImageUploadException(final ImageUploadException e) { return problemDetail; } + @ExceptionHandler(DuplicateException.class) + ProblemDetail handleDuplicateException(final DuplicateException e) { + + ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, e.getMessage()); + problemDetail.setTitle("중복 예외"); + + return problemDetail; + } + } diff --git a/src/main/java/com/somemore/interestcenter/controller/InterestCenterCommandApiController.java b/src/main/java/com/somemore/interestcenter/controller/InterestCenterCommandApiController.java new file mode 100644 index 000000000..bcd3df349 --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/controller/InterestCenterCommandApiController.java @@ -0,0 +1,40 @@ +package com.somemore.interestcenter.controller; + +import com.somemore.global.common.response.ApiResponse; +import com.somemore.interestcenter.dto.request.RegisterInterestCenterRequestDto; +import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; +import com.somemore.interestcenter.usecase.CancelInterestCenterUseCase; +import com.somemore.interestcenter.usecase.RegisterInterestCenterUseCase; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RequiredArgsConstructor +@RestController +@Tag(name = "Interest Center Command API", description = "관심 기관의 등록과 취소 API를 제공합니다") +public class InterestCenterCommandApiController { + + private final RegisterInterestCenterUseCase registerInterestCenterUseCase; + private final CancelInterestCenterUseCase cancelInterestCenterUseCase; + + @Operation(summary = "관심기관 등록 API") + @PostMapping("/api/interest-center") + public ApiResponse registerInterestCenter(@Valid @RequestBody RegisterInterestCenterRequestDto requestDto) { + + RegisterInterestCenterResponseDto responseDto = registerInterestCenterUseCase.registerInterestCenter(requestDto); + + return ApiResponse.ok(200, responseDto, "관심 기관 등록 성공"); + } + + @Operation(summary = "관심기관 취소 API") + @DeleteMapping("/api/interest-center/{interest-center-id}") + public ApiResponse deleteInterestCenter(@PathVariable("interest-center-id") Long interestCenterId) { + + cancelInterestCenterUseCase.cancelInterestCenter(interestCenterId); + + return ApiResponse.ok("관심 기관 취소 성공"); + } + +} diff --git a/src/main/java/com/somemore/interestcenter/domain/InterestCenter.java b/src/main/java/com/somemore/interestcenter/domain/InterestCenter.java new file mode 100644 index 000000000..df1c045c7 --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/domain/InterestCenter.java @@ -0,0 +1,39 @@ +package com.somemore.interestcenter.domain; + +import com.somemore.global.common.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +import java.util.UUID; + +import static jakarta.persistence.GenerationType.IDENTITY; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +@Entity +@Table(name = "interest_center") +public class InterestCenter extends BaseEntity { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + @Column(name = "volunteer_id", nullable = false) + private UUID volunteerId; + + @Column(name = "center_id", nullable = false) + private UUID centerId; + + @Builder + private InterestCenter(UUID volunteerId, UUID centerId) { + this.volunteerId = volunteerId; + this.centerId = centerId; + } + + public static InterestCenter create(UUID volunteerId, UUID centerId) { + return InterestCenter.builder() + .volunteerId(volunteerId) + .centerId(centerId) + .build(); + } +} diff --git a/src/main/java/com/somemore/interestcenter/dto/request/RegisterInterestCenterRequestDto.java b/src/main/java/com/somemore/interestcenter/dto/request/RegisterInterestCenterRequestDto.java new file mode 100644 index 000000000..beb917c84 --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/dto/request/RegisterInterestCenterRequestDto.java @@ -0,0 +1,25 @@ +package com.somemore.interestcenter.dto.request; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.somemore.interestcenter.domain.InterestCenter; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; + +import java.util.UUID; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public record RegisterInterestCenterRequestDto( + + @Schema(description = "봉사자 ID", example = "123e4567-e89b-12d3-a456-426614174000") + @NotNull(message = "봉사자 ID는 필수값입니다.") + UUID volunteerId, + + @Schema(description = "기관 ID", example = "123e4567-e89b-12d3-a456-426614174000") + @NotNull(message = "기관 ID는 필수값입니다.") + UUID centerId +) { + public InterestCenter toEntity(){ + return InterestCenter.create(volunteerId, centerId); + } +} diff --git a/src/main/java/com/somemore/interestcenter/dto/response/RegisterInterestCenterResponseDto.java b/src/main/java/com/somemore/interestcenter/dto/response/RegisterInterestCenterResponseDto.java new file mode 100644 index 000000000..4a20fa957 --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/dto/response/RegisterInterestCenterResponseDto.java @@ -0,0 +1,30 @@ +package com.somemore.interestcenter.dto.response; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.somemore.interestcenter.domain.InterestCenter; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; + +import java.util.UUID; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@Builder +public record RegisterInterestCenterResponseDto( + @Schema(description = "관심 ID", example = "1111") + Long id, + + @Schema(description = "봉사자 ID", example = "123e4567-e89b-12d3-a456-426614174000") + UUID volunteerId, + + @Schema(description = "센터 ID", example = "123e4567-e89b-12d3-a456-426614174000") + UUID centerId +) { + public static RegisterInterestCenterResponseDto from(InterestCenter interestCenter) { + return RegisterInterestCenterResponseDto.builder() + .id(interestCenter.getId()) + .volunteerId(interestCenter.getVolunteerId()) + .centerId(interestCenter.getCenterId()) + .build(); + } +} diff --git a/src/main/java/com/somemore/interestcenter/repository/InterestCenterJpaRepository.java b/src/main/java/com/somemore/interestcenter/repository/InterestCenterJpaRepository.java new file mode 100644 index 000000000..34b9a37e1 --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/repository/InterestCenterJpaRepository.java @@ -0,0 +1,7 @@ +package com.somemore.interestcenter.repository; + +import com.somemore.interestcenter.domain.InterestCenter; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface InterestCenterJpaRepository extends JpaRepository { +} diff --git a/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepository.java b/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepository.java new file mode 100644 index 000000000..14b07b2d2 --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepository.java @@ -0,0 +1,14 @@ +package com.somemore.interestcenter.repository; + +import com.somemore.interestcenter.domain.InterestCenter; +import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; + +import java.util.Optional; +import java.util.UUID; + +public interface InterestCenterRepository { + InterestCenter save(InterestCenter interestCenter); + Optional findById(Long id); + Optional findInterestCenterResponseById(Long id); + boolean existsByVolunteerIdAndCenterId(UUID volunteerId, UUID centerId); +} diff --git a/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepositoryImpl.java b/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepositoryImpl.java new file mode 100644 index 000000000..5667967b8 --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/repository/InterestCenterRepositoryImpl.java @@ -0,0 +1,81 @@ +package com.somemore.interestcenter.repository; + +import com.querydsl.core.types.Projections; +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.somemore.interestcenter.domain.InterestCenter; +import com.somemore.interestcenter.domain.QInterestCenter; +import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; +import java.util.UUID; + +@RequiredArgsConstructor +@Repository +public class InterestCenterRepositoryImpl implements InterestCenterRepository { + + private final JPAQueryFactory queryFactory; + private final InterestCenterJpaRepository interestCenterJpaRepository; + + @Override + public InterestCenter save(InterestCenter interestCenter) { + return interestCenterJpaRepository.save(interestCenter); + } + + @Override + public Optional findById(Long id) { + QInterestCenter interestCenter = QInterestCenter.interestCenter; + + InterestCenter result = queryFactory + .selectFrom(interestCenter) + .where( + interestCenter.id.eq(id) + .and(interestCenter.deleted.eq(false)) + ) + .fetchOne(); + + return Optional.ofNullable(result); + } + + @Override + public Optional findInterestCenterResponseById(Long id) { + QInterestCenter interestCenter = QInterestCenter.interestCenter; + + RegisterInterestCenterResponseDto result = queryFactory + .select( + Projections.constructor( + RegisterInterestCenterResponseDto.class, + interestCenter.id, + interestCenter.volunteerId, + interestCenter.centerId + ) + ) + .from(interestCenter) + .where( + interestCenter.id.eq(id) + .and(interestCenter.deleted.eq(false)) + ) + .fetchOne(); + + return Optional.ofNullable(result); + } + + @Override + public boolean existsByVolunteerIdAndCenterId(UUID volunteerId, UUID centerId) { + QInterestCenter interestCenter = QInterestCenter.interestCenter; + + Integer result = queryFactory + .selectOne() + .from(interestCenter) + .where( + interestCenter.volunteerId.eq(volunteerId) + .and(interestCenter.centerId.eq(centerId)) + .and(interestCenter.deleted.eq(false)) + ) + .fetchFirst(); + + return result != null; + } + +} diff --git a/src/main/java/com/somemore/interestcenter/service/CancelInterestCenterService.java b/src/main/java/com/somemore/interestcenter/service/CancelInterestCenterService.java new file mode 100644 index 000000000..a3c72145c --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/service/CancelInterestCenterService.java @@ -0,0 +1,28 @@ +package com.somemore.interestcenter.service; + +import com.somemore.global.exception.BadRequestException; +import com.somemore.interestcenter.domain.InterestCenter; +import com.somemore.interestcenter.repository.InterestCenterRepository; +import com.somemore.interestcenter.usecase.CancelInterestCenterUseCase; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import static com.somemore.global.exception.ExceptionMessage.CANNOT_CANCEL_DELETED_INTEREST_CENTER; + +@RequiredArgsConstructor +@Service +public class CancelInterestCenterService implements CancelInterestCenterUseCase { + + private final InterestCenterRepository interestCenterRepository; + + @Override + public void cancelInterestCenter(Long interestCenterId) { + InterestCenter interestCenter = interestCenterRepository.findById(interestCenterId) + .orElseThrow(() -> new BadRequestException(CANNOT_CANCEL_DELETED_INTEREST_CENTER.getMessage())); + + interestCenter.markAsDeleted(); + + interestCenterRepository.save(interestCenter); + } + +} diff --git a/src/main/java/com/somemore/interestcenter/service/RegisterInterestCenterService.java b/src/main/java/com/somemore/interestcenter/service/RegisterInterestCenterService.java new file mode 100644 index 000000000..c9fb8ce3d --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/service/RegisterInterestCenterService.java @@ -0,0 +1,37 @@ +package com.somemore.interestcenter.service; + +import com.somemore.center.usecase.query.CenterQueryUseCase; +import com.somemore.global.exception.DuplicateException; +import com.somemore.interestcenter.domain.InterestCenter; +import com.somemore.interestcenter.dto.request.RegisterInterestCenterRequestDto; +import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; +import com.somemore.interestcenter.repository.InterestCenterRepository; +import com.somemore.interestcenter.usecase.RegisterInterestCenterUseCase; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import static com.somemore.global.exception.ExceptionMessage.DUPLICATE_INTEREST_CENTER; + +@RequiredArgsConstructor +@Service +public class RegisterInterestCenterService implements RegisterInterestCenterUseCase { + + private final InterestCenterRepository repository; + private final CenterQueryUseCase centerQueryUseCase; + + @Override + public RegisterInterestCenterResponseDto registerInterestCenter(RegisterInterestCenterRequestDto requestDto) { + + centerQueryUseCase.validateCenterExists(requestDto.centerId()); + + boolean isDuplicate = repository.existsByVolunteerIdAndCenterId(requestDto.volunteerId(), requestDto.centerId()); + if(isDuplicate){ + throw new DuplicateException(DUPLICATE_INTEREST_CENTER.getMessage()); + } + + InterestCenter interestCenter = repository.save(requestDto.toEntity()); + + return RegisterInterestCenterResponseDto.from(interestCenter); + } + +} diff --git a/src/main/java/com/somemore/interestcenter/usecase/CancelInterestCenterUseCase.java b/src/main/java/com/somemore/interestcenter/usecase/CancelInterestCenterUseCase.java new file mode 100644 index 000000000..53515a1cd --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/usecase/CancelInterestCenterUseCase.java @@ -0,0 +1,5 @@ +package com.somemore.interestcenter.usecase; + +public interface CancelInterestCenterUseCase { + void cancelInterestCenter(Long interestCenterId); +} diff --git a/src/main/java/com/somemore/interestcenter/usecase/RegisterInterestCenterUseCase.java b/src/main/java/com/somemore/interestcenter/usecase/RegisterInterestCenterUseCase.java new file mode 100644 index 000000000..f6ce68e7a --- /dev/null +++ b/src/main/java/com/somemore/interestcenter/usecase/RegisterInterestCenterUseCase.java @@ -0,0 +1,8 @@ +package com.somemore.interestcenter.usecase; + +import com.somemore.interestcenter.dto.request.RegisterInterestCenterRequestDto; +import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; + +public interface RegisterInterestCenterUseCase { + RegisterInterestCenterResponseDto registerInterestCenter(RegisterInterestCenterRequestDto requestDto); +} diff --git a/src/test/java/com/somemore/interestcenter/controller/InterestCenterCommandApiControllerTest.java b/src/test/java/com/somemore/interestcenter/controller/InterestCenterCommandApiControllerTest.java new file mode 100644 index 000000000..a49476b02 --- /dev/null +++ b/src/test/java/com/somemore/interestcenter/controller/InterestCenterCommandApiControllerTest.java @@ -0,0 +1,68 @@ +package com.somemore.interestcenter.controller; + +import com.somemore.ControllerTestSupport; +import com.somemore.interestcenter.dto.request.RegisterInterestCenterRequestDto; +import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; +import com.somemore.interestcenter.usecase.CancelInterestCenterUseCase; +import com.somemore.interestcenter.usecase.RegisterInterestCenterUseCase; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; +import org.springframework.boot.test.mock.mockito.MockBean; + +import java.util.UUID; + +import static org.mockito.BDDMockito.given; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +class InterestCenterCommandApiControllerTest extends ControllerTestSupport { + + @MockBean + private RegisterInterestCenterUseCase registerInterestCenterUseCase; + + @MockBean + private CancelInterestCenterUseCase cancelInterestCenterUseCase; + + private RegisterInterestCenterRequestDto requestDto; + + @BeforeEach + void setUp() { + requestDto = new RegisterInterestCenterRequestDto( + UUID.fromString("123e4567-e89b-12d3-a456-426614174000"), + UUID.fromString("123e4567-e89b-12d3-a456-426614174000") + ); + } + + @Test + void registerInterestCenter_ShouldReturnSuccess() throws Exception { + // given + RegisterInterestCenterResponseDto responseDto = new RegisterInterestCenterResponseDto(1L, requestDto.volunteerId(), requestDto.centerId()); + given(registerInterestCenterUseCase.registerInterestCenter(any(RegisterInterestCenterRequestDto.class))) + .willReturn(responseDto); + + // when & then + mockMvc.perform(post("/api/interest-center") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("관심 기관 등록 성공")) + .andExpect(jsonPath("$.data.id").value(responseDto.id())) + .andExpect(jsonPath("$.data.volunteer_id").value(responseDto.volunteerId().toString())) + .andExpect(jsonPath("$.data.center_id").value(responseDto.centerId().toString())); + } + + @Test + void deleteInterestCenter_ShouldReturnSuccess() throws Exception { + // given + Long interestCenterId = 1L; + doNothing().when(cancelInterestCenterUseCase).cancelInterestCenter(interestCenterId); + + // when & then + mockMvc.perform(delete("/api/interest-center/{interest-center-id}", interestCenterId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("관심 기관 취소 성공")); + } +} diff --git a/src/test/java/com/somemore/interestcenter/service/CancelInterestCenterServiceTest.java b/src/test/java/com/somemore/interestcenter/service/CancelInterestCenterServiceTest.java new file mode 100644 index 000000000..e89946ffc --- /dev/null +++ b/src/test/java/com/somemore/interestcenter/service/CancelInterestCenterServiceTest.java @@ -0,0 +1,98 @@ +package com.somemore.interestcenter.service; + +import com.somemore.IntegrationTestSupport; +import com.somemore.center.domain.Center; +import com.somemore.center.repository.CenterRepository; +import com.somemore.global.exception.BadRequestException; +import com.somemore.interestcenter.domain.InterestCenter; +import com.somemore.interestcenter.dto.request.RegisterInterestCenterRequestDto; +import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; +import com.somemore.interestcenter.repository.InterestCenterRepository; +import com.somemore.interestcenter.usecase.CancelInterestCenterUseCase; +import com.somemore.interestcenter.usecase.RegisterInterestCenterUseCase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; +import java.util.UUID; + +import static com.somemore.global.exception.ExceptionMessage.CANNOT_CANCEL_DELETED_INTEREST_CENTER; +import static org.junit.jupiter.api.Assertions.*; + +@Transactional +class CancelInterestCenterServiceTest extends IntegrationTestSupport { + + @Autowired + private CancelInterestCenterUseCase cancelInterestCenterUseCase; + + @Autowired + private RegisterInterestCenterUseCase registerInterestCenterUseCase; + + @Autowired + private InterestCenterRepository interestCenterRepository; + + @Autowired + private CenterRepository centerRepository; + + @DisplayName("봉사자는 기관에 대한 관심 표시를 취소할 수 있다.") + @Test + void CancelInterestCenter() { + //given + Center center = createCenter(); + UUID volunteerId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"); + UUID centerId = center.getId(); + RegisterInterestCenterRequestDto requestDto = new RegisterInterestCenterRequestDto(volunteerId, centerId); + + RegisterInterestCenterResponseDto responseDto = registerInterestCenterUseCase.registerInterestCenter(requestDto); + + InterestCenter savedInterestCenter = interestCenterRepository.findById(responseDto.id()) + .orElseThrow(() -> new IllegalStateException("등록된 관심 기관이 없습니다.")); + assertEquals(savedInterestCenter.getId(), responseDto.id()); + + //when + cancelInterestCenterUseCase.cancelInterestCenter(responseDto.id()); + + //then + Optional deletedInterestCenterOptional = interestCenterRepository.findById(responseDto.id()); + assertTrue(deletedInterestCenterOptional.isEmpty()); + + } + + @DisplayName("이미 삭제된 관심 기관을 다시 취소하려 하면 예외가 발생한다.") + @Test + void cancelInterestCenter_AlreadyDeleted_ShouldThrowException() { + //given + Center center = createCenter(); + UUID volunteerId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"); + UUID centerId = center.getId(); + RegisterInterestCenterRequestDto requestDto = new RegisterInterestCenterRequestDto(volunteerId, centerId); + RegisterInterestCenterResponseDto responseDto = registerInterestCenterUseCase.registerInterestCenter(requestDto); + cancelInterestCenterUseCase.cancelInterestCenter(responseDto.id()); + + //when, then + long interestCenterId = responseDto.id(); + assertThrows(BadRequestException.class, + () -> cancelInterestCenterUseCase.cancelInterestCenter(interestCenterId), + CANNOT_CANCEL_DELETED_INTEREST_CENTER.getMessage() + ); + } + + private Center createCenter() { + Center center = Center.create( + "기본 기관 이름", + "010-1234-5678", + "http://example.com/image.jpg", + "기관 소개 내용", + "http://example.com", + "account123", + "password123" + ); + + centerRepository.save(center); + + return center; + } + +} diff --git a/src/test/java/com/somemore/interestcenter/service/RegisterInterestCenterServiceTest.java b/src/test/java/com/somemore/interestcenter/service/RegisterInterestCenterServiceTest.java new file mode 100644 index 000000000..a7e7bbeee --- /dev/null +++ b/src/test/java/com/somemore/interestcenter/service/RegisterInterestCenterServiceTest.java @@ -0,0 +1,109 @@ +package com.somemore.interestcenter.service; + +import com.somemore.IntegrationTestSupport; +import com.somemore.center.domain.Center; +import com.somemore.center.repository.CenterRepository; +import com.somemore.global.exception.BadRequestException; +import com.somemore.global.exception.DuplicateException; +import com.somemore.interestcenter.dto.request.RegisterInterestCenterRequestDto; +import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; +import com.somemore.interestcenter.repository.InterestCenterRepository; +import com.somemore.interestcenter.usecase.RegisterInterestCenterUseCase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +@Transactional +class RegisterInterestCenterServiceTest extends IntegrationTestSupport { + + @Autowired + private RegisterInterestCenterUseCase registerInterestCenter; + + @Autowired + private InterestCenterRepository interestCenterRepository; + + @Autowired + private CenterRepository centerRepository; + + @DisplayName("봉사자는 관심 기관을 등록할 수 있다.") + @Test + void RegisterInterestCenter() { + //given + Center center = createCenter(); + UUID volunteerId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"); + UUID centerId = center.getId(); + RegisterInterestCenterRequestDto requestDto = new RegisterInterestCenterRequestDto(volunteerId, centerId); + + //when + RegisterInterestCenterResponseDto responseDto = registerInterestCenter.registerInterestCenter(requestDto); + + //then + Optional result = interestCenterRepository.findInterestCenterResponseById(responseDto.id()); + assertTrue(result.isPresent()); + assertEquals(responseDto.id(), result.get().id()); + assertEquals(volunteerId, result.get().volunteerId()); + assertEquals(centerId, result.get().centerId()); + } + + @DisplayName("이미 관심 표시한 기관에 관심 표시를 시도하면 예외를 던져준다.") + @Test + void registerInterestCenter_WithDuplicateCenterId_ShouldThrowException() { + // given + Center center = createCenter(); + UUID volunteerId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"); + UUID centerId = center.getId(); + RegisterInterestCenterRequestDto requestDto = new RegisterInterestCenterRequestDto(volunteerId, centerId); + + registerInterestCenter.registerInterestCenter(requestDto); + + // when + DuplicateException exception = assertThrows( + DuplicateException.class, + () -> registerInterestCenter.registerInterestCenter(requestDto) + ); + + // then + assertEquals("이미 관심 표시한 기관입니다.", exception.getMessage()); + } + + + @DisplayName("존재하지 않는 기관 Id로 관심 기관 등록 시 예외가 발생한다.") + @Test + void registerInterestCenter_WithInvalidCenterId_ShouldThrowException() { + // given + UUID volunteerId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"); + UUID invalidCenterId = UUID.fromString("123e4567-e89b-12d3-a456-426614174001"); + RegisterInterestCenterRequestDto requestDto = new RegisterInterestCenterRequestDto(volunteerId, invalidCenterId); + + // when + BadRequestException exception = assertThrows(BadRequestException.class, () -> { + registerInterestCenter.registerInterestCenter(requestDto); + }); + + //then + assertEquals("존재하지 않는 기관 입니다.", exception.getMessage()); + } + + private Center createCenter() { + Center center = Center.create( + "기본 기관 이름", + "010-1234-5678", + "http://example.com/image.jpg", + "기관 소개 내용", + "http://example.com", + "account123", + "password123" + ); + + centerRepository.save(center); + + return center; + } + +}