feat(project): implement CRUD operations for projects
This commit is contained in:
parent
d3362571f2
commit
82074add45
@ -0,0 +1,66 @@
|
|||||||
|
package ai.decompile.workspace.controller;
|
||||||
|
|
||||||
|
import ai.decompile.workspace.model.dto.ProjectRequest;
|
||||||
|
import ai.decompile.workspace.model.dto.ProjectResponse;
|
||||||
|
import ai.decompile.workspace.service.ProjectService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/workspaces/{workspaceId}/projects")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ProjectController {
|
||||||
|
private final ProjectService projectService;
|
||||||
|
|
||||||
|
@Operation(
|
||||||
|
summary = "List projects of a workspace",
|
||||||
|
description = "Retrieve all projects belonging to a workspace, optionally filtered by name.",
|
||||||
|
tags = {"Project"},
|
||||||
|
operationId = "getProjects")
|
||||||
|
@GetMapping
|
||||||
|
public List<ProjectResponse> getProjects(
|
||||||
|
@PathVariable UUID workspaceId,
|
||||||
|
@Parameter(description = "Filter projects whose name contains this value (case-insensitive)")
|
||||||
|
@RequestParam(required = false)
|
||||||
|
String name) {
|
||||||
|
return projectService.getProjects(workspaceId, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(
|
||||||
|
summary = "Create a project",
|
||||||
|
description = "Create a project in a workspace.",
|
||||||
|
tags = {"Project"},
|
||||||
|
operationId = "createProject")
|
||||||
|
@PostMapping
|
||||||
|
public ProjectResponse createProject(
|
||||||
|
@PathVariable UUID workspaceId, @RequestBody ProjectRequest request) {
|
||||||
|
return projectService.createProject(workspaceId, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(
|
||||||
|
summary = "Update a project",
|
||||||
|
description = "Update a project's name and description.",
|
||||||
|
tags = {"Project"},
|
||||||
|
operationId = "updateProject")
|
||||||
|
@PutMapping("/{projectId}")
|
||||||
|
public ProjectResponse updateProject(
|
||||||
|
@PathVariable UUID workspaceId,
|
||||||
|
@PathVariable UUID projectId,
|
||||||
|
@RequestBody ProjectRequest request) {
|
||||||
|
return projectService.updateProject(projectId, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(
|
||||||
|
summary = "Delete a project",
|
||||||
|
description = "Delete a project.",
|
||||||
|
tags = {"Project"},
|
||||||
|
operationId = "deleteProject")
|
||||||
|
@DeleteMapping("/{projectId}")
|
||||||
|
public void deleteProject(@PathVariable UUID workspaceId, @PathVariable UUID projectId) {
|
||||||
|
projectService.deleteProject(projectId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,7 +11,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/workspace")
|
@RequestMapping("/workspaces")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class WorkspaceController {
|
public class WorkspaceController {
|
||||||
private final WorkspaceService workspaceService;
|
private final WorkspaceService workspaceService;
|
||||||
@ -30,6 +30,16 @@ public class WorkspaceController {
|
|||||||
return workspaceService.getWorkspaces(name);
|
return workspaceService.getWorkspaces(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(
|
||||||
|
summary = "Get a workspace by ID",
|
||||||
|
description = "Retrieve a single workspace by its unique identifier.",
|
||||||
|
tags = {"Workspace"},
|
||||||
|
operationId = "getWorkspace")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public WorkspaceResponse getWorkspace(@PathVariable UUID id) {
|
||||||
|
return workspaceService.getWorkspaceById(id);
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Create a workspace",
|
summary = "Create a workspace",
|
||||||
description = "Create a workspace",
|
description = "Create a workspace",
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
package ai.decompile.workspace.model.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
public record ProjectRequest(@NotBlank String name, String description) {}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package ai.decompile.workspace.model.dto;
|
||||||
|
|
||||||
|
import ai.decompile.workspace.model.entity.Project;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record ProjectResponse(
|
||||||
|
@NotNull UUID id,
|
||||||
|
@NotNull UUID workspaceId,
|
||||||
|
@NotBlank String name,
|
||||||
|
String description,
|
||||||
|
@NotNull Instant updatedAt) {
|
||||||
|
public static ProjectResponse from(Project project) {
|
||||||
|
return new ProjectResponse(
|
||||||
|
project.getId(),
|
||||||
|
project.getWorkspace().getId(),
|
||||||
|
project.getName(),
|
||||||
|
project.getDescription(),
|
||||||
|
project.getUpdatedAt());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,12 +7,18 @@ import java.time.Instant;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public record WorkspaceResponse(
|
public record WorkspaceResponse(
|
||||||
@NotNull UUID id, @NotBlank String name, String description, @NotNull Instant updatedAt) {
|
@NotNull UUID id,
|
||||||
|
@NotBlank String name,
|
||||||
|
String description,
|
||||||
|
@NotNull Instant updatedAt,
|
||||||
|
int projectCount) {
|
||||||
public static WorkspaceResponse from(Workspace workspace) {
|
public static WorkspaceResponse from(Workspace workspace) {
|
||||||
|
var projects = workspace.getProjects();
|
||||||
return new WorkspaceResponse(
|
return new WorkspaceResponse(
|
||||||
workspace.getId(),
|
workspace.getId(),
|
||||||
workspace.getName(),
|
workspace.getName(),
|
||||||
workspace.getDescription(),
|
workspace.getDescription(),
|
||||||
workspace.getUpdatedAt());
|
workspace.getUpdatedAt(),
|
||||||
|
projects != null ? projects.size() : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
package ai.decompile.workspace.model.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.*;
|
||||||
|
import org.hibernate.annotations.CreationTimestamp;
|
||||||
|
import org.hibernate.annotations.UpdateTimestamp;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "projects")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class Project {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private UUID id;
|
||||||
|
|
||||||
|
@Column(nullable = false, length = 150)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(length = 500)
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "workspace_id", nullable = false)
|
||||||
|
private Workspace workspace;
|
||||||
|
|
||||||
|
@CreationTimestamp
|
||||||
|
@Column(name = "created_at", nullable = false, updatable = false)
|
||||||
|
private Instant createdAt;
|
||||||
|
|
||||||
|
@UpdateTimestamp
|
||||||
|
@Column(name = "updated_at", nullable = false)
|
||||||
|
private Instant updatedAt;
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ package ai.decompile.workspace.model.entity;
|
|||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import org.hibernate.annotations.CreationTimestamp;
|
import org.hibernate.annotations.CreationTimestamp;
|
||||||
@ -32,4 +33,7 @@ public class Workspace {
|
|||||||
@UpdateTimestamp
|
@UpdateTimestamp
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
private Instant updatedAt;
|
private Instant updatedAt;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "workspace", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
|
private List<Project> projects;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
package ai.decompile.workspace.model.repository;
|
||||||
|
|
||||||
|
import ai.decompile.workspace.model.entity.Project;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
|
public interface ProjectRepository
|
||||||
|
extends JpaRepository<Project, UUID>, JpaSpecificationExecutor<Project> {
|
||||||
|
boolean existsByNameIgnoreCaseAndWorkspace_Id(@NotBlank String name, UUID workspaceId);
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package ai.decompile.workspace.model.specification;
|
||||||
|
|
||||||
|
import ai.decompile.workspace.model.entity.Project;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class ProjectSpecification {
|
||||||
|
public static Specification<Project> nameContains(String name) {
|
||||||
|
return (root, _, cb) -> cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Specification<Project> hasWorkspaceId(UUID workspaceId) {
|
||||||
|
return (root, _, cb) -> cb.equal(root.get("workspace").get("id"), workspaceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,12 @@
|
|||||||
package ai.decompile.workspace.model.specification;
|
package ai.decompile.workspace.model.specification;
|
||||||
|
|
||||||
import ai.decompile.workspace.model.entity.Workspace;
|
import ai.decompile.workspace.model.entity.Workspace;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public class WorkspaceSpecification {
|
public class WorkspaceSpecification {
|
||||||
|
|
||||||
private WorkspaceSpecification() {}
|
|
||||||
|
|
||||||
public static Specification<Workspace> nameContains(String name) {
|
public static Specification<Workspace> nameContains(String name) {
|
||||||
return (root, _, cb) -> cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%");
|
return (root, _, cb) -> cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,77 @@
|
|||||||
|
package ai.decompile.workspace.service;
|
||||||
|
|
||||||
|
import ai.decompile.exception.ConflictException;
|
||||||
|
import ai.decompile.exception.NotFoundException;
|
||||||
|
import ai.decompile.workspace.model.dto.ProjectRequest;
|
||||||
|
import ai.decompile.workspace.model.dto.ProjectResponse;
|
||||||
|
import ai.decompile.workspace.model.entity.Project;
|
||||||
|
import ai.decompile.workspace.model.repository.ProjectRepository;
|
||||||
|
import ai.decompile.workspace.model.specification.ProjectSpecification;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ProjectService {
|
||||||
|
private final WorkspaceService workspaceService;
|
||||||
|
private final ProjectRepository projectRepository;
|
||||||
|
|
||||||
|
public List<ProjectResponse> getProjects(UUID workspaceId, String name) {
|
||||||
|
Specification<Project> spec = ProjectSpecification.hasWorkspaceId(workspaceId);
|
||||||
|
if (StringUtils.isNotBlank(name)) {
|
||||||
|
spec = spec.and(ProjectSpecification.nameContains(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectRepository.findAll(spec).stream().map(ProjectResponse::from).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProjectResponse createProject(UUID workspaceId, ProjectRequest request) {
|
||||||
|
var workspace = workspaceService.getWorkspace(workspaceId);
|
||||||
|
|
||||||
|
if (projectRepository.existsByNameIgnoreCaseAndWorkspace_Id(request.name(), workspaceId)) {
|
||||||
|
throw new ConflictException(
|
||||||
|
"A project with the name '" + request.name() + "' already exists in this workspace.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var project =
|
||||||
|
projectRepository.save(
|
||||||
|
Project.builder()
|
||||||
|
.name(request.name())
|
||||||
|
.description(request.description())
|
||||||
|
.workspace(workspace)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
return ProjectResponse.from(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProjectResponse updateProject(UUID projectId, ProjectRequest request) {
|
||||||
|
var project = getProject(projectId);
|
||||||
|
|
||||||
|
if (!project.getName().equalsIgnoreCase(request.name())
|
||||||
|
&& projectRepository.existsByNameIgnoreCaseAndWorkspace_Id(
|
||||||
|
request.name(), project.getWorkspace().getId())) {
|
||||||
|
throw new ConflictException(
|
||||||
|
"A project with the name '" + request.name() + "' already exists in this workspace.");
|
||||||
|
}
|
||||||
|
|
||||||
|
project.setName(request.name());
|
||||||
|
project.setDescription(request.description());
|
||||||
|
|
||||||
|
return ProjectResponse.from(projectRepository.save(project));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteProject(UUID projectId) {
|
||||||
|
var project = getProject(projectId);
|
||||||
|
projectRepository.delete(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Project getProject(UUID projectId) {
|
||||||
|
return projectRepository
|
||||||
|
.findById(projectId)
|
||||||
|
.orElseThrow(() -> new NotFoundException("Project with id '" + projectId + "' not found"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,13 +19,18 @@ public class WorkspaceService {
|
|||||||
private final WorkspaceRepository workspaceRepository;
|
private final WorkspaceRepository workspaceRepository;
|
||||||
|
|
||||||
public List<WorkspaceResponse> getWorkspaces(String name) {
|
public List<WorkspaceResponse> getWorkspaces(String name) {
|
||||||
|
List<Workspace> workspaces;
|
||||||
if (StringUtils.isNotBlank(name)) {
|
if (StringUtils.isNotBlank(name)) {
|
||||||
return workspaceRepository.findAll(WorkspaceSpecification.nameContains(name)).stream()
|
workspaces = workspaceRepository.findAll(WorkspaceSpecification.nameContains(name));
|
||||||
.map(WorkspaceResponse::from)
|
} else {
|
||||||
.toList();
|
workspaces = workspaceRepository.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
return workspaceRepository.findAll().stream().map(WorkspaceResponse::from).toList();
|
return workspaces.stream().map(WorkspaceResponse::from).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorkspaceResponse getWorkspaceById(UUID id) {
|
||||||
|
return WorkspaceResponse.from(getWorkspace(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorkspaceResponse createWorkspace(WorkspaceRequest request) {
|
public WorkspaceResponse createWorkspace(WorkspaceRequest request) {
|
||||||
@ -55,7 +60,7 @@ public class WorkspaceService {
|
|||||||
workspaceRepository.delete(workspace);
|
workspaceRepository.delete(workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Workspace getWorkspace(UUID id) {
|
protected Workspace getWorkspace(UUID id) {
|
||||||
return workspaceRepository
|
return workspaceRepository
|
||||||
.findById(id)
|
.findById(id)
|
||||||
.orElseThrow(() -> new NotFoundException("Workspace with id '" + id + "' not found"));
|
.orElseThrow(() -> new NotFoundException("Workspace with id '" + id + "' not found"));
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
CREATE TABLE projects
|
||||||
|
(
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
workspace_id UUID NOT NULL REFERENCES workspaces (id) ON DELETE CASCADE,
|
||||||
|
name VARCHAR(150) NOT NULL,
|
||||||
|
description VARCHAR(500),
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE NOT NULL
|
||||||
|
);
|
||||||
@ -0,0 +1,196 @@
|
|||||||
|
package ai.decompile.project.controller;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.ArgumentMatchers.isNull;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||||
|
|
||||||
|
import ai.decompile.exception.ConflictException;
|
||||||
|
import ai.decompile.exception.NotFoundException;
|
||||||
|
import ai.decompile.workspace.controller.ProjectController;
|
||||||
|
import ai.decompile.workspace.model.dto.ProjectRequest;
|
||||||
|
import ai.decompile.workspace.model.dto.ProjectResponse;
|
||||||
|
import ai.decompile.workspace.service.ProjectService;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
@WebMvcTest(ProjectController.class)
|
||||||
|
class ProjectControllerTest {
|
||||||
|
|
||||||
|
@Autowired private MockMvc mockMvc;
|
||||||
|
|
||||||
|
@MockitoBean private ProjectService projectService;
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getProjectsShouldReturn200WithList() throws Exception {
|
||||||
|
var workspaceId = UUID.randomUUID();
|
||||||
|
var projectId = UUID.randomUUID();
|
||||||
|
var response =
|
||||||
|
new ProjectResponse(projectId, workspaceId, "Test Project", "Desc", Instant.now());
|
||||||
|
when(projectService.getProjects(eq(workspaceId), isNull())).thenReturn(List.of(response));
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces/{workspaceId}/projects", workspaceId))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$[0].id").value(projectId.toString()))
|
||||||
|
.andExpect(jsonPath("$[0].name").value("Test Project"))
|
||||||
|
.andExpect(jsonPath("$[0].workspaceId").value(workspaceId.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getProjectsShouldReturn200WithEmptyList() throws Exception {
|
||||||
|
var workspaceId = UUID.randomUUID();
|
||||||
|
when(projectService.getProjects(eq(workspaceId), isNull())).thenReturn(List.of());
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces/{workspaceId}/projects", workspaceId))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$").isArray())
|
||||||
|
.andExpect(jsonPath("$").isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getProjectsShouldFilterByName() throws Exception {
|
||||||
|
var workspaceId = UUID.randomUUID();
|
||||||
|
var projectId = UUID.randomUUID();
|
||||||
|
var response =
|
||||||
|
new ProjectResponse(projectId, workspaceId, "Filtered Project", "Desc", Instant.now());
|
||||||
|
when(projectService.getProjects(eq(workspaceId), eq("filter"))).thenReturn(List.of(response));
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces/{workspaceId}/projects", workspaceId).param("name", "filter"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$[0].id").value(projectId.toString()))
|
||||||
|
.andExpect(jsonPath("$[0].name").value("Filtered Project"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createProjectShouldReturn200() throws Exception {
|
||||||
|
var workspaceId = UUID.randomUUID();
|
||||||
|
var projectId = UUID.randomUUID();
|
||||||
|
var request = new ProjectRequest("New Project", "A description");
|
||||||
|
var response =
|
||||||
|
new ProjectResponse(projectId, workspaceId, "New Project", "A description", Instant.now());
|
||||||
|
when(projectService.createProject(eq(workspaceId), any())).thenReturn(response);
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", workspaceId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.id").value(projectId.toString()))
|
||||||
|
.andExpect(jsonPath("$.name").value("New Project"))
|
||||||
|
.andExpect(jsonPath("$.workspaceId").value(workspaceId.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createProjectShouldReturn404WhenWorkspaceNotFound() throws Exception {
|
||||||
|
var workspaceId = UUID.randomUUID();
|
||||||
|
var request = new ProjectRequest("New Project", "desc");
|
||||||
|
when(projectService.createProject(eq(workspaceId), any()))
|
||||||
|
.thenThrow(new NotFoundException("Workspace with id '" + workspaceId + "' not found"));
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", workspaceId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
.andExpect(jsonPath("$.status").value(404))
|
||||||
|
.andExpect(jsonPath("$.error").value("Not Found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createProjectShouldReturn409OnConflict() throws Exception {
|
||||||
|
var workspaceId = UUID.randomUUID();
|
||||||
|
var request = new ProjectRequest("Duplicate", "desc");
|
||||||
|
when(projectService.createProject(eq(workspaceId), any()))
|
||||||
|
.thenThrow(
|
||||||
|
new ConflictException(
|
||||||
|
"A project with the name 'Duplicate' already exists in this workspace."));
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", workspaceId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isConflict())
|
||||||
|
.andExpect(jsonPath("$.status").value(409))
|
||||||
|
.andExpect(jsonPath("$.error").value("Conflict"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void updateProjectShouldReturn200() throws Exception {
|
||||||
|
var workspaceId = UUID.randomUUID();
|
||||||
|
var projectId = UUID.randomUUID();
|
||||||
|
var request = new ProjectRequest("Updated", "Updated desc");
|
||||||
|
var response =
|
||||||
|
new ProjectResponse(projectId, workspaceId, "Updated", "Updated desc", Instant.now());
|
||||||
|
when(projectService.updateProject(eq(projectId), any())).thenReturn(response);
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
put("/workspaces/{workspaceId}/projects/{projectId}", workspaceId, projectId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.name").value("Updated"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void updateProjectShouldReturn404OnNotFound() throws Exception {
|
||||||
|
var workspaceId = UUID.randomUUID();
|
||||||
|
var projectId = UUID.randomUUID();
|
||||||
|
var request = new ProjectRequest("Updated", "desc");
|
||||||
|
when(projectService.updateProject(eq(projectId), any()))
|
||||||
|
.thenThrow(new NotFoundException("Project with id '" + projectId + "' not found"));
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
put("/workspaces/{workspaceId}/projects/{projectId}", workspaceId, projectId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
.andExpect(jsonPath("$.status").value(404))
|
||||||
|
.andExpect(jsonPath("$.error").value("Not Found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteProjectShouldReturn200() throws Exception {
|
||||||
|
var workspaceId = UUID.randomUUID();
|
||||||
|
var projectId = UUID.randomUUID();
|
||||||
|
doNothing().when(projectService).deleteProject(projectId);
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(delete("/workspaces/{workspaceId}/projects/{projectId}", workspaceId, projectId))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteProjectShouldReturn404OnNotFound() throws Exception {
|
||||||
|
var workspaceId = UUID.randomUUID();
|
||||||
|
var projectId = UUID.randomUUID();
|
||||||
|
doThrow(new NotFoundException("Project with id '" + projectId + "' not found"))
|
||||||
|
.when(projectService)
|
||||||
|
.deleteProject(projectId);
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(delete("/workspaces/{workspaceId}/projects/{projectId}", workspaceId, projectId))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
.andExpect(jsonPath("$.status").value(404))
|
||||||
|
.andExpect(jsonPath("$.error").value("Not Found"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,274 @@
|
|||||||
|
package ai.decompile.project.controller;
|
||||||
|
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||||
|
|
||||||
|
import ai.decompile.workspace.model.dto.ProjectRequest;
|
||||||
|
import ai.decompile.workspace.model.dto.ProjectResponse;
|
||||||
|
import ai.decompile.workspace.model.dto.WorkspaceRequest;
|
||||||
|
import ai.decompile.workspace.model.dto.WorkspaceResponse;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import jakarta.persistence.EntityManager;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
@ActiveProfiles("test")
|
||||||
|
@Transactional
|
||||||
|
class ProjectIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired private MockMvc mockMvc;
|
||||||
|
|
||||||
|
@Autowired private EntityManager entityManager;
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getProjectsShouldReturnEmptyList() throws Exception {
|
||||||
|
var workspaceId = createWorkspace("Empty Workspace");
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces/{workspaceId}/projects", workspaceId))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$").isArray())
|
||||||
|
.andExpect(jsonPath("$").isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getProjectsShouldFilterByName() throws Exception {
|
||||||
|
var workspaceId = createWorkspace("Filter WS");
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", workspaceId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(new ProjectRequest("Alpha", "desc"))))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", workspaceId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(new ProjectRequest("Beta", "desc"))))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces/{workspaceId}/projects", workspaceId).param("name", "alpha"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.length()").value(1))
|
||||||
|
.andExpect(jsonPath("$[0].name").value("Alpha"));
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces/{workspaceId}/projects", workspaceId))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.length()").value(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void fullCrudFlow() throws Exception {
|
||||||
|
var workspaceId = createWorkspace("CRUD Workspace");
|
||||||
|
|
||||||
|
var request = new ProjectRequest("CRUD Project", "CRUD Description");
|
||||||
|
|
||||||
|
var createdJson =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", workspaceId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.name").value("CRUD Project"))
|
||||||
|
.andExpect(jsonPath("$.workspaceId").value(workspaceId.toString()))
|
||||||
|
.andReturn()
|
||||||
|
.getResponse()
|
||||||
|
.getContentAsString();
|
||||||
|
|
||||||
|
var created = objectMapper.readValue(createdJson, ProjectResponse.class);
|
||||||
|
|
||||||
|
var updateRequest = new ProjectRequest("CRUD Updated", "Updated Description");
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
put("/workspaces/{workspaceId}/projects/{projectId}", workspaceId, created.id())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(updateRequest)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.name").value("CRUD Updated"));
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
delete("/workspaces/{workspaceId}/projects/{projectId}", workspaceId, created.id()))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
delete("/workspaces/{workspaceId}/projects/{projectId}", workspaceId, created.id()))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createProjectShouldReturn409OnDuplicateNameInSameWorkspace() throws Exception {
|
||||||
|
var workspaceId = createWorkspace("Dup WS");
|
||||||
|
|
||||||
|
var request = new ProjectRequest("Unique Name", "desc");
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", workspaceId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", workspaceId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isConflict())
|
||||||
|
.andExpect(jsonPath("$.status").value(409))
|
||||||
|
.andExpect(jsonPath("$.error").value("Conflict"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sameNameInDifferentWorkspacesShouldSucceed() throws Exception {
|
||||||
|
var ws1 = createWorkspace("Workspace One");
|
||||||
|
var ws2 = createWorkspace("Workspace Two");
|
||||||
|
|
||||||
|
var request = new ProjectRequest("Same Name", "desc");
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", ws1)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", ws2)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createProjectShouldReturn404WhenWorkspaceDoesNotExist() throws Exception {
|
||||||
|
var nonExistentId = UUID.randomUUID();
|
||||||
|
var request = new ProjectRequest("Project", "desc");
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", nonExistentId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
.andExpect(jsonPath("$.status").value(404));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nonexistentProjectShouldReturn404() throws Exception {
|
||||||
|
var workspaceId = createWorkspace("WS");
|
||||||
|
var nonExistentId = UUID.randomUUID();
|
||||||
|
var request = new ProjectRequest("Update", "desc");
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
put("/workspaces/{workspaceId}/projects/{projectId}", workspaceId, nonExistentId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
.andExpect(jsonPath("$.status").value(404));
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
delete("/workspaces/{workspaceId}/projects/{projectId}", workspaceId, nonExistentId))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
.andExpect(jsonPath("$.status").value(404));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void workspaceListShouldReturnProjectCount() throws Exception {
|
||||||
|
var workspaceId = createWorkspace("Count WS");
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$[0].projectCount").value(0));
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", workspaceId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(new ProjectRequest("P1", "desc"))))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", workspaceId)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(new ProjectRequest("P2", "desc"))))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
entityManager.flush();
|
||||||
|
entityManager.clear();
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$[0].projectCount").value(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void listProjectsForSpecificWorkspace() throws Exception {
|
||||||
|
var ws1 = createWorkspace("WS1");
|
||||||
|
var ws2 = createWorkspace("WS2");
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", ws1)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(new ProjectRequest("Project A", "desc"))))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces/{workspaceId}/projects", ws2)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(new ProjectRequest("Project B", "desc"))))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces/{workspaceId}/projects", ws1))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.length()").value(1))
|
||||||
|
.andExpect(jsonPath("$[0].name").value("Project A"));
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces/{workspaceId}/projects", ws2))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.length()").value(1))
|
||||||
|
.andExpect(jsonPath("$[0].name").value("Project B"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private UUID createWorkspace(String name) throws Exception {
|
||||||
|
var request = new WorkspaceRequest(name, "description");
|
||||||
|
var json =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andReturn()
|
||||||
|
.getResponse()
|
||||||
|
.getContentAsString();
|
||||||
|
return objectMapper.readValue(json, WorkspaceResponse.class).id();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -35,11 +35,11 @@ class WorkspaceControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
void getWorkspacesShouldReturn200WithList() throws Exception {
|
void getWorkspacesShouldReturn200WithList() throws Exception {
|
||||||
var id = UUID.randomUUID();
|
var id = UUID.randomUUID();
|
||||||
var response = new WorkspaceResponse(id, "Test", "Desc", Instant.now());
|
var response = new WorkspaceResponse(id, "Test", "Desc", Instant.now(), 0);
|
||||||
when(workspaceService.getWorkspaces(isNull())).thenReturn(List.of(response));
|
when(workspaceService.getWorkspaces(isNull())).thenReturn(List.of(response));
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(get("/workspace"))
|
.perform(get("/workspaces"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$[0].id").value(id.toString()))
|
.andExpect(jsonPath("$[0].id").value(id.toString()))
|
||||||
.andExpect(jsonPath("$[0].name").value("Test"))
|
.andExpect(jsonPath("$[0].name").value("Test"))
|
||||||
@ -49,26 +49,53 @@ class WorkspaceControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
void getWorkspacesShouldFilterByName() throws Exception {
|
void getWorkspacesShouldFilterByName() throws Exception {
|
||||||
var id = UUID.randomUUID();
|
var id = UUID.randomUUID();
|
||||||
var response = new WorkspaceResponse(id, "Filtered", "Desc", Instant.now());
|
var response = new WorkspaceResponse(id, "Filtered", "Desc", Instant.now(), 0);
|
||||||
when(workspaceService.getWorkspaces(eq("fil"))).thenReturn(List.of(response));
|
when(workspaceService.getWorkspaces(eq("fil"))).thenReturn(List.of(response));
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(get("/workspace").param("name", "fil"))
|
.perform(get("/workspaces").param("name", "fil"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$[0].id").value(id.toString()))
|
.andExpect(jsonPath("$[0].id").value(id.toString()))
|
||||||
.andExpect(jsonPath("$[0].name").value("Filtered"));
|
.andExpect(jsonPath("$[0].name").value("Filtered"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWorkspaceShouldReturn200() throws Exception {
|
||||||
|
var id = UUID.randomUUID();
|
||||||
|
var response = new WorkspaceResponse(id, "Single", "Desc", Instant.now(), 0);
|
||||||
|
when(workspaceService.getWorkspaceById(id)).thenReturn(response);
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces/{id}", id))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.id").value(id.toString()))
|
||||||
|
.andExpect(jsonPath("$.name").value("Single"))
|
||||||
|
.andExpect(jsonPath("$.description").value("Desc"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWorkspaceShouldReturn404OnNotFound() throws Exception {
|
||||||
|
var id = UUID.randomUUID();
|
||||||
|
when(workspaceService.getWorkspaceById(id))
|
||||||
|
.thenThrow(new NotFoundException("Workspace with id '" + id + "' not found"));
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces/{id}", id))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
.andExpect(jsonPath("$.status").value(404))
|
||||||
|
.andExpect(jsonPath("$.error").value("Not Found"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createWorkspaceShouldReturn200() throws Exception {
|
void createWorkspaceShouldReturn200() throws Exception {
|
||||||
var id = UUID.randomUUID();
|
var id = UUID.randomUUID();
|
||||||
var request = new WorkspaceRequest("New Workspace", "A description");
|
var request = new WorkspaceRequest("New Workspace", "A description");
|
||||||
var response = new WorkspaceResponse(id, "New Workspace", "A description", Instant.now());
|
var response = new WorkspaceResponse(id, "New Workspace", "A description", Instant.now(), 0);
|
||||||
when(workspaceService.createWorkspace(any())).thenReturn(response);
|
when(workspaceService.createWorkspace(any())).thenReturn(response);
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
post("/workspace")
|
post("/workspaces")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
@ -84,7 +111,7 @@ class WorkspaceControllerTest {
|
|||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
post("/workspace")
|
post("/workspaces")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
.andExpect(status().isConflict())
|
.andExpect(status().isConflict())
|
||||||
@ -96,12 +123,12 @@ class WorkspaceControllerTest {
|
|||||||
void updateWorkspaceShouldReturn200() throws Exception {
|
void updateWorkspaceShouldReturn200() throws Exception {
|
||||||
var id = UUID.randomUUID();
|
var id = UUID.randomUUID();
|
||||||
var request = new WorkspaceRequest("Updated", "Updated desc");
|
var request = new WorkspaceRequest("Updated", "Updated desc");
|
||||||
var response = new WorkspaceResponse(id, "Updated", "Updated desc", Instant.now());
|
var response = new WorkspaceResponse(id, "Updated", "Updated desc", Instant.now(), 0);
|
||||||
when(workspaceService.updateWorkspace(eq(id), any())).thenReturn(response);
|
when(workspaceService.updateWorkspace(eq(id), any())).thenReturn(response);
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
put("/workspace/{id}", id)
|
put("/workspaces/{id}", id)
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
@ -117,7 +144,7 @@ class WorkspaceControllerTest {
|
|||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
put("/workspace/{id}", id)
|
put("/workspaces/{id}", id)
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
.andExpect(status().isNotFound())
|
.andExpect(status().isNotFound())
|
||||||
@ -130,7 +157,7 @@ class WorkspaceControllerTest {
|
|||||||
var id = UUID.randomUUID();
|
var id = UUID.randomUUID();
|
||||||
doNothing().when(workspaceService).deleteWorkspace(id);
|
doNothing().when(workspaceService).deleteWorkspace(id);
|
||||||
|
|
||||||
mockMvc.perform(delete("/workspace/{id}", id)).andExpect(status().isOk());
|
mockMvc.perform(delete("/workspaces/{id}", id)).andExpect(status().isOk());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -141,7 +168,7 @@ class WorkspaceControllerTest {
|
|||||||
.deleteWorkspace(id);
|
.deleteWorkspace(id);
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(delete("/workspace/{id}", id))
|
.perform(delete("/workspaces/{id}", id))
|
||||||
.andExpect(status().isNotFound())
|
.andExpect(status().isNotFound())
|
||||||
.andExpect(jsonPath("$.status").value(404))
|
.andExpect(jsonPath("$.status").value(404))
|
||||||
.andExpect(jsonPath("$.error").value("Not Found"));
|
.andExpect(jsonPath("$.error").value("Not Found"));
|
||||||
|
|||||||
@ -28,12 +28,37 @@ class WorkspaceIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
void getWorkspacesShouldReturnEmptyList() throws Exception {
|
void getWorkspacesShouldReturnEmptyList() throws Exception {
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(get("/workspace"))
|
.perform(get("/workspaces"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$").isArray())
|
.andExpect(jsonPath("$").isArray())
|
||||||
.andExpect(jsonPath("$").isEmpty());
|
.andExpect(jsonPath("$").isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWorkspaceByIdShouldReturnWorkspace() throws Exception {
|
||||||
|
var request = new WorkspaceRequest("Get By Id", "desc");
|
||||||
|
var createdJson =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/workspaces")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andReturn()
|
||||||
|
.getResponse()
|
||||||
|
.getContentAsString();
|
||||||
|
|
||||||
|
var created = objectMapper.readValue(createdJson, WorkspaceResponse.class);
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces/{id}", created.id()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.id").value(created.id().toString()))
|
||||||
|
.andExpect(jsonPath("$.name").value("Get By Id"))
|
||||||
|
.andExpect(jsonPath("$.description").value("desc"))
|
||||||
|
.andExpect(jsonPath("$.projectCount").value(0));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getWorkspacesShouldFilterByName() throws Exception {
|
void getWorkspacesShouldFilterByName() throws Exception {
|
||||||
var requestA = new WorkspaceRequest("Alpha Project", "desc");
|
var requestA = new WorkspaceRequest("Alpha Project", "desc");
|
||||||
@ -41,20 +66,20 @@ class WorkspaceIntegrationTest {
|
|||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
post("/workspace")
|
post("/workspaces")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(requestA)))
|
.content(objectMapper.writeValueAsString(requestA)))
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
post("/workspace")
|
post("/workspaces")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(requestB)))
|
.content(objectMapper.writeValueAsString(requestB)))
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(get("/workspace").param("name", "alpha"))
|
.perform(get("/workspaces").param("name", "alpha"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$").isArray())
|
.andExpect(jsonPath("$").isArray())
|
||||||
.andExpect(jsonPath("$.length()").value(1))
|
.andExpect(jsonPath("$.length()").value(1))
|
||||||
@ -68,7 +93,7 @@ class WorkspaceIntegrationTest {
|
|||||||
var createdJson =
|
var createdJson =
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
post("/workspace")
|
post("/workspaces")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
@ -82,15 +107,15 @@ class WorkspaceIntegrationTest {
|
|||||||
var updateRequest = new WorkspaceRequest("CRUD Updated", "Updated Description");
|
var updateRequest = new WorkspaceRequest("CRUD Updated", "Updated Description");
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
put("/workspace/{id}", created.id())
|
put("/workspaces/{id}", created.id())
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(updateRequest)))
|
.content(objectMapper.writeValueAsString(updateRequest)))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.name").value("CRUD Updated"));
|
.andExpect(jsonPath("$.name").value("CRUD Updated"));
|
||||||
|
|
||||||
mockMvc.perform(delete("/workspace/{id}", created.id())).andExpect(status().isOk());
|
mockMvc.perform(delete("/workspaces/{id}", created.id())).andExpect(status().isOk());
|
||||||
|
|
||||||
mockMvc.perform(delete("/workspace/{id}", created.id())).andExpect(status().isNotFound());
|
mockMvc.perform(delete("/workspaces/{id}", created.id())).andExpect(status().isNotFound());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -99,14 +124,14 @@ class WorkspaceIntegrationTest {
|
|||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
post("/workspace")
|
post("/workspaces")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
post("/workspace")
|
post("/workspaces")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
.andExpect(status().isConflict())
|
.andExpect(status().isConflict())
|
||||||
@ -119,16 +144,21 @@ class WorkspaceIntegrationTest {
|
|||||||
var nonExistentId = "00000000-0000-0000-0000-000000000000";
|
var nonExistentId = "00000000-0000-0000-0000-000000000000";
|
||||||
var request = new WorkspaceRequest("Update", "desc");
|
var request = new WorkspaceRequest("Update", "desc");
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/workspaces/{id}", nonExistentId))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
.andExpect(jsonPath("$.status").value(404));
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
put("/workspace/{id}", nonExistentId)
|
put("/workspaces/{id}", nonExistentId)
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
.andExpect(status().isNotFound())
|
.andExpect(status().isNotFound())
|
||||||
.andExpect(jsonPath("$.status").value(404));
|
.andExpect(jsonPath("$.status").value(404));
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(delete("/workspace/{id}", nonExistentId))
|
.perform(delete("/workspaces/{id}", nonExistentId))
|
||||||
.andExpect(status().isNotFound())
|
.andExpect(status().isNotFound())
|
||||||
.andExpect(jsonPath("$.status").value(404));
|
.andExpect(jsonPath("$.status").value(404));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,214 @@
|
|||||||
|
package ai.decompile.workspace.service;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import ai.decompile.exception.ConflictException;
|
||||||
|
import ai.decompile.exception.NotFoundException;
|
||||||
|
import ai.decompile.workspace.model.dto.ProjectRequest;
|
||||||
|
import ai.decompile.workspace.model.dto.ProjectResponse;
|
||||||
|
import ai.decompile.workspace.model.entity.Project;
|
||||||
|
import ai.decompile.workspace.model.entity.Workspace;
|
||||||
|
import ai.decompile.workspace.model.repository.ProjectRepository;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
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 org.springframework.data.jpa.domain.Specification;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class ProjectServiceTest {
|
||||||
|
|
||||||
|
@Mock private ProjectRepository projectRepository;
|
||||||
|
|
||||||
|
@Mock private WorkspaceService workspaceService;
|
||||||
|
|
||||||
|
@InjectMocks private ProjectService projectService;
|
||||||
|
|
||||||
|
private UUID workspaceId;
|
||||||
|
private UUID projectId;
|
||||||
|
private Workspace workspace;
|
||||||
|
private Project project;
|
||||||
|
private ProjectRequest request;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
workspaceId = UUID.randomUUID();
|
||||||
|
projectId = UUID.randomUUID();
|
||||||
|
workspace =
|
||||||
|
Workspace.builder()
|
||||||
|
.id(workspaceId)
|
||||||
|
.name("Test Workspace")
|
||||||
|
.description("WS Desc")
|
||||||
|
.createdAt(Instant.now())
|
||||||
|
.updatedAt(Instant.now())
|
||||||
|
.build();
|
||||||
|
project =
|
||||||
|
Project.builder()
|
||||||
|
.id(projectId)
|
||||||
|
.name("Test Project")
|
||||||
|
.description("Project Desc")
|
||||||
|
.workspace(workspace)
|
||||||
|
.createdAt(Instant.now())
|
||||||
|
.updatedAt(Instant.now())
|
||||||
|
.build();
|
||||||
|
request = new ProjectRequest("Test Project", "Project Desc");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getProjectsShouldReturnEmptyList() {
|
||||||
|
when(projectRepository.findAll(any(Specification.class))).thenReturn(List.of());
|
||||||
|
|
||||||
|
List<ProjectResponse> result = projectService.getProjects(workspaceId, null);
|
||||||
|
|
||||||
|
assertTrue(result.isEmpty());
|
||||||
|
verify(projectRepository).findAll(any(Specification.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getProjectsShouldReturnList() {
|
||||||
|
when(projectRepository.findAll(any(Specification.class))).thenReturn(List.of(project));
|
||||||
|
|
||||||
|
List<ProjectResponse> result = projectService.getProjects(workspaceId, null);
|
||||||
|
|
||||||
|
assertEquals(1, result.size());
|
||||||
|
assertEquals(projectId, result.getFirst().id());
|
||||||
|
assertEquals("Test Project", result.getFirst().name());
|
||||||
|
assertEquals(workspaceId, result.getFirst().workspaceId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getProjectsShouldFilterByName() {
|
||||||
|
when(projectRepository.findAll(any(Specification.class))).thenReturn(List.of(project));
|
||||||
|
|
||||||
|
List<ProjectResponse> result = projectService.getProjects(workspaceId, "test");
|
||||||
|
|
||||||
|
assertEquals(1, result.size());
|
||||||
|
assertEquals("Test Project", result.getFirst().name());
|
||||||
|
verify(projectRepository).findAll(any(Specification.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createProjectShouldSucceed() {
|
||||||
|
when(workspaceService.getWorkspace(workspaceId)).thenReturn(workspace);
|
||||||
|
when(projectRepository.existsByNameIgnoreCaseAndWorkspace_Id(request.name(), workspaceId))
|
||||||
|
.thenReturn(false);
|
||||||
|
when(projectRepository.save(any(Project.class))).thenReturn(project);
|
||||||
|
|
||||||
|
ProjectResponse result = projectService.createProject(workspaceId, request);
|
||||||
|
|
||||||
|
assertEquals(projectId, result.id());
|
||||||
|
assertEquals("Test Project", result.name());
|
||||||
|
assertEquals(workspaceId, result.workspaceId());
|
||||||
|
verify(workspaceService).getWorkspace(workspaceId);
|
||||||
|
verify(projectRepository).existsByNameIgnoreCaseAndWorkspace_Id(request.name(), workspaceId);
|
||||||
|
verify(projectRepository).save(any(Project.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createProjectShouldThrowNotFoundWhenWorkspaceDoesNotExist() {
|
||||||
|
when(workspaceService.getWorkspace(workspaceId))
|
||||||
|
.thenThrow(new NotFoundException("Workspace with id '" + workspaceId + "' not found"));
|
||||||
|
|
||||||
|
NotFoundException ex =
|
||||||
|
assertThrows(
|
||||||
|
NotFoundException.class, () -> projectService.createProject(workspaceId, request));
|
||||||
|
|
||||||
|
assertTrue(ex.getMessage().contains(workspaceId.toString()));
|
||||||
|
verify(projectRepository, never()).save(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createProjectShouldThrowConflictWhenNameExistsInWorkspace() {
|
||||||
|
when(workspaceService.getWorkspace(workspaceId)).thenReturn(workspace);
|
||||||
|
when(projectRepository.existsByNameIgnoreCaseAndWorkspace_Id(request.name(), workspaceId))
|
||||||
|
.thenReturn(true);
|
||||||
|
|
||||||
|
ConflictException ex =
|
||||||
|
assertThrows(
|
||||||
|
ConflictException.class, () -> projectService.createProject(workspaceId, request));
|
||||||
|
|
||||||
|
assertTrue(ex.getMessage().contains("Test Project"));
|
||||||
|
verify(projectRepository, never()).save(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void updateProjectShouldSucceed() {
|
||||||
|
when(projectRepository.findById(projectId)).thenReturn(Optional.of(project));
|
||||||
|
when(projectRepository.save(any(Project.class))).thenReturn(project);
|
||||||
|
|
||||||
|
var newRequest = new ProjectRequest("Updated Name", "Updated Desc");
|
||||||
|
ProjectResponse result = projectService.updateProject(projectId, newRequest);
|
||||||
|
|
||||||
|
assertEquals(projectId, result.id());
|
||||||
|
assertEquals("Updated Name", result.name());
|
||||||
|
verify(projectRepository).findById(projectId);
|
||||||
|
verify(projectRepository).save(any(Project.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void updateProjectShouldSucceedWhenNameUnchanged() {
|
||||||
|
when(projectRepository.findById(projectId)).thenReturn(Optional.of(project));
|
||||||
|
when(projectRepository.save(any(Project.class))).thenReturn(project);
|
||||||
|
|
||||||
|
ProjectResponse result = projectService.updateProject(projectId, request);
|
||||||
|
|
||||||
|
assertEquals(projectId, result.id());
|
||||||
|
verify(projectRepository).save(any(Project.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void updateProjectShouldThrowNotFound() {
|
||||||
|
when(projectRepository.findById(projectId)).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
NotFoundException ex =
|
||||||
|
assertThrows(
|
||||||
|
NotFoundException.class, () -> projectService.updateProject(projectId, request));
|
||||||
|
|
||||||
|
assertTrue(ex.getMessage().contains(projectId.toString()));
|
||||||
|
verify(projectRepository, never()).save(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void updateProjectShouldThrowConflictWhenNameExistsInWorkspace() {
|
||||||
|
var newRequest = new ProjectRequest("New Name", "desc");
|
||||||
|
when(projectRepository.findById(projectId)).thenReturn(Optional.of(project));
|
||||||
|
when(projectRepository.existsByNameIgnoreCaseAndWorkspace_Id("New Name", workspaceId))
|
||||||
|
.thenReturn(true);
|
||||||
|
|
||||||
|
ConflictException ex =
|
||||||
|
assertThrows(
|
||||||
|
ConflictException.class, () -> projectService.updateProject(projectId, newRequest));
|
||||||
|
|
||||||
|
assertTrue(ex.getMessage().contains("New Name"));
|
||||||
|
verify(projectRepository, never()).save(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteProjectShouldSucceed() {
|
||||||
|
when(projectRepository.findById(projectId)).thenReturn(Optional.of(project));
|
||||||
|
|
||||||
|
projectService.deleteProject(projectId);
|
||||||
|
|
||||||
|
verify(projectRepository).findById(projectId);
|
||||||
|
verify(projectRepository).delete(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteProjectShouldThrowNotFound() {
|
||||||
|
when(projectRepository.findById(projectId)).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
NotFoundException ex =
|
||||||
|
assertThrows(NotFoundException.class, () -> projectService.deleteProject(projectId));
|
||||||
|
|
||||||
|
assertTrue(ex.getMessage().contains(projectId.toString()));
|
||||||
|
verify(projectRepository, never()).delete((Project) any());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -43,6 +43,7 @@ class WorkspaceServiceTest {
|
|||||||
.description("Test Description")
|
.description("Test Description")
|
||||||
.createdAt(Instant.now())
|
.createdAt(Instant.now())
|
||||||
.updatedAt(Instant.now())
|
.updatedAt(Instant.now())
|
||||||
|
.projects(List.of())
|
||||||
.build();
|
.build();
|
||||||
request = new WorkspaceRequest("Test Workspace", "Test Description");
|
request = new WorkspaceRequest("Test Workspace", "Test Description");
|
||||||
}
|
}
|
||||||
@ -66,6 +67,7 @@ class WorkspaceServiceTest {
|
|||||||
assertEquals(1, result.size());
|
assertEquals(1, result.size());
|
||||||
assertEquals(id, result.getFirst().id());
|
assertEquals(id, result.getFirst().id());
|
||||||
assertEquals("Test Workspace", result.getFirst().name());
|
assertEquals("Test Workspace", result.getFirst().name());
|
||||||
|
assertEquals(0, result.getFirst().projectCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -76,9 +78,32 @@ class WorkspaceServiceTest {
|
|||||||
|
|
||||||
assertEquals(1, result.size());
|
assertEquals(1, result.size());
|
||||||
assertEquals("Test Workspace", result.getFirst().name());
|
assertEquals("Test Workspace", result.getFirst().name());
|
||||||
|
assertEquals(0, result.getFirst().projectCount());
|
||||||
verify(workspaceRepository).findAll(any(Specification.class));
|
verify(workspaceRepository).findAll(any(Specification.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWorkspaceByIdShouldReturnWorkspace() {
|
||||||
|
when(workspaceRepository.findById(id)).thenReturn(Optional.of(workspace));
|
||||||
|
|
||||||
|
WorkspaceResponse result = workspaceService.getWorkspaceById(id);
|
||||||
|
|
||||||
|
assertEquals(id, result.id());
|
||||||
|
assertEquals("Test Workspace", result.name());
|
||||||
|
assertEquals(0, result.projectCount());
|
||||||
|
verify(workspaceRepository).findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWorkspaceByIdShouldThrowNotFound() {
|
||||||
|
when(workspaceRepository.findById(id)).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
NotFoundException ex =
|
||||||
|
assertThrows(NotFoundException.class, () -> workspaceService.getWorkspaceById(id));
|
||||||
|
|
||||||
|
assertTrue(ex.getMessage().contains(id.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createWorkspaceShouldSucceed() {
|
void createWorkspaceShouldSucceed() {
|
||||||
when(workspaceRepository.existsByNameIgnoreCase(request.name())).thenReturn(false);
|
when(workspaceRepository.existsByNameIgnoreCase(request.name())).thenReturn(false);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user