From b34cc0408259ead53e308f2dca76f7ab43ea6cba Mon Sep 17 00:00:00 2001 From: YudiG12 Date: Thu, 16 Feb 2023 00:56:01 -0300 Subject: [PATCH 1/2] Created project with all requirements testes in unit tests. --- .mvn/wrapper/MavenWrapperDownloader.java | 117 ------------------ .mvn/wrapper/MavenWrapperDownloader.kt | 95 ++++++++++++++ pom.xml | 83 ++++++++++++- .../com/blz/testjava/TestJavaApplication.java | 12 -- .../com/blz/testjava/TestJavaApplication.kt | 10 ++ .../handler/GlobalExceptionHandler.kt | 18 +++ .../service/CreateProductService.kt | 16 +++ .../service/DeleteProductService.kt | 14 +++ .../application/service/GetProductService.kt | 15 +++ .../service/UpdateProductService.kt | 16 +++ .../web/controller/ProductController.kt | 47 +++++++ .../request/CreateProductRequest.kt | 17 +++ .../controller/request/InventoryRequest.kt | 6 + .../web/controller/request/ProductResponse.kt | 28 +++++ .../request/UpdateProductRequest.kt | 16 +++ .../controller/request/WarehouseRequest.kt | 24 ++++ .../testjava/domain/entity/CreateProduct.kt | 12 ++ .../com/blz/testjava/domain/entity/Product.kt | 6 + .../testjava/domain/entity/UpdateProduct.kt | 12 ++ .../blz/testjava/domain/entity/Warehouse.kt | 7 ++ .../testjava/domain/entity/WarehouseType.kt | 5 + .../exception/AlreadyExistsException.kt | 4 + .../domain/exception/NotFoundException.kt | 4 + .../domain/repository/ProductRepository.kt | 13 ++ .../domain/repository/WarehouseRepository.kt | 11 ++ .../domain/usecase/CreateProductUseCase.kt | 25 ++++ .../domain/usecase/DeleteProductUseCase.kt | 24 ++++ .../domain/usecase/GetProductUseCase.kt | 22 ++++ .../domain/usecase/UpdateProductUseCase.kt | 31 +++++ .../blz/testjava/domain/usecase/UseCase.kt | 5 + .../repository/entity/ProductEntity.kt | 23 ++++ .../repository/entity/WarehouseEntity.kt | 34 +++++ .../repository/impl/ProductRepositoryImpl.kt | 30 +++++ .../impl/WarehouseRepositoryImpl.kt | 25 ++++ .../repository/jpa/JpaProductRepository.kt | 6 + .../repository/jpa/JpaWarehouseRepository.kt | 11 ++ src/main/resources/application.yml | 8 ++ .../testjava/TestJavaApplicationTests.java | 16 --- .../blz/testjava/TestJavaApplicationTests.kt | 14 +++ .../usecase/CreateProductUseCaseTest.kt | 72 +++++++++++ .../usecase/DeleteProductUseCaseTest.kt | 41 ++++++ .../domain/usecase/GetProductUseCaseTest.kt | 58 +++++++++ .../usecase/UpdateProductUseCaseTest.kt | 79 ++++++++++++ .../com/blz/testjava/factory/MockFactory.kt | 12 ++ 44 files changed, 995 insertions(+), 149 deletions(-) delete mode 100644 .mvn/wrapper/MavenWrapperDownloader.java create mode 100644 .mvn/wrapper/MavenWrapperDownloader.kt delete mode 100644 src/main/java/br/com/blz/testjava/TestJavaApplication.java create mode 100644 src/main/java/br/com/blz/testjava/TestJavaApplication.kt create mode 100644 src/main/java/br/com/blz/testjava/application/handler/GlobalExceptionHandler.kt create mode 100644 src/main/java/br/com/blz/testjava/application/service/CreateProductService.kt create mode 100644 src/main/java/br/com/blz/testjava/application/service/DeleteProductService.kt create mode 100644 src/main/java/br/com/blz/testjava/application/service/GetProductService.kt create mode 100644 src/main/java/br/com/blz/testjava/application/service/UpdateProductService.kt create mode 100644 src/main/java/br/com/blz/testjava/application/web/controller/ProductController.kt create mode 100644 src/main/java/br/com/blz/testjava/application/web/controller/request/CreateProductRequest.kt create mode 100644 src/main/java/br/com/blz/testjava/application/web/controller/request/InventoryRequest.kt create mode 100644 src/main/java/br/com/blz/testjava/application/web/controller/request/ProductResponse.kt create mode 100644 src/main/java/br/com/blz/testjava/application/web/controller/request/UpdateProductRequest.kt create mode 100644 src/main/java/br/com/blz/testjava/application/web/controller/request/WarehouseRequest.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/entity/CreateProduct.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/entity/Product.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/entity/UpdateProduct.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/entity/Warehouse.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/entity/WarehouseType.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/exception/AlreadyExistsException.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/exception/NotFoundException.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/repository/ProductRepository.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/repository/WarehouseRepository.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/usecase/CreateProductUseCase.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/usecase/DeleteProductUseCase.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/usecase/GetProductUseCase.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/usecase/UpdateProductUseCase.kt create mode 100644 src/main/java/br/com/blz/testjava/domain/usecase/UseCase.kt create mode 100644 src/main/java/br/com/blz/testjava/resource/repository/entity/ProductEntity.kt create mode 100644 src/main/java/br/com/blz/testjava/resource/repository/entity/WarehouseEntity.kt create mode 100644 src/main/java/br/com/blz/testjava/resource/repository/impl/ProductRepositoryImpl.kt create mode 100644 src/main/java/br/com/blz/testjava/resource/repository/impl/WarehouseRepositoryImpl.kt create mode 100644 src/main/java/br/com/blz/testjava/resource/repository/jpa/JpaProductRepository.kt create mode 100644 src/main/java/br/com/blz/testjava/resource/repository/jpa/JpaWarehouseRepository.kt delete mode 100644 src/test/java/br/com/blz/testjava/TestJavaApplicationTests.java create mode 100644 src/test/java/br/com/blz/testjava/TestJavaApplicationTests.kt create mode 100644 src/test/java/br/com/blz/testjava/domain/usecase/CreateProductUseCaseTest.kt create mode 100644 src/test/java/br/com/blz/testjava/domain/usecase/DeleteProductUseCaseTest.kt create mode 100644 src/test/java/br/com/blz/testjava/domain/usecase/GetProductUseCaseTest.kt create mode 100644 src/test/java/br/com/blz/testjava/domain/usecase/UpdateProductUseCaseTest.kt create mode 100644 src/test/java/br/com/blz/testjava/factory/MockFactory.kt diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index 1ef8d698..00000000 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.4"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + " .jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/.mvn/wrapper/MavenWrapperDownloader.kt b/.mvn/wrapper/MavenWrapperDownloader.kt new file mode 100644 index 00000000..47e9c02f --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.kt @@ -0,0 +1,95 @@ +import java.util.Properties + +object MavenWrapperDownloader { + private val WRAPPER_VERSION: String? = "0.5.4" + + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private val DEFAULT_DOWNLOAD_URL: String? = ("https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + " .jar") + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private val MAVEN_WRAPPER_PROPERTIES_PATH: String? = ".mvn/wrapper/maven-wrapper.properties" + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private val MAVEN_WRAPPER_JAR_PATH: String? = ".mvn/wrapper/maven-wrapper.jar" + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private val PROPERTY_NAME_WRAPPER_URL: String? = "wrapperUrl" + fun main(args: Array?) { + System.out.println("- Downloader started") + val baseDirectory = File(args.get(0)) + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()) + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + val mavenWrapperPropertyFile = File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH) + var url = DEFAULT_DOWNLOAD_URL + if (mavenWrapperPropertyFile.exists()) { + var mavenWrapperPropertyFileInputStream: FileInputStream? = null + try { + mavenWrapperPropertyFileInputStream = FileInputStream(mavenWrapperPropertyFile) + val mavenWrapperProperties = Properties() + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream) + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url) + } catch (e: IOException) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'") + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close() + } + } catch (e: IOException) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: $url") + val outputFile = File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH) + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'") + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()) + try { + downloadFileFromURL(url, outputFile) + System.out.println("Done") + System.exit(0) + } catch (e: Throwable) { + System.out.println("- Error downloading") + e.printStackTrace() + System.exit(1) + } + } + + @Throws(Exception::class) + private fun downloadFileFromURL(urlString: String?, destination: File?) { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + val username: String = System.getenv("MVNW_USERNAME") + val password: CharArray = System.getenv("MVNW_PASSWORD").toCharArray() + Authenticator.setDefault(object : Authenticator() { + @Override + protected fun getPasswordAuthentication(): PasswordAuthentication? { + return PasswordAuthentication(username, password) + } + }) + } + val website = URL(urlString) + val rbc: ReadableByteChannel + rbc = Channels.newChannel(website.openStream()) + val fos = FileOutputStream(destination) + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE) + fos.close() + rbc.close() + } +} diff --git a/pom.xml b/pom.xml index 0c508267..a5cb2397 100644 --- a/pom.xml +++ b/pom.xml @@ -23,20 +23,51 @@ UTF-8 UTF-8 1.8 - + 1.8.10 + org.springframework.boot spring-boot-starter-web - org.springframework.boot spring-boot-starter-test test - + + org.springframework.boot + spring-boot-starter-data-jpa + 2.2.2.RELEASE + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + junit + junit + test + + + org.hsqldb + hsqldb + 2.7.1 + runtime + + + com.fasterxml.jackson.module + jackson-module-kotlin + 2.14.2 + + @@ -44,7 +75,51 @@ org.springframework.boot spring-boot-maven-plugin - + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + 1.8 + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + diff --git a/src/main/java/br/com/blz/testjava/TestJavaApplication.java b/src/main/java/br/com/blz/testjava/TestJavaApplication.java deleted file mode 100644 index f12c87db..00000000 --- a/src/main/java/br/com/blz/testjava/TestJavaApplication.java +++ /dev/null @@ -1,12 +0,0 @@ -package br.com.blz.testjava; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication(scanBasePackageClasses = TestJavaApplication.class) -public class TestJavaApplication { - - public static void main(String[] args) { - SpringApplication.run(TestJavaApplication.class, args); - } -} diff --git a/src/main/java/br/com/blz/testjava/TestJavaApplication.kt b/src/main/java/br/com/blz/testjava/TestJavaApplication.kt new file mode 100644 index 00000000..25acbcb8 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/TestJavaApplication.kt @@ -0,0 +1,10 @@ +package br.com.blz.testjava + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication + +@SpringBootApplication(scanBasePackageClasses = [TestJavaApplication::class]) +open class TestJavaApplication +fun main(args: Array) { + runApplication(*args) +} diff --git a/src/main/java/br/com/blz/testjava/application/handler/GlobalExceptionHandler.kt b/src/main/java/br/com/blz/testjava/application/handler/GlobalExceptionHandler.kt new file mode 100644 index 00000000..cea7fed4 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/application/handler/GlobalExceptionHandler.kt @@ -0,0 +1,18 @@ +package br.com.blz.testjava.application.handler + +import br.com.blz.testjava.domain.exception.AlreadyExistsException +import br.com.blz.testjava.domain.exception.NotFoundException +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.ControllerAdvice +import org.springframework.web.bind.annotation.ExceptionHandler + +@ControllerAdvice +class GlobalExceptionHandler { + + @ExceptionHandler(value = [(AlreadyExistsException::class)]) + fun handleAlreadyExistsException(ex: Exception) = ResponseEntity.status(HttpStatus.CONFLICT).body(ex.message) + + @ExceptionHandler(value = [(NotFoundException::class)]) + fun handleNotFoundException(ex: Exception) = ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.message) +} diff --git a/src/main/java/br/com/blz/testjava/application/service/CreateProductService.kt b/src/main/java/br/com/blz/testjava/application/service/CreateProductService.kt new file mode 100644 index 00000000..05b3330f --- /dev/null +++ b/src/main/java/br/com/blz/testjava/application/service/CreateProductService.kt @@ -0,0 +1,16 @@ +package br.com.blz.testjava.application.service + +import br.com.blz.testjava.application.web.controller.request.CreateProductRequest +import br.com.blz.testjava.application.web.controller.request.ProductResponse +import br.com.blz.testjava.domain.usecase.CreateProductUseCase +import org.springframework.stereotype.Service + +@Service +class CreateProductService( + private val createProductUseCase: CreateProductUseCase +) { + + fun createProduct(createProductRequest: CreateProductRequest): ProductResponse { + return createProductUseCase.execute(createProductRequest.toDomain()) + } +} diff --git a/src/main/java/br/com/blz/testjava/application/service/DeleteProductService.kt b/src/main/java/br/com/blz/testjava/application/service/DeleteProductService.kt new file mode 100644 index 00000000..057fbd7d --- /dev/null +++ b/src/main/java/br/com/blz/testjava/application/service/DeleteProductService.kt @@ -0,0 +1,14 @@ +package br.com.blz.testjava.application.service + +import br.com.blz.testjava.domain.usecase.DeleteProductUseCase +import org.springframework.stereotype.Service + +@Service +class DeleteProductService( + private val deleteProductUseCase: DeleteProductUseCase +) { + + fun deleteProduct(sku: String) { + deleteProductUseCase.execute(sku) + } +} diff --git a/src/main/java/br/com/blz/testjava/application/service/GetProductService.kt b/src/main/java/br/com/blz/testjava/application/service/GetProductService.kt new file mode 100644 index 00000000..40885191 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/application/service/GetProductService.kt @@ -0,0 +1,15 @@ +package br.com.blz.testjava.application.service + +import br.com.blz.testjava.application.web.controller.request.ProductResponse +import br.com.blz.testjava.domain.usecase.GetProductUseCase +import org.springframework.stereotype.Service + +@Service +class GetProductService( + private val getProductUseCase: GetProductUseCase +) { + + fun getProduct(sku: String): ProductResponse { + return getProductUseCase.execute(sku) + } +} diff --git a/src/main/java/br/com/blz/testjava/application/service/UpdateProductService.kt b/src/main/java/br/com/blz/testjava/application/service/UpdateProductService.kt new file mode 100644 index 00000000..5ef07021 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/application/service/UpdateProductService.kt @@ -0,0 +1,16 @@ +package br.com.blz.testjava.application.service + +import br.com.blz.testjava.application.web.controller.request.ProductResponse +import br.com.blz.testjava.application.web.controller.request.UpdateProductRequest +import br.com.blz.testjava.domain.usecase.UpdateProductUseCase +import org.springframework.stereotype.Service + +@Service +class UpdateProductService( + private val updateProductUseCase: UpdateProductUseCase +) { + + fun updateProduct(sku: String, updateProductRequest: UpdateProductRequest): ProductResponse { + return updateProductUseCase.execute(updateProductRequest.toDomain(sku.toInt())) + } +} diff --git a/src/main/java/br/com/blz/testjava/application/web/controller/ProductController.kt b/src/main/java/br/com/blz/testjava/application/web/controller/ProductController.kt new file mode 100644 index 00000000..d96748f5 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/application/web/controller/ProductController.kt @@ -0,0 +1,47 @@ +package br.com.blz.testjava.application.web.controller + +import br.com.blz.testjava.application.service.CreateProductService +import br.com.blz.testjava.application.service.DeleteProductService +import br.com.blz.testjava.application.service.GetProductService +import br.com.blz.testjava.application.service.UpdateProductService +import br.com.blz.testjava.application.web.controller.request.CreateProductRequest +import br.com.blz.testjava.application.web.controller.request.ProductResponse +import br.com.blz.testjava.application.web.controller.request.UpdateProductRequest +import org.springframework.web.bind.annotation.* + +@RestController +class ProductController( + private val createProductService: CreateProductService, + private val getProductService: GetProductService, + private val deleteProductService: DeleteProductService, + private val updateProductService: UpdateProductService +) { + @GetMapping("/product/{sku}") + fun getProductBySku( + @PathVariable("sku") sku: String + ): ProductResponse { + return getProductService.getProduct(sku) + } + + @PostMapping("/product") + fun createProduct( + @RequestBody createProductRequest: CreateProductRequest + ): ProductResponse { + return createProductService.createProduct(createProductRequest) + } + + @DeleteMapping("/product/{sku}") + fun deleteProduct( + @PathVariable("sku") sku: String + ) { + deleteProductService.deleteProduct(sku) + } + + @PutMapping("/product/{sku}") + fun deleteProduct( + @PathVariable("sku") sku: String, + @RequestBody updateProductRequest: UpdateProductRequest + ) { + updateProductService.updateProduct(sku, updateProductRequest) + } +} diff --git a/src/main/java/br/com/blz/testjava/application/web/controller/request/CreateProductRequest.kt b/src/main/java/br/com/blz/testjava/application/web/controller/request/CreateProductRequest.kt new file mode 100644 index 00000000..83e12aaa --- /dev/null +++ b/src/main/java/br/com/blz/testjava/application/web/controller/request/CreateProductRequest.kt @@ -0,0 +1,17 @@ +package br.com.blz.testjava.application.web.controller.request + +import br.com.blz.testjava.domain.entity.CreateProduct +import java.io.Serializable + +data class CreateProductRequest( + val sku: Int, + val name: String, + val inventory: InventoryRequest +): Serializable { + + fun toDomain() = CreateProduct( + sku = sku, + name = name, + inventory = inventory.warehouses.map { it.toDomain() } + ) +} diff --git a/src/main/java/br/com/blz/testjava/application/web/controller/request/InventoryRequest.kt b/src/main/java/br/com/blz/testjava/application/web/controller/request/InventoryRequest.kt new file mode 100644 index 00000000..1e11517e --- /dev/null +++ b/src/main/java/br/com/blz/testjava/application/web/controller/request/InventoryRequest.kt @@ -0,0 +1,6 @@ +package br.com.blz.testjava.application.web.controller.request + +data class InventoryRequest( + val quantity: Int, + val warehouses: List +) diff --git a/src/main/java/br/com/blz/testjava/application/web/controller/request/ProductResponse.kt b/src/main/java/br/com/blz/testjava/application/web/controller/request/ProductResponse.kt new file mode 100644 index 00000000..fe94490c --- /dev/null +++ b/src/main/java/br/com/blz/testjava/application/web/controller/request/ProductResponse.kt @@ -0,0 +1,28 @@ +package br.com.blz.testjava.application.web.controller.request + +import br.com.blz.testjava.domain.entity.Product +import br.com.blz.testjava.domain.entity.Warehouse +import java.io.Serializable + +data class ProductResponse( + val sku: Int, + val name: String, + val inventory: InventoryRequest, + val isMarketable: Boolean +): Serializable { + companion object { + fun fromDomain(product: Product, warehouseList: List): ProductResponse { + val quantity = warehouseList.sumOf { it.quantity } + + return ProductResponse( + sku = product.sku, + name = product.name, + inventory = InventoryRequest( + quantity = quantity, + warehouses = warehouseList.map { WarehouseRequest.fromDomain(it) } + ), + isMarketable = quantity > 0 + ) + } + } +} diff --git a/src/main/java/br/com/blz/testjava/application/web/controller/request/UpdateProductRequest.kt b/src/main/java/br/com/blz/testjava/application/web/controller/request/UpdateProductRequest.kt new file mode 100644 index 00000000..aefe093a --- /dev/null +++ b/src/main/java/br/com/blz/testjava/application/web/controller/request/UpdateProductRequest.kt @@ -0,0 +1,16 @@ +package br.com.blz.testjava.application.web.controller.request + +import br.com.blz.testjava.domain.entity.UpdateProduct +import java.io.Serializable + +data class UpdateProductRequest( + val name: String, + val inventory: InventoryRequest +): Serializable { + + fun toDomain(sku: Int) = UpdateProduct( + sku = sku, + name = name, + inventory = inventory.warehouses.map { it.toDomain() } + ) +} diff --git a/src/main/java/br/com/blz/testjava/application/web/controller/request/WarehouseRequest.kt b/src/main/java/br/com/blz/testjava/application/web/controller/request/WarehouseRequest.kt new file mode 100644 index 00000000..154dc85b --- /dev/null +++ b/src/main/java/br/com/blz/testjava/application/web/controller/request/WarehouseRequest.kt @@ -0,0 +1,24 @@ +package br.com.blz.testjava.application.web.controller.request + +import br.com.blz.testjava.domain.entity.Warehouse +import br.com.blz.testjava.domain.entity.WarehouseType + +data class WarehouseRequest( + val locality: String, + val quantity: Int, + val type: String +) { + fun toDomain() = Warehouse( + locality = locality, + quantity = quantity, + type = WarehouseType.valueOf(type) + ) + + companion object { + fun fromDomain(warehouse: Warehouse) = WarehouseRequest( + locality = warehouse.locality, + quantity = warehouse.quantity, + type = warehouse.type.name + ) + } +} diff --git a/src/main/java/br/com/blz/testjava/domain/entity/CreateProduct.kt b/src/main/java/br/com/blz/testjava/domain/entity/CreateProduct.kt new file mode 100644 index 00000000..23b33c53 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/entity/CreateProduct.kt @@ -0,0 +1,12 @@ +package br.com.blz.testjava.domain.entity + +data class CreateProduct ( + val sku: Int, + val name: String, + val inventory: List +) { + fun toProduct() = Product( + sku = sku, + name = name + ) +} diff --git a/src/main/java/br/com/blz/testjava/domain/entity/Product.kt b/src/main/java/br/com/blz/testjava/domain/entity/Product.kt new file mode 100644 index 00000000..88b32f1b --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/entity/Product.kt @@ -0,0 +1,6 @@ +package br.com.blz.testjava.domain.entity + +data class Product ( + val sku: Int, + val name: String, +) diff --git a/src/main/java/br/com/blz/testjava/domain/entity/UpdateProduct.kt b/src/main/java/br/com/blz/testjava/domain/entity/UpdateProduct.kt new file mode 100644 index 00000000..320e62c6 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/entity/UpdateProduct.kt @@ -0,0 +1,12 @@ +package br.com.blz.testjava.domain.entity + +data class UpdateProduct ( + val sku: Int, + val name: String, + val inventory: List +) { + fun toProduct() = Product( + sku = sku, + name = name + ) +} diff --git a/src/main/java/br/com/blz/testjava/domain/entity/Warehouse.kt b/src/main/java/br/com/blz/testjava/domain/entity/Warehouse.kt new file mode 100644 index 00000000..3985533a --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/entity/Warehouse.kt @@ -0,0 +1,7 @@ +package br.com.blz.testjava.domain.entity + +data class Warehouse ( + val locality: String, + val quantity: Int, + val type: WarehouseType +) diff --git a/src/main/java/br/com/blz/testjava/domain/entity/WarehouseType.kt b/src/main/java/br/com/blz/testjava/domain/entity/WarehouseType.kt new file mode 100644 index 00000000..fbf6d31e --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/entity/WarehouseType.kt @@ -0,0 +1,5 @@ +package br.com.blz.testjava.domain.entity + +enum class WarehouseType { + ECOMMERCE, PHYSICAL_STORE +} diff --git a/src/main/java/br/com/blz/testjava/domain/exception/AlreadyExistsException.kt b/src/main/java/br/com/blz/testjava/domain/exception/AlreadyExistsException.kt new file mode 100644 index 00000000..ea5aacec --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/exception/AlreadyExistsException.kt @@ -0,0 +1,4 @@ +package br.com.blz.testjava.domain.exception + +class AlreadyExistsException(override val message: String?): Exception(message) { +} diff --git a/src/main/java/br/com/blz/testjava/domain/exception/NotFoundException.kt b/src/main/java/br/com/blz/testjava/domain/exception/NotFoundException.kt new file mode 100644 index 00000000..f93b3737 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/exception/NotFoundException.kt @@ -0,0 +1,4 @@ +package br.com.blz.testjava.domain.exception + +class NotFoundException(override val message: String?): Exception(message) { +} diff --git a/src/main/java/br/com/blz/testjava/domain/repository/ProductRepository.kt b/src/main/java/br/com/blz/testjava/domain/repository/ProductRepository.kt new file mode 100644 index 00000000..ca8707a9 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/repository/ProductRepository.kt @@ -0,0 +1,13 @@ +package br.com.blz.testjava.domain.repository + +import br.com.blz.testjava.domain.entity.Product + +interface ProductRepository { + fun save(product: Product): Product + + fun findById(sku: String): Product? + + fun existsById(sku: Int): Boolean + + fun deleteById(sku: Int) +} diff --git a/src/main/java/br/com/blz/testjava/domain/repository/WarehouseRepository.kt b/src/main/java/br/com/blz/testjava/domain/repository/WarehouseRepository.kt new file mode 100644 index 00000000..0e6d7d5c --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/repository/WarehouseRepository.kt @@ -0,0 +1,11 @@ +package br.com.blz.testjava.domain.repository + +import br.com.blz.testjava.domain.entity.Warehouse + +interface WarehouseRepository { + fun save(warehouse: Warehouse, sku: Int): Warehouse + + fun findBySku(sku: Int): List + + fun deleteBySku(sku: Int) +} diff --git a/src/main/java/br/com/blz/testjava/domain/usecase/CreateProductUseCase.kt b/src/main/java/br/com/blz/testjava/domain/usecase/CreateProductUseCase.kt new file mode 100644 index 00000000..b788f1d1 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/usecase/CreateProductUseCase.kt @@ -0,0 +1,25 @@ +package br.com.blz.testjava.domain.usecase + +import br.com.blz.testjava.application.web.controller.request.ProductResponse +import br.com.blz.testjava.domain.entity.CreateProduct +import br.com.blz.testjava.domain.exception.AlreadyExistsException +import br.com.blz.testjava.domain.repository.ProductRepository +import br.com.blz.testjava.domain.repository.WarehouseRepository +import org.springframework.stereotype.Component + +@Component +class CreateProductUseCase ( + private val productRepository: ProductRepository, + private val warehouseRepository: WarehouseRepository +): UseCase { + override fun execute(input: CreateProduct): ProductResponse { + if (productRepository.existsById(input.sku)) { + throw AlreadyExistsException("SKU ${input.sku} already exists.") + } + val savedProduct = productRepository.save(input.toProduct()) + val savedWarehouses = input.inventory.map { + warehouseRepository.save(it, input.sku) + } + return ProductResponse.fromDomain(savedProduct, savedWarehouses) + } +} diff --git a/src/main/java/br/com/blz/testjava/domain/usecase/DeleteProductUseCase.kt b/src/main/java/br/com/blz/testjava/domain/usecase/DeleteProductUseCase.kt new file mode 100644 index 00000000..bef96b25 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/usecase/DeleteProductUseCase.kt @@ -0,0 +1,24 @@ +package br.com.blz.testjava.domain.usecase + +import br.com.blz.testjava.domain.exception.NotFoundException +import br.com.blz.testjava.domain.repository.ProductRepository +import br.com.blz.testjava.domain.repository.WarehouseRepository +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional + +@Component +open class DeleteProductUseCase ( + private val productRepository: ProductRepository, + private val warehouseRepository: WarehouseRepository +): UseCase { + + @Transactional + override fun execute(input: String) { + productRepository.findById(input)?.let { + productRepository.deleteById(input.toInt()) + warehouseRepository.deleteBySku(input.toInt()) + return + } + throw NotFoundException("Not found product for SKU $input") + } +} diff --git a/src/main/java/br/com/blz/testjava/domain/usecase/GetProductUseCase.kt b/src/main/java/br/com/blz/testjava/domain/usecase/GetProductUseCase.kt new file mode 100644 index 00000000..00aadf62 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/usecase/GetProductUseCase.kt @@ -0,0 +1,22 @@ +package br.com.blz.testjava.domain.usecase + +import br.com.blz.testjava.application.web.controller.request.ProductResponse +import br.com.blz.testjava.domain.exception.NotFoundException +import br.com.blz.testjava.domain.repository.ProductRepository +import br.com.blz.testjava.domain.repository.WarehouseRepository +import org.springframework.stereotype.Component + +@Component +class GetProductUseCase ( + private val productRepository: ProductRepository, + private val warehouseRepository: WarehouseRepository +): UseCase { + + override fun execute(input: String): ProductResponse { + productRepository.findById(input)?.let { + val warehouses = warehouseRepository.findBySku(input.toInt()) + return ProductResponse.fromDomain(it, warehouses) + } + throw NotFoundException("Not found product for SKU $input") + } +} diff --git a/src/main/java/br/com/blz/testjava/domain/usecase/UpdateProductUseCase.kt b/src/main/java/br/com/blz/testjava/domain/usecase/UpdateProductUseCase.kt new file mode 100644 index 00000000..27cd9d23 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/usecase/UpdateProductUseCase.kt @@ -0,0 +1,31 @@ +package br.com.blz.testjava.domain.usecase + +import br.com.blz.testjava.application.web.controller.request.ProductResponse +import br.com.blz.testjava.domain.entity.UpdateProduct +import br.com.blz.testjava.domain.exception.NotFoundException +import br.com.blz.testjava.domain.repository.ProductRepository +import br.com.blz.testjava.domain.repository.WarehouseRepository +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional + +@Component +open class UpdateProductUseCase ( + private val productRepository: ProductRepository, + private val warehouseRepository: WarehouseRepository +): UseCase { + + @Transactional + override fun execute(input: UpdateProduct): ProductResponse { + productRepository.findById(input.sku.toString())?.let { + val savedProduct = productRepository.save(input.toProduct()) + + warehouseRepository.deleteBySku(it.sku) + val savedWarehouses = input.inventory.map { warehouse -> + warehouseRepository.save(warehouse, input.sku) + } + + return ProductResponse.fromDomain(savedProduct, savedWarehouses) + } + throw NotFoundException("Not found product for SKU ${input.sku}") + } +} diff --git a/src/main/java/br/com/blz/testjava/domain/usecase/UseCase.kt b/src/main/java/br/com/blz/testjava/domain/usecase/UseCase.kt new file mode 100644 index 00000000..94eacb7e --- /dev/null +++ b/src/main/java/br/com/blz/testjava/domain/usecase/UseCase.kt @@ -0,0 +1,5 @@ +package br.com.blz.testjava.domain.usecase + +interface UseCase { + fun execute(input: Input): Output +} diff --git a/src/main/java/br/com/blz/testjava/resource/repository/entity/ProductEntity.kt b/src/main/java/br/com/blz/testjava/resource/repository/entity/ProductEntity.kt new file mode 100644 index 00000000..721f0579 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/resource/repository/entity/ProductEntity.kt @@ -0,0 +1,23 @@ +package br.com.blz.testjava.resource.repository.entity + +import br.com.blz.testjava.domain.entity.Product +import javax.persistence.Entity +import javax.persistence.Id + +@Entity +class ProductEntity( + @Id + val sku: Int = 0, + val name: String = "", +) { + fun toDomain() = Product( + sku = sku, + name = name + ) + companion object { + fun fromDomain(product: Product) = ProductEntity( + sku = product.sku, + name = product.name + ) + } +} diff --git a/src/main/java/br/com/blz/testjava/resource/repository/entity/WarehouseEntity.kt b/src/main/java/br/com/blz/testjava/resource/repository/entity/WarehouseEntity.kt new file mode 100644 index 00000000..6794cfe9 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/resource/repository/entity/WarehouseEntity.kt @@ -0,0 +1,34 @@ +package br.com.blz.testjava.resource.repository.entity + +import br.com.blz.testjava.domain.entity.Warehouse +import br.com.blz.testjava.domain.entity.WarehouseType +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.GenerationType +import javax.persistence.Id + +@Entity +class WarehouseEntity( + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + val id: Int = 0, + + val locality: String = "", + val quantity: Int = 0, + val type: String = "", + val sku: Int = 0 +) { + fun toDomain() = Warehouse( + locality = locality, + quantity = quantity, + type = WarehouseType.valueOf(type) + ) + companion object { + fun fromDomain(warehouse: Warehouse, sku: Int = 0) = WarehouseEntity( + locality = warehouse.locality, + quantity = warehouse.quantity, + type = warehouse.type.name, + sku = sku + ) + } +} diff --git a/src/main/java/br/com/blz/testjava/resource/repository/impl/ProductRepositoryImpl.kt b/src/main/java/br/com/blz/testjava/resource/repository/impl/ProductRepositoryImpl.kt new file mode 100644 index 00000000..dc893b34 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/resource/repository/impl/ProductRepositoryImpl.kt @@ -0,0 +1,30 @@ +package br.com.blz.testjava.resource.repository.impl + +import br.com.blz.testjava.domain.entity.Product +import br.com.blz.testjava.domain.repository.ProductRepository +import br.com.blz.testjava.resource.repository.entity.ProductEntity +import br.com.blz.testjava.resource.repository.jpa.JpaProductRepository +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Repository + +@Repository +open class ProductRepositoryImpl ( + private val jpa: JpaProductRepository +) : ProductRepository { + override fun save(product: Product): Product { + return jpa.save(ProductEntity.fromDomain(product)).toDomain() + } + + override fun findById(sku: String): Product? { + return jpa.findByIdOrNull(sku.toInt())?.toDomain() + } + + override fun existsById(sku: Int): Boolean { + return jpa.existsById(sku) + } + + override fun deleteById(sku: Int) { + jpa.deleteById(sku) + } + +} diff --git a/src/main/java/br/com/blz/testjava/resource/repository/impl/WarehouseRepositoryImpl.kt b/src/main/java/br/com/blz/testjava/resource/repository/impl/WarehouseRepositoryImpl.kt new file mode 100644 index 00000000..b9c96e93 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/resource/repository/impl/WarehouseRepositoryImpl.kt @@ -0,0 +1,25 @@ +package br.com.blz.testjava.resource.repository.impl + +import br.com.blz.testjava.domain.entity.Warehouse +import br.com.blz.testjava.domain.repository.WarehouseRepository +import br.com.blz.testjava.resource.repository.entity.WarehouseEntity +import br.com.blz.testjava.resource.repository.jpa.JpaWarehouseRepository +import org.springframework.stereotype.Repository + +@Repository +open class WarehouseRepositoryImpl ( + private val jpa: JpaWarehouseRepository +) : WarehouseRepository { + override fun save(warehouse: Warehouse, sku: Int): Warehouse { + return jpa.save(WarehouseEntity.fromDomain(warehouse, sku)).toDomain() + } + + override fun findBySku(sku: Int): List { + return jpa.findBySku(sku).map { it.toDomain() } + } + + override fun deleteBySku(sku: Int) { + jpa.deleteBySku(sku) + } + +} diff --git a/src/main/java/br/com/blz/testjava/resource/repository/jpa/JpaProductRepository.kt b/src/main/java/br/com/blz/testjava/resource/repository/jpa/JpaProductRepository.kt new file mode 100644 index 00000000..f3933748 --- /dev/null +++ b/src/main/java/br/com/blz/testjava/resource/repository/jpa/JpaProductRepository.kt @@ -0,0 +1,6 @@ +package br.com.blz.testjava.resource.repository.jpa + +import br.com.blz.testjava.resource.repository.entity.ProductEntity +import org.springframework.data.repository.CrudRepository + +interface JpaProductRepository : CrudRepository diff --git a/src/main/java/br/com/blz/testjava/resource/repository/jpa/JpaWarehouseRepository.kt b/src/main/java/br/com/blz/testjava/resource/repository/jpa/JpaWarehouseRepository.kt new file mode 100644 index 00000000..3f083cad --- /dev/null +++ b/src/main/java/br/com/blz/testjava/resource/repository/jpa/JpaWarehouseRepository.kt @@ -0,0 +1,11 @@ +package br.com.blz.testjava.resource.repository.jpa + +import br.com.blz.testjava.resource.repository.entity.WarehouseEntity +import org.springframework.data.repository.CrudRepository + +interface JpaWarehouseRepository : CrudRepository { + + fun findBySku(sku: Int): List + + fun deleteBySku(sku: Int) +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e69de29b..f714fbcb 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -0,0 +1,8 @@ +spring: + datasource: + username: sa + password: + + jpa: + hibernate: + ddl-auto: create diff --git a/src/test/java/br/com/blz/testjava/TestJavaApplicationTests.java b/src/test/java/br/com/blz/testjava/TestJavaApplicationTests.java deleted file mode 100644 index 08c41062..00000000 --- a/src/test/java/br/com/blz/testjava/TestJavaApplicationTests.java +++ /dev/null @@ -1,16 +0,0 @@ -package br.com.blz.testjava; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest -public class TestJavaApplicationTests { - - @Test - public void contextLoads() { - } - -} diff --git a/src/test/java/br/com/blz/testjava/TestJavaApplicationTests.kt b/src/test/java/br/com/blz/testjava/TestJavaApplicationTests.kt new file mode 100644 index 00000000..8832e1fe --- /dev/null +++ b/src/test/java/br/com/blz/testjava/TestJavaApplicationTests.kt @@ -0,0 +1,14 @@ +package br.com.blz.testjava + +import org.junit.Test +import org.junit.runner.RunWith +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.junit4.SpringRunner + +@RunWith(SpringRunner::class) +@SpringBootTest +class TestJavaApplicationTests { + @Test + fun contextLoads() { + } +} diff --git a/src/test/java/br/com/blz/testjava/domain/usecase/CreateProductUseCaseTest.kt b/src/test/java/br/com/blz/testjava/domain/usecase/CreateProductUseCaseTest.kt new file mode 100644 index 00000000..3f2fa0f7 --- /dev/null +++ b/src/test/java/br/com/blz/testjava/domain/usecase/CreateProductUseCaseTest.kt @@ -0,0 +1,72 @@ +package br.com.blz.testjava.domain.usecase + +import br.com.blz.testjava.domain.entity.CreateProduct +import br.com.blz.testjava.domain.exception.AlreadyExistsException +import br.com.blz.testjava.domain.repository.ProductRepository +import br.com.blz.testjava.domain.repository.WarehouseRepository +import br.com.blz.testjava.factory.MockFactory +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Mockito.doReturn +import org.mockito.junit.jupiter.MockitoExtension +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +@ExtendWith(MockitoExtension::class) +class CreateProductUseCaseTest { + @Mock private lateinit var productRepository: ProductRepository + @Mock private lateinit var warehouseRepository: WarehouseRepository + + @InjectMocks private lateinit var createProductUseCase: CreateProductUseCase + + @Test + fun `Should create product with correct values for isMarketable and quantity`() { + val product = MockFactory.createProduct() + val warehouse = MockFactory.createWarehouse() + doReturn(product).`when`(productRepository).save(product) + doReturn(warehouse).`when`(warehouseRepository).save(warehouse, product.sku) + + val response = createProductUseCase.execute( + CreateProduct(product.sku, product.name, listOf( + warehouse, + warehouse, + )) + ) + + assertTrue { response.isMarketable } + assertEquals(10, response.inventory.quantity) + } + + @Test + fun `Should create product with correct values for isMarketable and quantity when is not marketable`() { + val product = MockFactory.createProduct() + val warehouse = MockFactory.createWarehouse(quantity = 0) + doReturn(product).`when`(productRepository).save(product) + doReturn(warehouse).`when`(warehouseRepository).save(warehouse, product.sku) + + val response = createProductUseCase.execute( + CreateProduct(product.sku, product.name, listOf( + warehouse, + warehouse, + )) + ) + + assertFalse { response.isMarketable } + assertEquals(0, response.inventory.quantity) + } + + @Test + fun `Should throw already exists exception when sku already exists`() { + val product = MockFactory.createProduct() + doReturn(true).`when`(productRepository).existsById(product.sku) + + assertThrows { + createProductUseCase.execute(CreateProduct(product.sku, product.name, listOf())) + } + } + +} diff --git a/src/test/java/br/com/blz/testjava/domain/usecase/DeleteProductUseCaseTest.kt b/src/test/java/br/com/blz/testjava/domain/usecase/DeleteProductUseCaseTest.kt new file mode 100644 index 00000000..9e1a4b1e --- /dev/null +++ b/src/test/java/br/com/blz/testjava/domain/usecase/DeleteProductUseCaseTest.kt @@ -0,0 +1,41 @@ +package br.com.blz.testjava.domain.usecase + +import br.com.blz.testjava.domain.exception.NotFoundException +import br.com.blz.testjava.domain.repository.ProductRepository +import br.com.blz.testjava.domain.repository.WarehouseRepository +import br.com.blz.testjava.factory.MockFactory +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Mockito.doReturn +import org.mockito.junit.jupiter.MockitoExtension + +@ExtendWith(MockitoExtension::class) +class DeleteProductUseCaseTest { + @Mock private lateinit var productRepository: ProductRepository + @Mock private lateinit var warehouseRepository: WarehouseRepository + + @InjectMocks private lateinit var deleteProductUseCase: DeleteProductUseCase + + @Test + fun `Should delete product`() { + val product = MockFactory.createProduct() + doReturn(product).`when`(productRepository).findById(product.sku.toString()) + + assertDoesNotThrow { + deleteProductUseCase.execute(product.sku.toString()) + } + } + + @Test + fun `Should throw not found exception when sku is not found`() { + val product = MockFactory.createProduct() + assertThrows { + deleteProductUseCase.execute(product.sku.toString()) + } + } + +} diff --git a/src/test/java/br/com/blz/testjava/domain/usecase/GetProductUseCaseTest.kt b/src/test/java/br/com/blz/testjava/domain/usecase/GetProductUseCaseTest.kt new file mode 100644 index 00000000..acce49f2 --- /dev/null +++ b/src/test/java/br/com/blz/testjava/domain/usecase/GetProductUseCaseTest.kt @@ -0,0 +1,58 @@ +package br.com.blz.testjava.domain.usecase + +import br.com.blz.testjava.domain.exception.NotFoundException +import br.com.blz.testjava.domain.repository.ProductRepository +import br.com.blz.testjava.domain.repository.WarehouseRepository +import br.com.blz.testjava.factory.MockFactory +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Mockito.doReturn +import org.mockito.junit.jupiter.MockitoExtension +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +@ExtendWith(MockitoExtension::class) +class GetProductUseCaseTest { + @Mock private lateinit var productRepository: ProductRepository + @Mock private lateinit var warehouseRepository: WarehouseRepository + + @InjectMocks private lateinit var getProductUseCase: GetProductUseCase + + @Test + fun `Should get product with correct values for isMarketable and quantity`() { + val product = MockFactory.createProduct() + val warehouse = MockFactory.createWarehouse() + doReturn(product).`when`(productRepository).findById(product.sku.toString()) + doReturn(listOf(warehouse, warehouse)).`when`(warehouseRepository).findBySku(product.sku) + + val response = getProductUseCase.execute(product.sku.toString()) + + assertTrue { response.isMarketable } + assertEquals(10, response.inventory.quantity) + } + + @Test + fun `Should get product with correct values for isMarketable and quantity when is not marketable`() { + val product = MockFactory.createProduct() + val warehouse = MockFactory.createWarehouse(0) + doReturn(product).`when`(productRepository).findById(product.sku.toString()) + doReturn(listOf(warehouse)).`when`(warehouseRepository).findBySku(product.sku) + + val response = getProductUseCase.execute(product.sku.toString()) + + assertFalse { response.isMarketable } + assertEquals(0, response.inventory.quantity) + } + + @Test + fun `Should throw not found exception when sku is not found`() { + assertThrows { + getProductUseCase.execute("000") + } + } + +} diff --git a/src/test/java/br/com/blz/testjava/domain/usecase/UpdateProductUseCaseTest.kt b/src/test/java/br/com/blz/testjava/domain/usecase/UpdateProductUseCaseTest.kt new file mode 100644 index 00000000..b611cfeb --- /dev/null +++ b/src/test/java/br/com/blz/testjava/domain/usecase/UpdateProductUseCaseTest.kt @@ -0,0 +1,79 @@ +package br.com.blz.testjava.domain.usecase + +import br.com.blz.testjava.domain.entity.UpdateProduct +import br.com.blz.testjava.domain.exception.NotFoundException +import br.com.blz.testjava.domain.repository.ProductRepository +import br.com.blz.testjava.domain.repository.WarehouseRepository +import br.com.blz.testjava.factory.MockFactory +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Mockito.doReturn +import org.mockito.junit.jupiter.MockitoExtension +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +@ExtendWith(MockitoExtension::class) +class UpdateProductUseCaseTest { + @Mock private lateinit var productRepository: ProductRepository + @Mock private lateinit var warehouseRepository: WarehouseRepository + + @InjectMocks private lateinit var updateProductUseCase: UpdateProductUseCase + + @Test + fun `Should update product with correct values for isMarketable and quantity`() { + val product = MockFactory.createProduct() + val warehouse = MockFactory.createWarehouse() + doReturn(product).`when`(productRepository).findById(product.sku.toString()) + doReturn(product).`when`(productRepository).save(product) + doReturn(warehouse).`when`(warehouseRepository).save(warehouse, product.sku) + + val response = updateProductUseCase.execute( + UpdateProduct(product.sku, product.name, listOf( + warehouse, + warehouse, + )) + ) + + assertTrue { response.isMarketable } + assertEquals(10, response.inventory.quantity) + } + + @Test + fun `Should update product with correct values for isMarketable and quantity when is not marketable`() { + val product = MockFactory.createProduct() + val warehouse = MockFactory.createWarehouse(quantity = 0) + doReturn(product).`when`(productRepository).findById(product.sku.toString()) + doReturn(product).`when`(productRepository).save(product) + doReturn(warehouse).`when`(warehouseRepository).save(warehouse, product.sku) + + val response = updateProductUseCase.execute( + UpdateProduct(product.sku, product.name, listOf( + warehouse, + warehouse, + )) + ) + + assertFalse { response.isMarketable } + assertEquals(0, response.inventory.quantity) + } + + @Test + fun `Should throw not found exception when sku is not found`() { + val product = MockFactory.createProduct() + val warehouse = MockFactory.createWarehouse(quantity = 0) + + assertThrows { + updateProductUseCase.execute( + UpdateProduct(product.sku, product.name, listOf( + warehouse, + warehouse, + )) + ) + } + } + +} diff --git a/src/test/java/br/com/blz/testjava/factory/MockFactory.kt b/src/test/java/br/com/blz/testjava/factory/MockFactory.kt new file mode 100644 index 00000000..ca0e5b91 --- /dev/null +++ b/src/test/java/br/com/blz/testjava/factory/MockFactory.kt @@ -0,0 +1,12 @@ +package br.com.blz.testjava.factory + +import br.com.blz.testjava.domain.entity.Product +import br.com.blz.testjava.domain.entity.Warehouse +import br.com.blz.testjava.domain.entity.WarehouseType + +class MockFactory { + companion object { + fun createProduct() = Product(10, "Produto teste") + fun createWarehouse(quantity: Int = 5) = Warehouse("SP", quantity, WarehouseType.ECOMMERCE) + } +} From cafd3a529d31d23e45fb00a57d13ee0d1084c52f Mon Sep 17 00:00:00 2001 From: YudiG12 Date: Thu, 16 Feb 2023 12:19:45 -0300 Subject: [PATCH 2/2] Added mutation tests pitest report. 100% coverage. --- pom.xml | 18 ++++++++++++++++++ .../domain/usecase/CreateProductUseCaseTest.kt | 5 +++-- .../domain/usecase/DeleteProductUseCaseTest.kt | 5 +++++ .../domain/usecase/UpdateProductUseCaseTest.kt | 3 +++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a5cb2397..ec2a55a9 100644 --- a/pom.xml +++ b/pom.xml @@ -119,6 +119,24 @@ + + org.pitest + pitest-maven + 1.11.0 + + + org.pitest + pitest-junit5-plugin + 1.1.1 + + + + + br.com.blz.testjava.domain.usecase.* + + kotlin.jvm.internal + + diff --git a/src/test/java/br/com/blz/testjava/domain/usecase/CreateProductUseCaseTest.kt b/src/test/java/br/com/blz/testjava/domain/usecase/CreateProductUseCaseTest.kt index 3f2fa0f7..25e08fdb 100644 --- a/src/test/java/br/com/blz/testjava/domain/usecase/CreateProductUseCaseTest.kt +++ b/src/test/java/br/com/blz/testjava/domain/usecase/CreateProductUseCaseTest.kt @@ -10,7 +10,7 @@ import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith import org.mockito.InjectMocks import org.mockito.Mock -import org.mockito.Mockito.doReturn +import org.mockito.Mockito.* import org.mockito.junit.jupiter.MockitoExtension import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -57,6 +57,7 @@ class CreateProductUseCaseTest { assertFalse { response.isMarketable } assertEquals(0, response.inventory.quantity) + verify(productRepository, times(1)).save(product) } @Test @@ -67,6 +68,6 @@ class CreateProductUseCaseTest { assertThrows { createProductUseCase.execute(CreateProduct(product.sku, product.name, listOf())) } + verify(productRepository, times(0)).save(product) } - } diff --git a/src/test/java/br/com/blz/testjava/domain/usecase/DeleteProductUseCaseTest.kt b/src/test/java/br/com/blz/testjava/domain/usecase/DeleteProductUseCaseTest.kt index 9e1a4b1e..fd229c0d 100644 --- a/src/test/java/br/com/blz/testjava/domain/usecase/DeleteProductUseCaseTest.kt +++ b/src/test/java/br/com/blz/testjava/domain/usecase/DeleteProductUseCaseTest.kt @@ -10,6 +10,7 @@ import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith import org.mockito.InjectMocks import org.mockito.Mock +import org.mockito.Mockito import org.mockito.Mockito.doReturn import org.mockito.junit.jupiter.MockitoExtension @@ -28,6 +29,8 @@ class DeleteProductUseCaseTest { assertDoesNotThrow { deleteProductUseCase.execute(product.sku.toString()) } + Mockito.verify(productRepository, Mockito.times(1)).deleteById(product.sku) + Mockito.verify(warehouseRepository, Mockito.times(1)).deleteBySku(product.sku) } @Test @@ -36,6 +39,8 @@ class DeleteProductUseCaseTest { assertThrows { deleteProductUseCase.execute(product.sku.toString()) } + Mockito.verify(productRepository, Mockito.times(0)).deleteById(product.sku) + Mockito.verify(warehouseRepository, Mockito.times(0)).deleteBySku(product.sku) } } diff --git a/src/test/java/br/com/blz/testjava/domain/usecase/UpdateProductUseCaseTest.kt b/src/test/java/br/com/blz/testjava/domain/usecase/UpdateProductUseCaseTest.kt index b611cfeb..10e583ea 100644 --- a/src/test/java/br/com/blz/testjava/domain/usecase/UpdateProductUseCaseTest.kt +++ b/src/test/java/br/com/blz/testjava/domain/usecase/UpdateProductUseCaseTest.kt @@ -10,6 +10,7 @@ import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith import org.mockito.InjectMocks import org.mockito.Mock +import org.mockito.Mockito import org.mockito.Mockito.doReturn import org.mockito.junit.jupiter.MockitoExtension import kotlin.test.assertEquals @@ -40,6 +41,7 @@ class UpdateProductUseCaseTest { assertTrue { response.isMarketable } assertEquals(10, response.inventory.quantity) + Mockito.verify(warehouseRepository, Mockito.times(1)).deleteBySku(product.sku) } @Test @@ -74,6 +76,7 @@ class UpdateProductUseCaseTest { )) ) } + Mockito.verify(warehouseRepository, Mockito.times(0)).deleteBySku(product.sku) } }