Merge branch 'be/feat/workspace' into 'be/develop'
Be/feat/workspace See merge request s11-s-project/S11P21S002!9
This commit is contained in:
commit
0779417fdc
@ -58,6 +58,10 @@ dependencies {
|
|||||||
|
|
||||||
//Swagger
|
//Swagger
|
||||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
|
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
|
||||||
|
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
|
||||||
|
testImplementation 'org.mockito:mockito-core:3.9.0'
|
||||||
|
testImplementation 'org.mockito:mockito-junit-jupiter:3.9.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
package com.worlabel.domain.workspace.controller;
|
||||||
|
|
||||||
|
import com.worlabel.domain.workspace.entity.dto.WorkspaceRequest;
|
||||||
|
import com.worlabel.domain.workspace.entity.dto.WorkspaceResponse;
|
||||||
|
import com.worlabel.domain.workspace.entity.dto.WorkspaceResponses;
|
||||||
|
import com.worlabel.domain.workspace.service.WorkspaceService;
|
||||||
|
import com.worlabel.global.annotation.CurrentUser;
|
||||||
|
import com.worlabel.global.config.swagger.SwaggerApiError;
|
||||||
|
import com.worlabel.global.config.swagger.SwaggerApiSuccess;
|
||||||
|
import com.worlabel.global.exception.ErrorCode;
|
||||||
|
import com.worlabel.global.response.BaseResponse;
|
||||||
|
import com.worlabel.global.response.SuccessResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Tag(name = "워크스페이스 관련 API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/workspaces")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class WorkspaceController {
|
||||||
|
|
||||||
|
private final WorkspaceService workspaceService;
|
||||||
|
|
||||||
|
@Operation(summary = "워크스페이스 생성", description = "새로운 워크스페이스를 생성합니다.")
|
||||||
|
@SwaggerApiSuccess(description = "워크스페이스를 성공적으로 생성합니다.")
|
||||||
|
@SwaggerApiError({ErrorCode.EMPTY_REQUEST_PARAMETER, ErrorCode.SERVER_ERROR})
|
||||||
|
@PostMapping
|
||||||
|
public BaseResponse<WorkspaceResponse> createWorkspace(@CurrentUser final Integer memberId, @Valid @RequestBody final WorkspaceRequest workspaceRequest) {
|
||||||
|
WorkspaceResponse workspace = workspaceService.createWorkspace(memberId, workspaceRequest);
|
||||||
|
return SuccessResponse.of(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "특정 워크스페이스 조회", description = "특정 워크스페이스를 조회합니다.")
|
||||||
|
@SwaggerApiSuccess(description = "특정 워크스페이스를 성공적으로 조회합니다.")
|
||||||
|
@SwaggerApiError({ErrorCode.BAD_REQUEST, ErrorCode.NOT_AUTHOR, ErrorCode.SERVER_ERROR})
|
||||||
|
@GetMapping("/{workspace_id}")
|
||||||
|
public BaseResponse<WorkspaceResponse> getWorkspace(@CurrentUser final Integer memberId, @PathVariable("workspace_id") final Integer workspaceId) {
|
||||||
|
WorkspaceResponse workspace = workspaceService.getWorkspaceById(memberId, workspaceId);
|
||||||
|
return SuccessResponse.of(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "전체 워크스페이스 조회", description = "모든 워크스페이스를 조회합니다.")
|
||||||
|
@SwaggerApiSuccess(description = "전체 워크스페이스를 성공적으로 조회합니다.")
|
||||||
|
@SwaggerApiError({ErrorCode.SERVER_ERROR})
|
||||||
|
@GetMapping
|
||||||
|
public BaseResponse<WorkspaceResponses> getAllWorkspaces(
|
||||||
|
@CurrentUser final Integer memberId,
|
||||||
|
@Parameter(name = "마지막 워크스페이스 id", description = "마지막 워크스페이스 id를 넣으면 그 아래 부터 가져옴, 넣지않으면 가장 최신", example = "1") @RequestParam(required = false) Integer lastWorkspaceId,
|
||||||
|
@Parameter(name = "가져올 워크스페이스 수", description = "가져올 워크스페이스 수 default = 10", example = "20") @RequestParam(defaultValue = "10") Integer limitPage) {
|
||||||
|
List<WorkspaceResponse> workspaces = workspaceService.getAllWorkspaces(memberId, lastWorkspaceId, limitPage);
|
||||||
|
return SuccessResponse.of(WorkspaceResponses.from(workspaces));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "워크스페이스 수정", description = "특정 워크스페이스를 수정합니다.")
|
||||||
|
@SwaggerApiSuccess(description = "특정 워크스페이스를 성공적으로 수정합니다.")
|
||||||
|
@SwaggerApiError({ErrorCode.BAD_REQUEST, ErrorCode.NOT_AUTHOR, ErrorCode.SERVER_ERROR})
|
||||||
|
@PutMapping("/{workspace_id}")
|
||||||
|
public BaseResponse<WorkspaceResponse> updateWorkspace(
|
||||||
|
@CurrentUser final Integer memberId,
|
||||||
|
@PathVariable("workspace_id") final Integer workspaceId,
|
||||||
|
@Valid @RequestBody final WorkspaceRequest updatedWorkspace) {
|
||||||
|
WorkspaceResponse workspace = workspaceService.updateWorkspace(memberId, workspaceId, updatedWorkspace);
|
||||||
|
return SuccessResponse.of(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "워크스페이스 삭제", description = "특정 워크스페이스를 삭제합니다.")
|
||||||
|
@SwaggerApiSuccess(description = "특정 워크스페이스를 성공적으로 삭제합니다.")
|
||||||
|
@SwaggerApiError({ErrorCode.BAD_REQUEST, ErrorCode.NOT_AUTHOR, ErrorCode.SERVER_ERROR})
|
||||||
|
@DeleteMapping("/{workspace_id}")
|
||||||
|
public BaseResponse<Void> deleteWorkspace(@CurrentUser final Integer memberId, @PathVariable("workspace_id") final Integer workspaceId) {
|
||||||
|
workspaceService.deleteWorkspace(memberId, workspaceId);
|
||||||
|
return SuccessResponse.empty();
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,15 @@
|
|||||||
package com.worlabel.domain.workspace.entity;
|
package com.worlabel.domain.workspace.entity;
|
||||||
|
|
||||||
import com.worlabel.domain.project.entity.Project;
|
import com.worlabel.domain.member.entity.Member;
|
||||||
import com.worlabel.global.common.BaseEntity;
|
import com.worlabel.global.common.BaseEntity;
|
||||||
|
import com.worlabel.global.exception.CustomException;
|
||||||
|
import com.worlabel.global.exception.ErrorCode;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.hibernate.annotations.OnDelete;
|
||||||
import java.util.ArrayList;
|
import org.hibernate.annotations.OnDeleteAction;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Entity
|
@Entity
|
||||||
@ -24,6 +25,14 @@ public class Workspace extends BaseEntity {
|
|||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private Integer id;
|
private Integer id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 만든 사용자
|
||||||
|
*/
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "member_id", nullable = false)
|
||||||
|
@OnDelete(action = OnDeleteAction.CASCADE)
|
||||||
|
private Member member;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 워크 스페이스 제목
|
* 워크 스페이스 제목
|
||||||
*/
|
*/
|
||||||
@ -36,10 +45,27 @@ public class Workspace extends BaseEntity {
|
|||||||
@Column(name = "description", nullable = false, length = 255)
|
@Column(name = "description", nullable = false, length = 255)
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
/**
|
private Workspace(final Member member, final String title, final String description) {
|
||||||
* 워크 스페이스에 속한 프로젝트
|
validateInputs(member, title, description);
|
||||||
*/
|
this.member = member;
|
||||||
@OneToMany(mappedBy = "workspace", fetch = FetchType.LAZY,cascade = CascadeType.ALL, orphanRemoval = true)
|
this.title = title;
|
||||||
private List<Project> projectList = new ArrayList<>();
|
this.description = description;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Workspace of(final Member member, final String title, final String description) {
|
||||||
|
return new Workspace(member, title, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateWorkspace(final String title, final String description) {
|
||||||
|
validateInputs(member, title, description);
|
||||||
|
this.title = title;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateInputs(final Member member, final String title, final String description) {
|
||||||
|
if (member == null || title.isBlank() || description.isBlank()) {
|
||||||
|
throw new CustomException(ErrorCode.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.worlabel.domain.workspace.entity.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Schema(name = "워크스페이스 요청 dto", description = "워크스페이스 작성시 필요한 정보")
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class WorkspaceRequest {
|
||||||
|
|
||||||
|
@Schema(description = "제목", example = "title1")
|
||||||
|
@NotEmpty(message = "제목을 입력하세요.")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Schema(description = "내용", example = "content1")
|
||||||
|
@NotEmpty(message = "내용을 입력하세요.")
|
||||||
|
private String content;
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.worlabel.domain.workspace.entity.dto;
|
||||||
|
|
||||||
|
import com.worlabel.domain.workspace.entity.Workspace;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Schema(name = "워크스페이스 응답 dto", description = "워크스페이스 응답 DTO")
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class WorkspaceResponse {
|
||||||
|
|
||||||
|
@Schema(description = "워크스페이스 ID", example = "1")
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Schema(description = "회원 ID", example = "member123")
|
||||||
|
private String memberId;
|
||||||
|
|
||||||
|
@Schema(description = "제목", example = "workspace1")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Schema(description = "내용", example = "갤럭시 s24 불량 검증")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "생성일시", example = "2024-07-25 17:51:02")
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@Schema(description = "수정일시", example = "2024-07-28 17:51:02")
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
public static WorkspaceResponse from(final Workspace workspace) {
|
||||||
|
return new WorkspaceResponse(
|
||||||
|
workspace.getId(),
|
||||||
|
workspace.getMember().getNickname(),
|
||||||
|
workspace.getTitle(),
|
||||||
|
workspace.getDescription(),
|
||||||
|
workspace.getCreatedAt(),
|
||||||
|
workspace.getUpdatedAt());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.worlabel.domain.workspace.entity.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Schema(name = "워크스페이스 목록 응답 dto", description = "워크스페이스 목록 응답 DTO")
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class WorkspaceResponses {
|
||||||
|
|
||||||
|
@Schema(description = "워크스페이스 목록", example = "")
|
||||||
|
private List<WorkspaceResponse> workspaceResponses;
|
||||||
|
|
||||||
|
public static WorkspaceResponses from(final List<WorkspaceResponse> workspaceResponses) {
|
||||||
|
return new WorkspaceResponses(workspaceResponses);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.worlabel.domain.workspace.repository;
|
||||||
|
|
||||||
|
import com.worlabel.domain.workspace.entity.Workspace;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface WorkspaceRepository extends JpaRepository<Workspace, Integer> {
|
||||||
|
|
||||||
|
Optional<Workspace> findByMemberIdAndId(Integer memberId, Integer workspaceId);
|
||||||
|
|
||||||
|
@Query(value = "SELECT * FROM workspace w WHERE w.member_id = :memberId " +
|
||||||
|
"AND (:lastWorkspaceId IS NULL OR w.workspace_id < :lastWorkspaceId) " +
|
||||||
|
"ORDER BY w.workspace_id DESC " +
|
||||||
|
"LIMIT :pageSize",
|
||||||
|
nativeQuery = true)
|
||||||
|
List<Workspace> findWorkspacesByMemberIdAndLastWorkspaceId(@Param("memberId") Integer memberId, @Param("lastWorkspaceId") Integer lastWorkspaceId, @Param("pageSize") Integer pageSize);
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package com.worlabel.domain.workspace.service;
|
||||||
|
|
||||||
|
import com.worlabel.domain.member.entity.Member;
|
||||||
|
import com.worlabel.domain.member.repository.MemberRepository;
|
||||||
|
import com.worlabel.domain.workspace.entity.Workspace;
|
||||||
|
import com.worlabel.domain.workspace.entity.dto.WorkspaceRequest;
|
||||||
|
import com.worlabel.domain.workspace.entity.dto.WorkspaceResponse;
|
||||||
|
import com.worlabel.domain.workspace.repository.WorkspaceRepository;
|
||||||
|
import com.worlabel.global.exception.CustomException;
|
||||||
|
import com.worlabel.global.exception.ErrorCode;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Transactional
|
||||||
|
public class WorkspaceService {
|
||||||
|
|
||||||
|
private final WorkspaceRepository workspaceRepository;
|
||||||
|
private final MemberRepository memberRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 새로운 워크스페이스 생성
|
||||||
|
*/
|
||||||
|
public WorkspaceResponse createWorkspace(final Integer memberId, final WorkspaceRequest workspaceRequest) {
|
||||||
|
Member member = getMember(memberId);
|
||||||
|
Workspace workspace = Workspace.of(member, workspaceRequest.getTitle(), workspaceRequest.getContent());
|
||||||
|
workspaceRepository.save(workspace);
|
||||||
|
|
||||||
|
return WorkspaceResponse.from(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 특정 워크스페이스 조회
|
||||||
|
*/
|
||||||
|
public WorkspaceResponse getWorkspaceById(final Integer memberId, final Integer workspaceId) {
|
||||||
|
Workspace workspace = getWorkspace(memberId, workspaceId);
|
||||||
|
return WorkspaceResponse.from(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 전체 워크스페이스 조회
|
||||||
|
*/
|
||||||
|
public List<WorkspaceResponse> getAllWorkspaces(final Integer memberId, final Integer lastWorkspaceId, final Integer pageSize) {
|
||||||
|
return workspaceRepository.findWorkspacesByMemberIdAndLastWorkspaceId(memberId, lastWorkspaceId, pageSize).stream()
|
||||||
|
.map(WorkspaceResponse::from)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 워크스페이스 수정
|
||||||
|
*/
|
||||||
|
public WorkspaceResponse updateWorkspace(final Integer memberId, final Integer workspaceId, final WorkspaceRequest updatedWorkspace) {
|
||||||
|
Workspace workspace = getWorkspace(memberId, workspaceId);
|
||||||
|
workspace.updateWorkspace(updatedWorkspace.getTitle(), updatedWorkspace.getContent());
|
||||||
|
|
||||||
|
return WorkspaceResponse.from(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 워크스페이스 삭제
|
||||||
|
*/
|
||||||
|
public void deleteWorkspace(final Integer memberId, final Integer workspaceId) {
|
||||||
|
Workspace workspace = getWorkspace(memberId, workspaceId);
|
||||||
|
workspaceRepository.delete(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Member getMember(final Integer memberId) {
|
||||||
|
return memberRepository.findById(memberId)
|
||||||
|
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 작성자와 같은 워크스페이스만 가져옴
|
||||||
|
* 기존 방식은 DB 2번 접근 -> 쿼리로 한번에 접근하도록 바꿈
|
||||||
|
*/
|
||||||
|
private Workspace getWorkspace(final Integer memberId, final Integer workspaceId) {
|
||||||
|
return workspaceRepository.findByMemberIdAndId(memberId, workspaceId)
|
||||||
|
.orElseThrow(() -> new CustomException(ErrorCode.WORKSPACE_NOT_FOUND));
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,9 @@ public enum ErrorCode {
|
|||||||
INVALID_REFRESH_TOKEN(HttpStatus.BAD_REQUEST, 2004, "유효하지 않은 리프레시 토큰입니다."),
|
INVALID_REFRESH_TOKEN(HttpStatus.BAD_REQUEST, 2004, "유효하지 않은 리프레시 토큰입니다."),
|
||||||
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, 2005, "인증에 실패하였습니다."),
|
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, 2005, "인증에 실패하였습니다."),
|
||||||
|
|
||||||
|
// Workspace - 3000
|
||||||
|
NOT_AUTHOR(HttpStatus.FORBIDDEN, 3001, "작성자가 아닙니다. 이 작업을 수행할 권한이 없습니다."),
|
||||||
|
WORKSPACE_NOT_FOUND(HttpStatus.BAD_REQUEST, 3002, "해당 워크스페이스는 존재하지 않습니다."),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final HttpStatus status;
|
private final HttpStatus status;
|
||||||
|
@ -0,0 +1,193 @@
|
|||||||
|
package com.worlabel.domain.workspace.service;
|
||||||
|
|
||||||
|
import com.worlabel.domain.member.entity.Member;
|
||||||
|
import com.worlabel.domain.member.repository.MemberRepository;
|
||||||
|
import com.worlabel.domain.workspace.entity.Workspace;
|
||||||
|
import com.worlabel.domain.workspace.entity.dto.WorkspaceRequest;
|
||||||
|
import com.worlabel.domain.workspace.entity.dto.WorkspaceResponse;
|
||||||
|
import com.worlabel.domain.workspace.repository.WorkspaceRepository;
|
||||||
|
import com.worlabel.fixture.MemberFixture;
|
||||||
|
import com.worlabel.global.exception.CustomException;
|
||||||
|
import com.worlabel.global.exception.ErrorCode;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class WorkspaceServiceUnitTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private WorkspaceService workspaceService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private WorkspaceRepository workspaceRepository;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private MemberRepository memberRepository;
|
||||||
|
|
||||||
|
private Member member;
|
||||||
|
private Workspace workspace;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void init() {
|
||||||
|
member = MemberFixture.makeMember();
|
||||||
|
workspace = Workspace.of(member, "initTitle", "initContent");
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("워크스페이스 생성 시 제목이 비어있으면 예외가 발생한다.")
|
||||||
|
@Test
|
||||||
|
void throws_exception_when_create_workspace_with_empty_title() {
|
||||||
|
//given
|
||||||
|
when(memberRepository.findById(1)).thenReturn(Optional.of(member));
|
||||||
|
WorkspaceRequest request = new WorkspaceRequest("", "content");
|
||||||
|
|
||||||
|
//when & then
|
||||||
|
assertThatThrownBy(() -> workspaceService.createWorkspace(1, request))
|
||||||
|
.isInstanceOf(CustomException.class)
|
||||||
|
.hasMessageContaining(ErrorCode.BAD_REQUEST.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("워크스페이스 생성 시 사용자가 존재하지 않으면 예외가 발생한다.")
|
||||||
|
@Test
|
||||||
|
void throws_exception_when_create_workspace_with_invalid_member() {
|
||||||
|
//given
|
||||||
|
given(memberRepository.findById(anyInt())).willReturn(Optional.empty());
|
||||||
|
WorkspaceRequest request = new WorkspaceRequest("title", "content");
|
||||||
|
|
||||||
|
//when & then
|
||||||
|
assertThatThrownBy(() -> workspaceService.createWorkspace(anyInt(), request))
|
||||||
|
.isInstanceOf(CustomException.class)
|
||||||
|
.hasMessageContaining(ErrorCode.USER_NOT_FOUND.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("특정 워크스페이스 조회 시 존재하지 않으면 예외가 발생한다.")
|
||||||
|
@Test
|
||||||
|
void throws_exception_when_find_workspace_with_invalid_workspace_id() {
|
||||||
|
//given
|
||||||
|
Integer workspaceId = 1;
|
||||||
|
given(workspaceRepository.findByMemberIdAndId(anyInt(), eq(workspaceId))).willReturn(Optional.empty());
|
||||||
|
|
||||||
|
//when & then
|
||||||
|
assertThatThrownBy(() -> workspaceService.getWorkspaceById(1, workspaceId))
|
||||||
|
.isInstanceOf(CustomException.class)
|
||||||
|
.hasMessageContaining(ErrorCode.WORKSPACE_NOT_FOUND.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("워크스페이스 수정 시 제목이 비어있으면 예외가 발생한다.")
|
||||||
|
@Test
|
||||||
|
void throws_exception_when_update_workspace_with_empty_title() {
|
||||||
|
//given
|
||||||
|
WorkspaceRequest request = new WorkspaceRequest("", "updateContent");
|
||||||
|
given(workspaceRepository.findByMemberIdAndId(anyInt(), anyInt())).willReturn(Optional.of(workspace));
|
||||||
|
|
||||||
|
//when & then
|
||||||
|
assertThatThrownBy(() -> workspaceService.updateWorkspace(anyInt(), anyInt(), request))
|
||||||
|
.isInstanceOf(CustomException.class)
|
||||||
|
.hasMessageContaining(ErrorCode.BAD_REQUEST.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("워크스페이스 수정 시 내용이 비어있으면 예외가 발생한다.")
|
||||||
|
@Test
|
||||||
|
void throws_exception_when_update_workspace_with_empty_content() {
|
||||||
|
//given
|
||||||
|
WorkspaceRequest request = new WorkspaceRequest("updateTitle", "");
|
||||||
|
given(workspaceRepository.findByMemberIdAndId(anyInt(), anyInt())).willReturn(Optional.of(workspace));
|
||||||
|
|
||||||
|
//when & then
|
||||||
|
assertThatThrownBy(() -> workspaceService.updateWorkspace(anyInt(), anyInt(), request))
|
||||||
|
.isInstanceOf(CustomException.class)
|
||||||
|
.hasMessageContaining(ErrorCode.BAD_REQUEST.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("워크스페이스 삭제 시 존재하지 않으면 예외가 발생한다.")
|
||||||
|
@Test
|
||||||
|
void throws_exception_when_delete_workspace_with_invalid_workspace_id() {
|
||||||
|
//given
|
||||||
|
Integer workspaceId = 1;
|
||||||
|
given(workspaceRepository.findByMemberIdAndId(anyInt(), eq(workspaceId))).willReturn(Optional.empty());
|
||||||
|
|
||||||
|
//when & then
|
||||||
|
assertThatThrownBy(() -> workspaceService.deleteWorkspace(1, workspaceId))
|
||||||
|
.isInstanceOf(CustomException.class)
|
||||||
|
.hasMessageContaining(ErrorCode.WORKSPACE_NOT_FOUND.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("특정 워크스페이스 조회에 성공하면 정상적으로 반환된다.")
|
||||||
|
@Test
|
||||||
|
void success_when_find_workspace_by_id() {
|
||||||
|
//given
|
||||||
|
Integer workspaceId = 1;
|
||||||
|
given(workspaceRepository.findByMemberIdAndId(anyInt(), eq(workspaceId))).willReturn(Optional.of(workspace));
|
||||||
|
|
||||||
|
//when
|
||||||
|
WorkspaceResponse response = workspaceService.getWorkspaceById(1, workspaceId);
|
||||||
|
|
||||||
|
//then
|
||||||
|
assertNotNull(response);
|
||||||
|
assertEquals("initTitle", response.getTitle());
|
||||||
|
assertEquals("initContent", response.getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("전체 워크스페이스 조회에 성공하면 정상적으로 반환된다.")
|
||||||
|
@Test
|
||||||
|
void success_when_find_all_workspaces() {
|
||||||
|
//given
|
||||||
|
Integer lastWorkspaceId = null;
|
||||||
|
Integer pageSize = 10;
|
||||||
|
Workspace workspace1 = Workspace.of(member, "Title 1", "Content 1");
|
||||||
|
Workspace workspace2 = Workspace.of(member, "Title 2", "Content 2");
|
||||||
|
|
||||||
|
given(workspaceRepository.findWorkspacesByMemberIdAndLastWorkspaceId(member.getId(), lastWorkspaceId, pageSize))
|
||||||
|
.willReturn(List.of(workspace1, workspace2));
|
||||||
|
|
||||||
|
//when
|
||||||
|
List<WorkspaceResponse> responses = workspaceService.getAllWorkspaces(member.getId(), lastWorkspaceId, pageSize);
|
||||||
|
|
||||||
|
//then
|
||||||
|
assertEquals(2, responses.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("워크스페이스 수정에 성공하면 수정된 결과를 반환한다.")
|
||||||
|
@Test
|
||||||
|
void success_when_update_workspace() {
|
||||||
|
//given
|
||||||
|
WorkspaceRequest request = new WorkspaceRequest("Updated Title", "Updated Content");
|
||||||
|
given(workspaceRepository.findByMemberIdAndId(anyInt(), anyInt())).willReturn(Optional.of(workspace));
|
||||||
|
|
||||||
|
//when
|
||||||
|
WorkspaceResponse response = workspaceService.updateWorkspace(anyInt(), anyInt(), request);
|
||||||
|
|
||||||
|
//then
|
||||||
|
assertNotNull(response);
|
||||||
|
assertEquals("Updated Title", response.getTitle());
|
||||||
|
assertEquals("Updated Content", response.getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("워크스페이스 삭제에 성공하면 예외 없이 수행된다.")
|
||||||
|
@Test
|
||||||
|
void success_when_delete_workspace() {
|
||||||
|
//given
|
||||||
|
Integer workspaceId = 1;
|
||||||
|
given(workspaceRepository.findByMemberIdAndId(anyInt(), eq(workspaceId))).willReturn(Optional.of(workspace));
|
||||||
|
|
||||||
|
//when
|
||||||
|
workspaceService.deleteWorkspace(1, workspaceId);
|
||||||
|
|
||||||
|
//then
|
||||||
|
verify(workspaceRepository, times(1)).delete(workspace);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.worlabel.fixture;
|
||||||
|
|
||||||
|
import com.worlabel.domain.member.entity.Member;
|
||||||
|
|
||||||
|
public class MemberFixture {
|
||||||
|
|
||||||
|
public static Member makeMember() {
|
||||||
|
return Member.of("1","email@naver.com","nickname","abc.jpg");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user