diff --git a/src/main/java/land/leets/domain/portfolio/domain/Portfolio.java b/src/main/java/land/leets/domain/portfolio/domain/Portfolio.java deleted file mode 100644 index a63ec09..0000000 --- a/src/main/java/land/leets/domain/portfolio/domain/Portfolio.java +++ /dev/null @@ -1,73 +0,0 @@ -package land.leets.domain.portfolio.domain; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; -import land.leets.domain.contributor.domain.Contributor; -import land.leets.domain.shared.BaseTimeEntity; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; - -@Entity(name = "portfolios") -@Builder -@Getter -@Setter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor -public class Portfolio extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long portfolioId; - - @Column(nullable = false) - private Long generation; - - @Column(nullable = false) - private String name; - - @Column(nullable = false) - private String summary; - - @Column(columnDefinition = "text", nullable = false) - private String description; - - @Column(nullable = false) - private ProjectType type; - - @Column(nullable = false) - private ProjectScope scope; - - @Column(nullable = false) - private LocalDate startDate; - - @Column(nullable = false) - private LocalDate endDate; - - @Column(nullable = false) - private String serviceUrl; - - @OneToMany(mappedBy = "portfolio", fetch = FetchType.EAGER, orphanRemoval = true) - @JsonIgnore - private List contributors = new ArrayList<>(); - - @Column(nullable = false) - private String logoImgName; - - @Column(nullable = false) - private String mainImgName; - -} diff --git a/src/main/java/land/leets/domain/portfolio/domain/ProjectScope.java b/src/main/java/land/leets/domain/portfolio/domain/ProjectScope.java deleted file mode 100644 index a016b0b..0000000 --- a/src/main/java/land/leets/domain/portfolio/domain/ProjectScope.java +++ /dev/null @@ -1,13 +0,0 @@ -package land.leets.domain.portfolio.domain; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public enum ProjectScope { - TOY("Toy"), - FINAL("Final"); - - private final String projectScope; -} diff --git a/src/main/java/land/leets/domain/portfolio/domain/ProjectType.java b/src/main/java/land/leets/domain/portfolio/domain/ProjectType.java deleted file mode 100644 index ed7cb8b..0000000 --- a/src/main/java/land/leets/domain/portfolio/domain/ProjectType.java +++ /dev/null @@ -1,12 +0,0 @@ -package land.leets.domain.portfolio.domain; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public enum ProjectType { - WEB("WEB"); - - private final String projectType; -} diff --git a/src/main/java/land/leets/domain/portfolio/domain/repository/PortfolioRepository.java b/src/main/java/land/leets/domain/portfolio/domain/repository/PortfolioRepository.java deleted file mode 100644 index 3b9a18f..0000000 --- a/src/main/java/land/leets/domain/portfolio/domain/repository/PortfolioRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package land.leets.domain.portfolio.domain.repository; - -import land.leets.domain.portfolio.domain.Portfolio; -import land.leets.domain.portfolio.domain.ProjectScope; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.List; - -public interface PortfolioRepository extends JpaRepository { - List findAllByGenerationAndScope(Long generation, ProjectScope scope); - - List findAllByScopeOrderByGenerationDesc(ProjectScope scope); -} diff --git a/src/main/java/land/leets/domain/portfolio/exception/PortfolioNotFoundException.java b/src/main/java/land/leets/domain/portfolio/exception/PortfolioNotFoundException.java deleted file mode 100644 index 0ffc07b..0000000 --- a/src/main/java/land/leets/domain/portfolio/exception/PortfolioNotFoundException.java +++ /dev/null @@ -1,10 +0,0 @@ -package land.leets.domain.portfolio.exception; - -import land.leets.global.error.ErrorCode; -import land.leets.global.error.exception.ServiceException; - -public class PortfolioNotFoundException extends ServiceException { - public PortfolioNotFoundException() { - super(ErrorCode.PORTFOLIO_NOT_FOUND); - } -} diff --git a/src/main/java/land/leets/domain/portfolio/presentation/PortfolioController.java b/src/main/java/land/leets/domain/portfolio/presentation/PortfolioController.java deleted file mode 100644 index 96759cf..0000000 --- a/src/main/java/land/leets/domain/portfolio/presentation/PortfolioController.java +++ /dev/null @@ -1,41 +0,0 @@ -package land.leets.domain.portfolio.presentation; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import land.leets.domain.portfolio.presentation.dto.PortfolioResponse; -import land.leets.domain.portfolio.presentation.dto.PortfoliosResponse; -import land.leets.domain.portfolio.usecase.GetPortfolios; -import land.leets.global.error.ErrorResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/portfolios") -public class PortfolioController { - - private final GetPortfolios getPortfolios; - - @Operation(summary = "(비로그인) 포트폴리오 조회", description = "포트폴리오를 조회합니다.") - @ApiResponses({ - @ApiResponse(responseCode = "200"), - @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), - @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), - @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), - @ApiResponse(responseCode = "500", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) - }) - @GetMapping - public List> getAll(@RequestParam(required = false) String generation) { - return getPortfolios.all(generation); - } - - @GetMapping("/{portfolioId}") - public PortfolioResponse get(@PathVariable Long portfolioId) { - return getPortfolios.one(portfolioId); - } -} diff --git a/src/main/java/land/leets/domain/portfolio/presentation/dto/PortfolioResponse.java b/src/main/java/land/leets/domain/portfolio/presentation/dto/PortfolioResponse.java deleted file mode 100644 index a2b1258..0000000 --- a/src/main/java/land/leets/domain/portfolio/presentation/dto/PortfolioResponse.java +++ /dev/null @@ -1,60 +0,0 @@ -package land.leets.domain.portfolio.presentation.dto; - -import land.leets.domain.contributor.domain.Contributor; -import land.leets.domain.portfolio.domain.Portfolio; -import land.leets.domain.portfolio.domain.ProjectScope; -import land.leets.domain.portfolio.domain.ProjectType; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.time.LocalDate; -import java.util.List; - -@Getter -@AllArgsConstructor -public class PortfolioResponse { - - private Long portfolioId; - - private Long generation; - - private String name; - - private String summary; - - private String description; - - private ProjectType type; - - private ProjectScope scope; - - private LocalDate startDate; - - private LocalDate endDate; - - private String serviceUrl; - - private String logoImgName; - - private String mainImgName; - - private List contributors; - - public static PortfolioResponse from(Portfolio portfolio) { - return new PortfolioResponse( - portfolio.getPortfolioId(), - portfolio.getGeneration(), - portfolio.getName(), - portfolio.getSummary(), - portfolio.getDescription(), - portfolio.getType(), - portfolio.getScope(), - portfolio.getStartDate(), - portfolio.getEndDate(), - portfolio.getServiceUrl(), - portfolio.getLogoImgName(), - portfolio.getMainImgName(), - portfolio.getContributors() - ); - } -} diff --git a/src/main/java/land/leets/domain/portfolio/presentation/dto/PortfoliosResponse.java b/src/main/java/land/leets/domain/portfolio/presentation/dto/PortfoliosResponse.java deleted file mode 100644 index fe28786..0000000 --- a/src/main/java/land/leets/domain/portfolio/presentation/dto/PortfoliosResponse.java +++ /dev/null @@ -1,28 +0,0 @@ -package land.leets.domain.portfolio.presentation.dto; - -import jakarta.validation.constraints.NotBlank; -import land.leets.domain.portfolio.domain.Portfolio; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public class PortfoliosResponse { - - @NotBlank - private Long portfolioId; - - @NotBlank - private String name; - - @NotBlank - private String mainImgName; - - public static PortfoliosResponse from(Portfolio portfolio) { - return new PortfoliosResponse( - portfolio.getPortfolioId(), - portfolio.getName(), - portfolio.getMainImgName() - ); - } -} diff --git a/src/main/java/land/leets/domain/portfolio/usecase/GetPortfolios.java b/src/main/java/land/leets/domain/portfolio/usecase/GetPortfolios.java deleted file mode 100644 index b547b36..0000000 --- a/src/main/java/land/leets/domain/portfolio/usecase/GetPortfolios.java +++ /dev/null @@ -1,12 +0,0 @@ -package land.leets.domain.portfolio.usecase; - -import land.leets.domain.portfolio.presentation.dto.PortfolioResponse; -import land.leets.domain.portfolio.presentation.dto.PortfoliosResponse; - -import java.util.List; - -public interface GetPortfolios { - List> all(String generation); - - PortfolioResponse one(Long portfolioId); -} diff --git a/src/main/java/land/leets/domain/portfolio/usecase/GetPortfoliosImpl.java b/src/main/java/land/leets/domain/portfolio/usecase/GetPortfoliosImpl.java deleted file mode 100644 index ba3dc44..0000000 --- a/src/main/java/land/leets/domain/portfolio/usecase/GetPortfoliosImpl.java +++ /dev/null @@ -1,54 +0,0 @@ -package land.leets.domain.portfolio.usecase; - -import land.leets.domain.portfolio.domain.Portfolio; -import land.leets.domain.portfolio.domain.ProjectScope; -import land.leets.domain.portfolio.domain.repository.PortfolioRepository; -import land.leets.domain.portfolio.exception.PortfolioNotFoundException; -import land.leets.domain.portfolio.presentation.dto.PortfolioResponse; -import land.leets.domain.portfolio.presentation.dto.PortfoliosResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.List; - -@Service -@RequiredArgsConstructor -public class GetPortfoliosImpl implements GetPortfolios { - - private final PortfolioRepository portfolioRepository; - - @Override - public List> all(String generation) { - List> response = new ArrayList<>(); - if (generation == null) { - response.add(getPortfoliosByScope(ProjectScope.FINAL)); - response.add(getPortfoliosByScope(ProjectScope.TOY)); - return response; - } - response.add(getPortfoliosByGenerationAndScope(Long.parseLong(generation), ProjectScope.FINAL)); - response.add(getPortfoliosByGenerationAndScope(Long.parseLong(generation), ProjectScope.TOY)); - - return response; - } - - @Override - public PortfolioResponse one(Long portfolioId) { - Portfolio portfolio = portfolioRepository.findById(portfolioId) - .orElseThrow(PortfolioNotFoundException::new); - - return PortfolioResponse.from(portfolio); - } - - private List getPortfoliosByScope(ProjectScope scope) { - return portfolioRepository.findAllByScopeOrderByGenerationDesc(scope).stream() - .map(PortfoliosResponse::from) - .toList(); - } - - private List getPortfoliosByGenerationAndScope(Long generation, ProjectScope scope) { - return portfolioRepository.findAllByGenerationAndScope(generation, scope).stream() - .map(PortfoliosResponse::from) - .toList(); - } -} diff --git a/src/main/kotlin/land/leets/domain/portfolio/domain/Portfolio.kt b/src/main/kotlin/land/leets/domain/portfolio/domain/Portfolio.kt new file mode 100644 index 0000000..06e8140 --- /dev/null +++ b/src/main/kotlin/land/leets/domain/portfolio/domain/Portfolio.kt @@ -0,0 +1,53 @@ +package land.leets.domain.portfolio.domain + +import com.fasterxml.jackson.annotation.JsonIgnore +import jakarta.persistence.* +import land.leets.domain.contributor.domain.Contributor +import land.leets.domain.portfolio.type.ProjectScope +import land.leets.domain.portfolio.type.ProjectType +import land.leets.domain.shared.BaseTimeEntity +import java.time.LocalDate + +@Entity(name = "portfolios") +class Portfolio( + @Column(nullable = false) + val generation: Long, + + @Column(nullable = false) + val name: String, + + @Column(nullable = false) + val summary: String, + + @Column(columnDefinition = "text", nullable = false) + val description: String, + + @Column(nullable = false) + val type: ProjectType, + + @Column(nullable = false) + val scope: ProjectScope, + + @Column(nullable = false) + val startDate: LocalDate, + + @Column(nullable = false) + val endDate: LocalDate, + + @Column(nullable = false) + val serviceUrl: String, + + @Column(nullable = false) + val logoImgName: String, + + @Column(nullable = false) + val mainImgName: String, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val portfolioId: Long = 0L, + + @OneToMany(mappedBy = "portfolio", fetch = FetchType.EAGER, orphanRemoval = true) + @JsonIgnore + val contributors: ArrayList = arrayListOf() +) : BaseTimeEntity() diff --git a/src/main/kotlin/land/leets/domain/portfolio/domain/repository/PortfolioRepository.kt b/src/main/kotlin/land/leets/domain/portfolio/domain/repository/PortfolioRepository.kt new file mode 100644 index 0000000..af6b4cb --- /dev/null +++ b/src/main/kotlin/land/leets/domain/portfolio/domain/repository/PortfolioRepository.kt @@ -0,0 +1,11 @@ +package land.leets.domain.portfolio.domain.repository + +import land.leets.domain.portfolio.domain.Portfolio +import land.leets.domain.portfolio.type.ProjectScope +import org.springframework.data.jpa.repository.JpaRepository + +interface PortfolioRepository : JpaRepository { + fun findAllByGenerationAndScope(generation: Long, scope: ProjectScope): List + + fun findAllByScopeOrderByGenerationDesc(scope: ProjectScope): List +} diff --git a/src/main/kotlin/land/leets/domain/portfolio/exception/PortfolioNotFoundException.kt b/src/main/kotlin/land/leets/domain/portfolio/exception/PortfolioNotFoundException.kt new file mode 100644 index 0000000..1e57b32 --- /dev/null +++ b/src/main/kotlin/land/leets/domain/portfolio/exception/PortfolioNotFoundException.kt @@ -0,0 +1,6 @@ +package land.leets.domain.portfolio.exception + +import land.leets.global.error.ErrorCode +import land.leets.global.error.exception.ServiceException + +class PortfolioNotFoundException : ServiceException(ErrorCode.PORTFOLIO_NOT_FOUND) diff --git a/src/main/kotlin/land/leets/domain/portfolio/presentation/PortfolioController.kt b/src/main/kotlin/land/leets/domain/portfolio/presentation/PortfolioController.kt new file mode 100644 index 0000000..994735f --- /dev/null +++ b/src/main/kotlin/land/leets/domain/portfolio/presentation/PortfolioController.kt @@ -0,0 +1,37 @@ +package land.leets.domain.portfolio.presentation + +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.media.Content +import io.swagger.v3.oas.annotations.media.Schema +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.responses.ApiResponses +import land.leets.domain.portfolio.presentation.dto.PortfolioResponse +import land.leets.domain.portfolio.presentation.dto.PortfoliosResponse +import land.leets.domain.portfolio.usecase.GetPortfolios +import land.leets.global.error.ErrorResponse +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping("/portfolios") +class PortfolioController( + private val getPortfolios: GetPortfolios +) { + + @Operation(summary = "(비로그인) 포트폴리오 조회", description = "포트폴리오를 조회합니다.") + @ApiResponses( + ApiResponse(responseCode = "200"), + ApiResponse(responseCode = "400", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), + ApiResponse(responseCode = "403", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), + ApiResponse(responseCode = "404", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), + ApiResponse(responseCode = "500", content = [Content(schema = Schema(implementation = ErrorResponse::class))]) + ) + @GetMapping + fun getAll(@RequestParam(required = false) generation: String?): List> { + return getPortfolios.all(generation) + } + + @GetMapping("/{portfolioId}") + fun get(@PathVariable portfolioId: Long): PortfolioResponse { + return getPortfolios.one(portfolioId) + } +} diff --git a/src/main/kotlin/land/leets/domain/portfolio/presentation/dto/PortfolioResponse.kt b/src/main/kotlin/land/leets/domain/portfolio/presentation/dto/PortfolioResponse.kt new file mode 100644 index 0000000..6837f6c --- /dev/null +++ b/src/main/kotlin/land/leets/domain/portfolio/presentation/dto/PortfolioResponse.kt @@ -0,0 +1,43 @@ +package land.leets.domain.portfolio.presentation.dto + +import land.leets.domain.contributor.domain.Contributor +import land.leets.domain.portfolio.domain.Portfolio +import land.leets.domain.portfolio.type.ProjectScope +import land.leets.domain.portfolio.type.ProjectType +import java.time.LocalDate + +data class PortfolioResponse( + val portfolioId: Long, + val generation: Long, + val name: String, + val summary: String, + val description: String, + val type: ProjectType, + val scope: ProjectScope, + val startDate: LocalDate, + val endDate: LocalDate, + val serviceUrl: String, + val logoImgName: String, + val mainImgName: String, + val contributors: List +) { + companion object { + fun from(portfolio: Portfolio): PortfolioResponse { + return PortfolioResponse( + portfolioId = portfolio.portfolioId, + generation = portfolio.generation, + name = portfolio.name, + summary = portfolio.summary, + description = portfolio.description, + type = portfolio.type, + scope = portfolio.scope, + startDate = portfolio.startDate, + endDate = portfolio.endDate, + serviceUrl = portfolio.serviceUrl, + logoImgName = portfolio.logoImgName, + mainImgName = portfolio.mainImgName, + contributors = portfolio.contributors + ) + } + } +} diff --git a/src/main/kotlin/land/leets/domain/portfolio/presentation/dto/PortfoliosResponse.kt b/src/main/kotlin/land/leets/domain/portfolio/presentation/dto/PortfoliosResponse.kt new file mode 100644 index 0000000..ce7a7f2 --- /dev/null +++ b/src/main/kotlin/land/leets/domain/portfolio/presentation/dto/PortfoliosResponse.kt @@ -0,0 +1,19 @@ +package land.leets.domain.portfolio.presentation.dto + +import land.leets.domain.portfolio.domain.Portfolio + +data class PortfoliosResponse( + val portfolioId: Long, + val name: String, + val mainImgName: String, +) { + companion object { + fun from(portfolio: Portfolio): PortfoliosResponse { + return PortfoliosResponse( + portfolioId = portfolio.portfolioId, + name = portfolio.name, + mainImgName = portfolio.mainImgName + ) + } + } +} diff --git a/src/main/kotlin/land/leets/domain/portfolio/type/ProjectScope.kt b/src/main/kotlin/land/leets/domain/portfolio/type/ProjectScope.kt new file mode 100644 index 0000000..ed2ebed --- /dev/null +++ b/src/main/kotlin/land/leets/domain/portfolio/type/ProjectScope.kt @@ -0,0 +1,6 @@ +package land.leets.domain.portfolio.type + +enum class ProjectScope(val projectScope: String) { + TOY("Toy"), + FINAL("Final") +} diff --git a/src/main/kotlin/land/leets/domain/portfolio/type/ProjectType.kt b/src/main/kotlin/land/leets/domain/portfolio/type/ProjectType.kt new file mode 100644 index 0000000..119a628 --- /dev/null +++ b/src/main/kotlin/land/leets/domain/portfolio/type/ProjectType.kt @@ -0,0 +1,5 @@ +package land.leets.domain.portfolio.type + +enum class ProjectType(val projectType: String) { + WEB("WEB") +} diff --git a/src/main/kotlin/land/leets/domain/portfolio/usecase/GetPortfolios.kt b/src/main/kotlin/land/leets/domain/portfolio/usecase/GetPortfolios.kt new file mode 100644 index 0000000..5398ff1 --- /dev/null +++ b/src/main/kotlin/land/leets/domain/portfolio/usecase/GetPortfolios.kt @@ -0,0 +1,10 @@ +package land.leets.domain.portfolio.usecase + +import land.leets.domain.portfolio.presentation.dto.PortfolioResponse +import land.leets.domain.portfolio.presentation.dto.PortfoliosResponse + +interface GetPortfolios { + fun all(generation: String?): List> + + fun one(portfolioId: Long): PortfolioResponse +} diff --git a/src/main/kotlin/land/leets/domain/portfolio/usecase/GetPortfoliosImpl.kt b/src/main/kotlin/land/leets/domain/portfolio/usecase/GetPortfoliosImpl.kt new file mode 100644 index 0000000..39d8156 --- /dev/null +++ b/src/main/kotlin/land/leets/domain/portfolio/usecase/GetPortfoliosImpl.kt @@ -0,0 +1,45 @@ +package land.leets.domain.portfolio.usecase + +import land.leets.domain.portfolio.domain.repository.PortfolioRepository +import land.leets.domain.portfolio.exception.PortfolioNotFoundException +import land.leets.domain.portfolio.presentation.dto.PortfolioResponse +import land.leets.domain.portfolio.presentation.dto.PortfoliosResponse +import land.leets.domain.portfolio.type.ProjectScope +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class GetPortfoliosImpl( + private val portfolioRepository: PortfolioRepository +) : GetPortfolios { + + @Transactional(readOnly = true) + override fun all(generation: String?): List> { + return listOf( + getPortfolios(generation?.toLong(), ProjectScope.FINAL), + getPortfolios(generation?.toLong(), ProjectScope.TOY) + ) + } + + @Transactional(readOnly = true) + override fun one(portfolioId: Long): PortfolioResponse { + val portfolio = portfolioRepository.findById(portfolioId) + .orElseThrow { PortfolioNotFoundException() } + + return PortfolioResponse.from(portfolio) + } + + private fun getPortfolios( + generation: Long?, + scope: ProjectScope + ): List { + + val list = if (generation == null) { + portfolioRepository.findAllByScopeOrderByGenerationDesc(scope) + } else { + portfolioRepository.findAllByGenerationAndScope(generation, scope) + } + + return list.map { PortfoliosResponse.from(it) } + } +} diff --git a/src/test/kotlin/land/leets/domain/portfolio/usecase/GetPortfoliosImplTest.kt b/src/test/kotlin/land/leets/domain/portfolio/usecase/GetPortfoliosImplTest.kt new file mode 100644 index 0000000..905a132 --- /dev/null +++ b/src/test/kotlin/land/leets/domain/portfolio/usecase/GetPortfoliosImplTest.kt @@ -0,0 +1,147 @@ +package land.leets.domain.portfolio.usecase + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.mockk +import land.leets.domain.contributor.domain.Contributor +import land.leets.domain.contributor.domain.Position +import land.leets.domain.portfolio.domain.Portfolio +import land.leets.domain.portfolio.domain.repository.PortfolioRepository +import land.leets.domain.portfolio.exception.PortfolioNotFoundException +import land.leets.domain.portfolio.type.ProjectScope +import land.leets.domain.portfolio.type.ProjectType +import java.time.LocalDate +import java.util.* + +class GetPortfoliosImplTest : DescribeSpec({ + val portfolioRepository = mockk() + val getPortfolios = GetPortfoliosImpl(portfolioRepository) + + describe("GetPortfoliosImpl 유스케이스는") { + + context("generation이 null이면") { + val finalPortfolio = createMockPortfolio(1L, "Final Project", ProjectScope.FINAL, 5L) + val toyPortfolio = createMockPortfolio(2L, "Toy Project", ProjectScope.TOY, 5L) + + every { portfolioRepository.findAllByScopeOrderByGenerationDesc(ProjectScope.FINAL) } returns listOf(finalPortfolio) + every { portfolioRepository.findAllByScopeOrderByGenerationDesc(ProjectScope.TOY) } returns listOf(toyPortfolio) + + it("모든 기수의 FINAL, TOY 프로젝트를 각각 조회해야 한다") { + val response = getPortfolios.all(null) + + response.size shouldBe 2 + response[0].size shouldBe 1 // FINAL projects + response[0][0].name shouldBe "Final Project" + response[1].size shouldBe 1 // TOY projects + response[1][0].name shouldBe "Toy Project" + } + } + + context("generation이 주어지면") { + val generation = "5" + val finalPortfolio = createMockPortfolio(1L, "5th Final", ProjectScope.FINAL, 5L) + val toyPortfolio = createMockPortfolio(2L, "5th Toy", ProjectScope.TOY, 5L) + + every { portfolioRepository.findAllByGenerationAndScope(5L, ProjectScope.FINAL) } returns listOf(finalPortfolio) + every { portfolioRepository.findAllByGenerationAndScope(5L, ProjectScope.TOY) } returns listOf(toyPortfolio) + + it("해당 기수의 FINAL, TOY 프로젝트를 각각 조회해야 한다") { + val response = getPortfolios.all(generation) + + response.size shouldBe 2 + response[0].size shouldBe 1 + response[0][0].name shouldBe "5th Final" + response[1].size shouldBe 1 + response[1][0].name shouldBe "5th Toy" + } + } + + context("portfolioId로 단일 포트폴리오를 조회할 때") { + val portfolioId = 1L + val portfolio = createMockPortfolio(portfolioId, "Test Project", ProjectScope.FINAL, 5L) + + every { portfolioRepository.findById(portfolioId) } returns Optional.of(portfolio) + + it("해당 포트폴리오의 상세 정보를 반환해야 한다") { + val response = getPortfolios.one(portfolioId) + + response.portfolioId shouldBe portfolioId + response.name shouldBe "Test Project" + response.generation shouldBe 5L + response.scope shouldBe ProjectScope.FINAL + } + } + + context("포트폴리오가 contributors를 포함할 때") { + val portfolioId = 1L + val contributor1 = Contributor( + name = "이근표", + position = Position.BACK_END, + githubUrl = "https://github.com/rooTiket", + profileUrl = "https://example.com/profile1.jpg", + profile = "Backend Developer" + ) + val contributor2 = Contributor( + name = "홍길동", + position = Position.FRONT_END, + githubUrl = "https://github.com/hong", + profileUrl = "https://example.com/profile2.jpg", + profile = "Frontend Developer" + ) + + val portfolio = createMockPortfolio(portfolioId, "Team Project", ProjectScope.FINAL, 5L) + portfolio.contributors.add(contributor1) + portfolio.contributors.add(contributor2) + + every { portfolioRepository.findById(portfolioId) } returns Optional.of(portfolio) + + it("contributors를 포함한 상세 정보를 반환해야 한다") { + val response = getPortfolios.one(portfolioId) + + response.portfolioId shouldBe portfolioId + response.name shouldBe "Team Project" + response.contributors.size shouldBe 2 + response.contributors[0].name shouldBe "이근표" + response.contributors[0].position shouldBe Position.BACK_END + response.contributors[1].name shouldBe "홍길동" + response.contributors[1].position shouldBe Position.FRONT_END + } + } + + context("존재하지 않는 portfolioId로 조회하면") { + val portfolioId = 999L + + every { portfolioRepository.findById(portfolioId) } returns Optional.empty() + + it("PortfolioNotFoundException을 던져야 한다") { + shouldThrow { + getPortfolios.one(portfolioId) + } + } + } + } +}) + +private fun createMockPortfolio( + id: Long, + name: String, + scope: ProjectScope, + generation: Long +): Portfolio { + return Portfolio( + generation = generation, + name = name, + summary = "Test summary", + description = "Test description", + type = ProjectType.WEB, + scope = scope, + startDate = LocalDate.of(2024, 1, 1), + endDate = LocalDate.of(2024, 12, 31), + serviceUrl = "https://test.com", + logoImgName = "logo.png", + mainImgName = "main.png", + portfolioId = id + ) +}