diff --git a/backend/build.gradle b/backend/build.gradle index 0ab68f6..f905ba2 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -58,6 +58,10 @@ dependencies { //Swagger 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') { diff --git a/backend/src/main/java/com/worlabel/domain/workspace/controller/WorkspaceController.java b/backend/src/main/java/com/worlabel/domain/workspace/controller/WorkspaceController.java new file mode 100644 index 0000000..d5442d2 --- /dev/null +++ b/backend/src/main/java/com/worlabel/domain/workspace/controller/WorkspaceController.java @@ -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 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 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 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 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 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 deleteWorkspace(@CurrentUser final Integer memberId, @PathVariable("workspace_id") final Integer workspaceId) { + workspaceService.deleteWorkspace(memberId, workspaceId); + return SuccessResponse.empty(); + } +} diff --git a/backend/src/main/java/com/worlabel/domain/workspace/entity/Workspace.java b/backend/src/main/java/com/worlabel/domain/workspace/entity/Workspace.java index 18b1dc9..a7746cc 100644 --- a/backend/src/main/java/com/worlabel/domain/workspace/entity/Workspace.java +++ b/backend/src/main/java/com/worlabel/domain/workspace/entity/Workspace.java @@ -1,14 +1,15 @@ 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.exception.CustomException; +import com.worlabel.global.exception.ErrorCode; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; - -import java.util.ArrayList; -import java.util.List; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; @Getter @Entity @@ -24,22 +25,47 @@ public class Workspace extends BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; + /** + * 만든 사용자 + */ + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) + private Member member; + /** * 워크 스페이스 제목 */ - @Column(name = "title",nullable = false, length = 50) + @Column(name = "title", nullable = false, length = 50) private String title; /** * 워크 스페이스 설명 */ - @Column(name = "description", nullable = false,length = 255) + @Column(name = "description", nullable = false, length = 255) private String description; - /** - * 워크 스페이스에 속한 프로젝트 - */ - @OneToMany(mappedBy = "workspace", fetch = FetchType.LAZY,cascade = CascadeType.ALL, orphanRemoval = true) - private List projectList = new ArrayList<>(); + private Workspace(final Member member, final String title, final String description) { + validateInputs(member, title, description); + this.member = member; + this.title = title; + 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); + } + } } + diff --git a/backend/src/main/java/com/worlabel/domain/workspace/entity/dto/WorkspaceRequest.java b/backend/src/main/java/com/worlabel/domain/workspace/entity/dto/WorkspaceRequest.java new file mode 100644 index 0000000..48f79cc --- /dev/null +++ b/backend/src/main/java/com/worlabel/domain/workspace/entity/dto/WorkspaceRequest.java @@ -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; +} \ No newline at end of file diff --git a/backend/src/main/java/com/worlabel/domain/workspace/entity/dto/WorkspaceResponse.java b/backend/src/main/java/com/worlabel/domain/workspace/entity/dto/WorkspaceResponse.java new file mode 100644 index 0000000..b0a76be --- /dev/null +++ b/backend/src/main/java/com/worlabel/domain/workspace/entity/dto/WorkspaceResponse.java @@ -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()); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/worlabel/domain/workspace/entity/dto/WorkspaceResponses.java b/backend/src/main/java/com/worlabel/domain/workspace/entity/dto/WorkspaceResponses.java new file mode 100644 index 0000000..31c812e --- /dev/null +++ b/backend/src/main/java/com/worlabel/domain/workspace/entity/dto/WorkspaceResponses.java @@ -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 workspaceResponses; + + public static WorkspaceResponses from(final List workspaceResponses) { + return new WorkspaceResponses(workspaceResponses); + } +} diff --git a/backend/src/main/java/com/worlabel/domain/workspace/repository/WorkspaceRepository.java b/backend/src/main/java/com/worlabel/domain/workspace/repository/WorkspaceRepository.java new file mode 100644 index 0000000..a1f01c1 --- /dev/null +++ b/backend/src/main/java/com/worlabel/domain/workspace/repository/WorkspaceRepository.java @@ -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 { + + Optional 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 findWorkspacesByMemberIdAndLastWorkspaceId(@Param("memberId") Integer memberId, @Param("lastWorkspaceId") Integer lastWorkspaceId, @Param("pageSize") Integer pageSize); +} diff --git a/backend/src/main/java/com/worlabel/domain/workspace/service/WorkspaceService.java b/backend/src/main/java/com/worlabel/domain/workspace/service/WorkspaceService.java new file mode 100644 index 0000000..f8188a4 --- /dev/null +++ b/backend/src/main/java/com/worlabel/domain/workspace/service/WorkspaceService.java @@ -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 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)); + } +} diff --git a/backend/src/main/java/com/worlabel/global/exception/ErrorCode.java b/backend/src/main/java/com/worlabel/global/exception/ErrorCode.java index c18226d..b21bc38 100644 --- a/backend/src/main/java/com/worlabel/global/exception/ErrorCode.java +++ b/backend/src/main/java/com/worlabel/global/exception/ErrorCode.java @@ -23,6 +23,9 @@ public enum ErrorCode { INVALID_REFRESH_TOKEN(HttpStatus.BAD_REQUEST, 2004, "유효하지 않은 리프레시 토큰입니다."), UNAUTHORIZED(HttpStatus.UNAUTHORIZED, 2005, "인증에 실패하였습니다."), + // Workspace - 3000 + NOT_AUTHOR(HttpStatus.FORBIDDEN, 3001, "작성자가 아닙니다. 이 작업을 수행할 권한이 없습니다."), + WORKSPACE_NOT_FOUND(HttpStatus.BAD_REQUEST, 3002, "해당 워크스페이스는 존재하지 않습니다."), ; private final HttpStatus status; diff --git a/backend/src/test/java/com/worlabel/domain/workspace/service/WorkspaceServiceUnitTest.java b/backend/src/test/java/com/worlabel/domain/workspace/service/WorkspaceServiceUnitTest.java new file mode 100644 index 0000000..ccca18c --- /dev/null +++ b/backend/src/test/java/com/worlabel/domain/workspace/service/WorkspaceServiceUnitTest.java @@ -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 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); + } +} diff --git a/backend/src/test/java/com/worlabel/fixture/MemberFixture.java b/backend/src/test/java/com/worlabel/fixture/MemberFixture.java new file mode 100644 index 0000000..3193232 --- /dev/null +++ b/backend/src/test/java/com/worlabel/fixture/MemberFixture.java @@ -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"); + } +}