diff --git a/.gitignore b/.gitignore index 453cc450..c2065bc2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,214 +1,37 @@ - -# Created by https://www.gitignore.io/api/java,maven,macos,linux,intellij+all,eclipse - -### Eclipse ### - -.metadata -bin/ -tmp/ -*.tmp -*.bak -*.swp -*~.nib -local.properties -.settings/ -.loadpath -.recommenders - -# External tool builders -.externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" -*.launch - -# PyDev specific (Python IDE for Eclipse) -*.pydevproject - -# CDT-specific (C/C++ Development Tooling) -.cproject - -# Java annotation processor (APT) +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath .factorypath - -# PDT-specific (PHP Development Tools) -.buildpath - -# sbteclipse plugin -.target - -# Tern plugin -.tern-project - -# TeXlipse plugin -.texlipse - -# STS (Spring Tool Suite) -.springBeans - -# Code Recommenders -.recommenders/ - -# Scala IDE specific (Scala & Java development for Eclipse) -.cache-main -.scala_dependencies -.worksheet - -### Eclipse Patch ### -# Eclipse Core .project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ -# JDT-specific (Eclipse Java Development Tools) -.classpath - -# Annotation Processing -.apt_generated - -### Intellij+all ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff: -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/dictionaries - -# Sensitive or high-churn files: -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.xml -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml - -# Gradle: -.idea/**/gradle.xml -.idea/**/libraries - -# CMake -cmake-build-debug/ - -# Mongo Explorer plugin: -.idea/**/mongoSettings.xml - -## File-based project format: +### IntelliJ IDEA ### +.idea *.iws - -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Ruby plugin and RubyMine -/.rakeTasks - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -### Intellij+all Patch ### -# Ignores the whole .idea folder and all .iml files -# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 - -.idea/ - -# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 - *.iml -modules.xml -.idea/misc.xml *.ipr - -### Java ### -# Compiled class file -*.class - -# Log file -*.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### macOS ### -*.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### Maven ### -target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -pom.xml.next -release.properties -dependency-reduced-pom.xml -buildNumber.properties -.mvn/timing.properties - -# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) -!/.mvn/wrapper/maven-wrapper.jar - - -# End of https://www.gitignore.io/api/java,maven,macos,linux,intellij+all,eclipse +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ 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/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 1914b842..00000000 Binary files a/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 05c741ea..00000000 --- a/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.4/maven-wrapper-0.5.4.jar diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b2270fd7..00000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: java -jdk: - - openjdk8 -notifications: - email: false diff --git a/README.md b/README.md index e7111797..6964fabe 100644 --- a/README.md +++ b/README.md @@ -1,79 +1,39 @@ -### Backend Test +# Project Title -[![Build Status](https://travis-ci.com/belezanaweb/test-java.svg?branch=master)](https://travis-ci.com/belezanaweb/test-java) +Kotlin test for boticário group enterprise -[![codecov](https://codecov.io/gh/belezanaweb/test-java/branch/master/graph/badge.svg)](https://codecov.io/gh/belezanaweb/test-java) +## Getting Started -Esta é uma avaliação básica de código. +These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. -O objetivo é conhecer um pouco do seu conhecimento/prática de RESTful, Spring e Java. +### Prerequisites -Recomendamos que você não gaste mais do que 4 - 6 horas. +The things you need before clone this project. -Faça um fork deste repositório que contém o bootstrap de uma aplicação SpringBoot 1.5.12. (você pode utilizar spring boot 2+) +* JDK 17 (LTS) -Ao finalizar o teste, submeta um pull request para o repositório que nosso time será notificado. +### Installation -### Tarefas +A step by step guide that will tell you how to get the development environment up and running. -Com a seguinte representação de produto: - -```json -{ - "sku": 43264, - "name": "L'Oréal Professionnel Expert Absolut Repair Cortex Lipidium - Máscara de Reconstrução 500g", - "inventory": { - "quantity": 15, - "warehouses": [ - { - "locality": "SP", - "quantity": 12, - "type": "ECOMMERCE" - }, - { - "locality": "MOEMA", - "quantity": 3, - "type": "PHYSICAL_STORE" - } - ] - }, - "isMarketable": true -} +``` +$ ./gradlew :bootRun ``` -Crie endpoints para as seguintes ações: - -- [ ] Criação de produto onde o payload será o json informado acima (exceto as propriedades **isMarketable** e **inventory.quantity**) - -- [ ] Edição de produto por **sku** - -- [ ] Recuperação de produto por **sku** - -- [ ] Deleção de produto por **sku** - -### Requisitos - - -- [ ] Toda vez que um produto for recuperado por **sku** deverá ser calculado a propriedade: **inventory.quantity** - - A propriedade inventory.quantity é a soma da quantity dos warehouses - -- [ ] Toda vez que um produto for recuperado por **sku** deverá ser calculado a propriedade: **isMarketable** - - Um produto é marketable sempre que seu inventory.quantity for maior que 0 - -- [ ] Caso um produto já existente em memória tente ser criado com o mesmo **sku** uma exceção deverá ser lançada - - Dois produtos são considerados iguais se os seus skus forem iguais +## Usage +Server up on http://localhost:8080/ -- [ ] Ao atualizar um produto, o antigo deve ser sobrescrito com o que esta sendo enviado na requisição +Routes: +``` +POST -> http://localhost:8080/products +PUT -> http://localhost:8080/products +GET -> http://localhost:8080/products +DELETE -> http://localhost:8080/products +``` - A requisição deve receber o sku e atualizar com o produto que tbm esta vindo na requisição +## Additional Documentation -### Dicas +You can import test_boticario.postman_collection.json in your postman to use requests template for this api -- Os produtos devem ficar em memória, não é necessário persistir os dados. Não utilize `h2` -- Não é necessário adicionar swagger (não será avaliado) -- Sinta-se a vontade para fazer o código em ```groovy```, ```kotlin``` ou ```scala``` se preferir, utilizamos bastante aqui -- Testes são sempre bem-vindos :smiley: +Enjoy it! \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..14aa81cd --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,38 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("org.springframework.boot") version "3.0.2" + id("io.spring.dependency-management") version "1.1.0" + kotlin("jvm") version "1.7.22" + kotlin("plugin.spring") version "1.7.22" +} + +group = "com.boticario" +version = "0.0.1-SNAPSHOT" +java.sourceCompatibility = JavaVersion.VERSION_17 + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-data-jpa:3.0.2") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.jetbrains.kotlin:kotlin-reflect") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + implementation("org.hsqldb:hsqldb:2.7.1") + testImplementation("io.mockk:mockk:1.13.4") + testImplementation("org.springframework.boot:spring-boot-starter-test") +} + +tasks.withType { + kotlinOptions { + freeCompilerArgs = listOf("-Xjsr305=strict") + jvmTarget = "17" + } +} + +tasks.withType { + useJUnitPlatform() +} diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index 38e58063..00000000 --- a/codecov.yml +++ /dev/null @@ -1 +0,0 @@ -cat codecov.yml | curl --data-binary @- https://codecov.io/validate diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..249e5832 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..070cb702 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..a69d9cb6 --- /dev/null +++ b/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..53a6b238 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mvnw b/mvnw deleted file mode 100755 index 35ff643b..00000000 --- a/mvnw +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.4/maven-wrapper-0.5.4.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.4/maven-wrapper-0.5.4.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd deleted file mode 100644 index dae46d49..00000000 --- a/mvnw.cmd +++ /dev/null @@ -1,182 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.4/maven-wrapper-0.5.4.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.4/maven-wrapper-0.5.4.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 0c508267..00000000 --- a/pom.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - 4.0.0 - - br.com.blz - test-java - 0.0.1-SNAPSHOT - jar - - test-java - Java Test for Beleza Na Web - - - org.springframework.boot - spring-boot-starter-parent - 2.4.1 - - - - - UTF-8 - UTF-8 - 1.8 - - - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..85d84a92 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "test-kotlin" 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/kotlin/com/boticario/testkotlin/TestKotlinApplication.kt b/src/main/kotlin/com/boticario/testkotlin/TestKotlinApplication.kt new file mode 100644 index 00000000..d1cdb274 --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/TestKotlinApplication.kt @@ -0,0 +1,11 @@ +package com.boticario.testkotlin + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication + +@SpringBootApplication +class TestKotlinApplication + +fun main(args: Array) { + runApplication(*args) +} diff --git a/src/main/kotlin/com/boticario/testkotlin/application/request/CreateProductRequest.kt b/src/main/kotlin/com/boticario/testkotlin/application/request/CreateProductRequest.kt new file mode 100644 index 00000000..6ffd89ad --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/application/request/CreateProductRequest.kt @@ -0,0 +1,7 @@ +package com.boticario.testkotlin.application.request + +data class CreateProductRequest( + val sku: Long, + val name: String, + val inventory: InventoryRequest = InventoryRequest() +) diff --git a/src/main/kotlin/com/boticario/testkotlin/application/request/InventoryRequest.kt b/src/main/kotlin/com/boticario/testkotlin/application/request/InventoryRequest.kt new file mode 100644 index 00000000..def8368e --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/application/request/InventoryRequest.kt @@ -0,0 +1,9 @@ +package com.boticario.testkotlin.application.request + +import com.boticario.testkotlin.domain.entity.Inventory + +data class InventoryRequest( + val warehouses: MutableList = mutableListOf() +) { + fun toDomain() = Inventory(warehouses.map { it.toDomain() }.toMutableList()) +} diff --git a/src/main/kotlin/com/boticario/testkotlin/application/request/UpdateProductRequest.kt b/src/main/kotlin/com/boticario/testkotlin/application/request/UpdateProductRequest.kt new file mode 100644 index 00000000..776568b8 --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/application/request/UpdateProductRequest.kt @@ -0,0 +1,6 @@ +package com.boticario.testkotlin.application.request + +data class UpdateProductRequest( + val name: String? = null, + val inventory: InventoryRequest? = null +) diff --git a/src/main/kotlin/com/boticario/testkotlin/application/request/WarehouseRequest.kt b/src/main/kotlin/com/boticario/testkotlin/application/request/WarehouseRequest.kt new file mode 100644 index 00000000..9e6fe9d0 --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/application/request/WarehouseRequest.kt @@ -0,0 +1,12 @@ +package com.boticario.testkotlin.application.request + +import com.boticario.testkotlin.domain.entity.Warehouse +import com.boticario.testkotlin.domain.enum.WarehouseType + +data class WarehouseRequest( + val locality: String, + val quantity: Int, + val type: WarehouseType +) { + fun toDomain() = Warehouse(locality, quantity, type) +} diff --git a/src/main/kotlin/com/boticario/testkotlin/application/response/InventoryResponse.kt b/src/main/kotlin/com/boticario/testkotlin/application/response/InventoryResponse.kt new file mode 100644 index 00000000..2fbc06d5 --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/application/response/InventoryResponse.kt @@ -0,0 +1,15 @@ +package com.boticario.testkotlin.application.response + +import com.boticario.testkotlin.domain.entity.Inventory + +data class InventoryResponse( + val warehouses: MutableList, + val quantity: Int +) { + companion object { + fun fromDomain(inventory: Inventory) = InventoryResponse( + inventory.warehouses.map { WarehouseResponse.fromDomain(it) }.toMutableList(), + inventory.quantity + ) + } +} diff --git a/src/main/kotlin/com/boticario/testkotlin/application/response/ProductResponse.kt b/src/main/kotlin/com/boticario/testkotlin/application/response/ProductResponse.kt new file mode 100644 index 00000000..eba2e61e --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/application/response/ProductResponse.kt @@ -0,0 +1,20 @@ +package com.boticario.testkotlin.application.response + +import com.boticario.testkotlin.domain.entity.Inventory +import com.boticario.testkotlin.domain.entity.Product +import java.io.InvalidObjectException + +data class ProductResponse( + val sku: Long, + val name: String, + val inventory: InventoryResponse, + val isMarketable: Boolean +) { + companion object { + fun fromDomain(product: Product) = ProductResponse( + product.sku, + product.name, + InventoryResponse.fromDomain(product.inventory), + product.isMarketable) + } +} diff --git a/src/main/kotlin/com/boticario/testkotlin/application/response/WarehouseResponse.kt b/src/main/kotlin/com/boticario/testkotlin/application/response/WarehouseResponse.kt new file mode 100644 index 00000000..fff4be7f --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/application/response/WarehouseResponse.kt @@ -0,0 +1,18 @@ +package com.boticario.testkotlin.application.response + +import com.boticario.testkotlin.domain.entity.Warehouse +import com.boticario.testkotlin.domain.enum.WarehouseType + +data class WarehouseResponse( + val locality: String, + val quantity: Int, + val type: WarehouseType +) { + companion object { + fun fromDomain(warehouse: Warehouse) = WarehouseResponse( + warehouse.locality, + warehouse.quantity, + warehouse.type + ) + } +} diff --git a/src/main/kotlin/com/boticario/testkotlin/application/service/ProductService.kt b/src/main/kotlin/com/boticario/testkotlin/application/service/ProductService.kt new file mode 100644 index 00000000..580b7a47 --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/application/service/ProductService.kt @@ -0,0 +1,18 @@ +package com.boticario.testkotlin.application.service + +import com.boticario.testkotlin.application.request.CreateProductRequest +import com.boticario.testkotlin.application.request.UpdateProductRequest +import com.boticario.testkotlin.application.response.ProductResponse +import com.boticario.testkotlin.domain.entity.Product +import org.springframework.stereotype.Service + +@Service +interface ProductService { + fun insertProductInStock(request: CreateProductRequest): ProductResponse + + fun updateProductFromStock(request: UpdateProductRequest, sku: Long): ProductResponse + + fun getProductFromStock(sku: Long): ProductResponse + + fun deleteProductFromStock(sku: Long) +} diff --git a/src/main/kotlin/com/boticario/testkotlin/application/service/impl/ProductServiceImpl.kt b/src/main/kotlin/com/boticario/testkotlin/application/service/impl/ProductServiceImpl.kt new file mode 100644 index 00000000..4a16169b --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/application/service/impl/ProductServiceImpl.kt @@ -0,0 +1,66 @@ +package com.boticario.testkotlin.application.service.impl + +import com.boticario.testkotlin.domain.repository.ProductRepository +import com.boticario.testkotlin.application.request.CreateProductRequest +import com.boticario.testkotlin.application.request.UpdateProductRequest +import com.boticario.testkotlin.application.response.ProductResponse +import com.boticario.testkotlin.application.service.ProductService +import com.boticario.testkotlin.domain.entity.Product +import org.springframework.http.HttpStatus +import org.springframework.stereotype.Service +import org.springframework.web.server.ResponseStatusException + +@Service +class ProductServiceImpl ( + private val productRepository: ProductRepository, +) : ProductService { + override fun insertProductInStock(request: CreateProductRequest): ProductResponse { + val alreadyExists = productRepository.findOne(request.sku) + if (alreadyExists != null) throw ResponseStatusException( + HttpStatus.BAD_REQUEST, + "Fail to CREATE product, sku already exists!" + ) + + val response = productRepository.save(Product(request.sku, request.name, request.inventory.toDomain())) + + return ProductResponse.fromDomain(response) + } + + override fun updateProductFromStock(request: UpdateProductRequest, sku: Long): ProductResponse { + val product = productRepository.findOne(sku) ?: throw ResponseStatusException( + HttpStatus.BAD_REQUEST, + "Fail to UPDATE product, sku not found in database!" + ) + + val response = productRepository.save( + Product( + product.sku, + request.name ?: product.name, + request.inventory?.toDomain() ?: product.inventory + ) + ) + + return ProductResponse.fromDomain(response) + + } + + override fun getProductFromStock(sku: Long): ProductResponse { + val response = productRepository.findOne(sku) ?: throw ResponseStatusException( + HttpStatus.BAD_REQUEST, + "Fail to FIND product, sku not found in database!" + ) + + return ProductResponse.fromDomain(response) + } + + override fun deleteProductFromStock(sku: Long) = + runCatching { + productRepository.deleteOne(sku) + }.onFailure { + throw ResponseStatusException( + HttpStatus.BAD_REQUEST, + "Fail to DELETE product, sku not found in database!" + ) + }.getOrElse { } + +} diff --git a/src/main/kotlin/com/boticario/testkotlin/application/web/controller/ProductController.kt b/src/main/kotlin/com/boticario/testkotlin/application/web/controller/ProductController.kt new file mode 100644 index 00000000..877b84d2 --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/application/web/controller/ProductController.kt @@ -0,0 +1,40 @@ +package com.boticario.testkotlin.application.web.controller + +import com.boticario.testkotlin.application.request.CreateProductRequest +import com.boticario.testkotlin.application.request.UpdateProductRequest +import com.boticario.testkotlin.application.service.ProductService +import java.sql.SQLException +import org.springframework.http.HttpStatus +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.ResponseStatus +import org.springframework.web.bind.annotation.RestController + + +@RestController +@RequestMapping("/products") +class ProductController(private val productService: ProductService) { + @PostMapping + @ResponseStatus(code = HttpStatus.CREATED) + fun createProduct(@RequestBody createProductRequest: CreateProductRequest) = + productService.insertProductInStock(createProductRequest) + + @GetMapping("/{sku}") + fun getProduct(@PathVariable sku: Long) = productService.getProductFromStock(sku) + + @PutMapping("/{sku}") + @Transactional(rollbackFor = [SQLException::class]) + fun updateProduct(@RequestBody updateProductRequest: UpdateProductRequest, @PathVariable sku: Long) = + productService.updateProductFromStock(updateProductRequest, sku) + + @ResponseStatus(code = HttpStatus.NO_CONTENT) + @DeleteMapping("/{sku}") + fun deleteProduct(@PathVariable sku: Long) = productService.deleteProductFromStock(sku) + +} diff --git a/src/main/kotlin/com/boticario/testkotlin/domain/entity/Inventory.kt b/src/main/kotlin/com/boticario/testkotlin/domain/entity/Inventory.kt new file mode 100644 index 00000000..ed91100c --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/domain/entity/Inventory.kt @@ -0,0 +1,13 @@ +package com.boticario.testkotlin.domain.entity + +import java.io.InvalidObjectException + +data class Inventory( + val warehouses: MutableList = mutableListOf(), + val quantity: Int = warehouses.sumOf { it.quantity } +) { + init { + if(quantity != warehouses.sumOf { it.quantity }) + throw InvalidObjectException("Wrong inventory quantity!") + } +} diff --git a/src/main/kotlin/com/boticario/testkotlin/domain/entity/Product.kt b/src/main/kotlin/com/boticario/testkotlin/domain/entity/Product.kt new file mode 100644 index 00000000..6ed0dc0a --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/domain/entity/Product.kt @@ -0,0 +1,19 @@ +package com.boticario.testkotlin.domain.entity + +import java.io.InvalidObjectException + +data class Product( + val sku: Long, + val name: String, + val inventory: Inventory = Inventory(), + val isMarketable: Boolean = inventory.quantity > 0 +) { + init { + if (inventory.quantity <= 0) { + if (isMarketable) + throw InvalidObjectException("Wrong isMarketable flag for product, invalid data!") + } + else if (!isMarketable) + throw InvalidObjectException("Wrong isMarketable flag for product, invalid data!") + } +} diff --git a/src/main/kotlin/com/boticario/testkotlin/domain/entity/Warehouse.kt b/src/main/kotlin/com/boticario/testkotlin/domain/entity/Warehouse.kt new file mode 100644 index 00000000..1284f267 --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/domain/entity/Warehouse.kt @@ -0,0 +1,9 @@ +package com.boticario.testkotlin.domain.entity + +import com.boticario.testkotlin.domain.enum.WarehouseType + +data class Warehouse( + val locality: String, + val quantity: Int, + val type: WarehouseType +) diff --git a/src/main/kotlin/com/boticario/testkotlin/domain/enum/WarehouseType.kt b/src/main/kotlin/com/boticario/testkotlin/domain/enum/WarehouseType.kt new file mode 100644 index 00000000..90c217af --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/domain/enum/WarehouseType.kt @@ -0,0 +1,6 @@ +package com.boticario.testkotlin.domain.enum + +enum class WarehouseType { + ECOMMERCE, + PHYSICAL_STORE; +} \ No newline at end of file diff --git a/src/main/kotlin/com/boticario/testkotlin/domain/repository/ProductRepository.kt b/src/main/kotlin/com/boticario/testkotlin/domain/repository/ProductRepository.kt new file mode 100644 index 00000000..d2aa9c81 --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/domain/repository/ProductRepository.kt @@ -0,0 +1,11 @@ +package com.boticario.testkotlin.domain.repository + +import com.boticario.testkotlin.domain.entity.Product + +interface ProductRepository { + fun save(product: Product): Product + + fun findOne(sku: Long): Product? + + fun deleteOne(sku: Long) +} diff --git a/src/main/kotlin/com/boticario/testkotlin/domain/repository/WarehouseRepository.kt b/src/main/kotlin/com/boticario/testkotlin/domain/repository/WarehouseRepository.kt new file mode 100644 index 00000000..c7e59f02 --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/domain/repository/WarehouseRepository.kt @@ -0,0 +1,7 @@ +package com.boticario.testkotlin.domain.repository + +interface WarehouseRepository { + fun save() + + fun findOne() +} diff --git a/src/main/kotlin/com/boticario/testkotlin/infrastructure/adapters/repository/ProductJpaRepository.kt b/src/main/kotlin/com/boticario/testkotlin/infrastructure/adapters/repository/ProductJpaRepository.kt new file mode 100644 index 00000000..a70bd14b --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/infrastructure/adapters/repository/ProductJpaRepository.kt @@ -0,0 +1,6 @@ +package com.boticario.testkotlin.infrastructure.adapters.repository + +import com.boticario.testkotlin.infrastructure.persistence.entity.ProductEntity +import org.springframework.data.repository.CrudRepository + +interface ProductJpaRepository : CrudRepository \ No newline at end of file diff --git a/src/main/kotlin/com/boticario/testkotlin/infrastructure/adapters/repository/impl/ProductRepositoryImpl.kt b/src/main/kotlin/com/boticario/testkotlin/infrastructure/adapters/repository/impl/ProductRepositoryImpl.kt new file mode 100644 index 00000000..aa1fe3b8 --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/infrastructure/adapters/repository/impl/ProductRepositoryImpl.kt @@ -0,0 +1,19 @@ +package com.boticario.testkotlin.infrastructure.adapters.repository.impl + +import com.boticario.testkotlin.domain.repository.ProductRepository +import com.boticario.testkotlin.domain.entity.Product +import com.boticario.testkotlin.infrastructure.adapters.repository.ProductJpaRepository +import com.boticario.testkotlin.infrastructure.persistence.entity.ProductEntity +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Repository + +@Repository +class ProductRepositoryImpl(private val productJpaRepository: ProductJpaRepository) : ProductRepository { + override fun save(product: Product) = productJpaRepository.save(ProductEntity.fromDomain(product)).toDomain() + + override fun findOne(sku: Long) = productJpaRepository.findByIdOrNull(sku)?.toDomain() + + override fun deleteOne(sku: Long) { + productJpaRepository.deleteById(sku) + } +} diff --git a/src/main/kotlin/com/boticario/testkotlin/infrastructure/persistence/entity/ProductEntity.kt b/src/main/kotlin/com/boticario/testkotlin/infrastructure/persistence/entity/ProductEntity.kt new file mode 100644 index 00000000..dfbb3546 --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/infrastructure/persistence/entity/ProductEntity.kt @@ -0,0 +1,29 @@ +package com.boticario.testkotlin.infrastructure.persistence.entity + +import com.boticario.testkotlin.domain.entity.Inventory +import com.boticario.testkotlin.domain.entity.Product +import jakarta.persistence.CascadeType +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.OneToMany + +@Entity +class ProductEntity( + @Id + private val sku: Long = 0L, + private val name: String = "", + + @OneToMany(cascade = [CascadeType.ALL]) + private val warehouses: MutableList = mutableListOf() +) { + fun toDomain() = Product(sku, name, Inventory(warehouses.map { it.toDomain() }.toMutableList())) + + + companion object { + fun fromDomain(product: Product) = ProductEntity( + product.sku, + product.name, + product.inventory.warehouses.map { WarehouseEntity.fromDomain(it) }.toMutableList() + ) + } +} diff --git a/src/main/kotlin/com/boticario/testkotlin/infrastructure/persistence/entity/WarehouseEntity.kt b/src/main/kotlin/com/boticario/testkotlin/infrastructure/persistence/entity/WarehouseEntity.kt new file mode 100644 index 00000000..a4cefbea --- /dev/null +++ b/src/main/kotlin/com/boticario/testkotlin/infrastructure/persistence/entity/WarehouseEntity.kt @@ -0,0 +1,37 @@ +package com.boticario.testkotlin.infrastructure.persistence.entity + +import com.boticario.testkotlin.domain.entity.Warehouse +import com.boticario.testkotlin.domain.enum.WarehouseType +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne + +@Entity +class WarehouseEntity( + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private val id: Int = 0, + private val locality: String = "", + private val quantity: Int = 0, + private val type: String = "", + @ManyToOne + @JoinColumn(name = "product_sku") + private val product: ProductEntity? = null +) { + fun toDomain() = Warehouse( + locality, + quantity, + WarehouseType.valueOf(type) + ) + + companion object { + fun fromDomain(warehouse: Warehouse) = WarehouseEntity( + locality = warehouse.locality, + quantity = warehouse.quantity, + type = warehouse.type.name + ) + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e69de29b..b9450f44 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -0,0 +1,12 @@ +spring: + datasource: + driver-class-name: org.hsqldb.jdbc.JDBCDriver + url: jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1 + username: boticario + password: + jpa: + hibernate: + ddl-auto: create +server: + error: + include-message: always \ No newline at end of file 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/kotlin/com/boticario/testkotlin/TestKotlinApplicationTests.kt b/src/test/kotlin/com/boticario/testkotlin/TestKotlinApplicationTests.kt new file mode 100644 index 00000000..bd0866f0 --- /dev/null +++ b/src/test/kotlin/com/boticario/testkotlin/TestKotlinApplicationTests.kt @@ -0,0 +1,13 @@ +package com.boticario.testkotlin + +import org.junit.jupiter.api.Test +import org.springframework.boot.test.context.SpringBootTest + +@SpringBootTest +class TestKotlinApplicationTests { + + @Test + fun contextLoads() { + } + +} diff --git a/src/test/kotlin/com/boticario/testkotlin/application/service/impl/ProductServiceImplTest.kt b/src/test/kotlin/com/boticario/testkotlin/application/service/impl/ProductServiceImplTest.kt new file mode 100644 index 00000000..c3e0f08a --- /dev/null +++ b/src/test/kotlin/com/boticario/testkotlin/application/service/impl/ProductServiceImplTest.kt @@ -0,0 +1,161 @@ +package com.boticario.testkotlin.application.service.impl + +import com.boticario.testkotlin.domain.repository.ProductRepository + +import com.boticario.testkotlin.application.request.WarehouseRequest +import com.boticario.testkotlin.application.response.InventoryResponse +import com.boticario.testkotlin.application.response.ProductResponse +import com.boticario.testkotlin.domain.enum.WarehouseType +import com.boticario.testkotlin.factory.ProductFactory.createProduct +import com.boticario.testkotlin.factory.ProductFactory.createProductRequest +import com.boticario.testkotlin.factory.ProductFactory.inventoryRequest +import com.boticario.testkotlin.factory.ProductFactory.updateProduct +import com.boticario.testkotlin.factory.ProductFactory.updateProductRequest +import io.mockk.called +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.runs +import io.mockk.verify +import io.mockk.verifyOrder +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.springframework.http.HttpStatus +import org.springframework.web.server.ResponseStatusException + +internal class ProductServiceImplTest { + private val productRepository = mockk() + private val productService = ProductServiceImpl(productRepository) + + @BeforeEach + fun setUp() { + every { productRepository.findOne(any()) } returns createProduct + } + + @Test + fun `Given a create product request, When valid parameters are input, Then insert product repository must be invoked and return the created product`() { + every { productRepository.save(any()) } returns createProduct + every { productRepository.findOne(any()) } returns null + + val result = productService.insertProductInStock(createProductRequest) + + verifyOrder { + productRepository.findOne(createProduct.sku) + productRepository.save(createProduct) + } + + assert(result == ProductResponse.fromDomain(createProduct)) + } + + @Test + fun `Given a create product request, When an invalid sku is input, Then must be thrown an exception in the find product step`() { + val result = assertThrows { productService.insertProductInStock(createProductRequest) } + + verifyOrder { + productRepository.findOne(createProduct.sku) + productRepository.save(any()) wasNot called + } + + assert(result.reason == "Fail to CREATE product, sku already exists!") + assert(result.statusCode == HttpStatus.BAD_REQUEST) + } + + @Test + fun `Given an update product request, When only the name is input, Then must be updated only the name of the product`() { + every { productRepository.save(any()) } returns updateProduct + + val result = productService.updateProductFromStock(updateProductRequest, createProduct.sku) + + verifyOrder { + productRepository.findOne(createProduct.sku) + productRepository.save(updateProduct) + } + + assert(result.name == updateProduct.name) + assert(result.inventory == InventoryResponse.fromDomain(createProduct.inventory)) + } + + @Test + fun `Given an update product request, When a all fields are input, Then must be overwrite all fields`() { + val overwriteInventoryRequest = inventoryRequest.copy( + mutableListOf( + WarehouseRequest("MG", 42, WarehouseType.PHYSICAL_STORE), + WarehouseRequest("ES", 28, WarehouseType.ECOMMERCE) + ) + ) + val updateAllFields = updateProduct.copy(inventory = overwriteInventoryRequest.toDomain()) + val updateAllFieldsRequest = updateProductRequest.copy(inventory = overwriteInventoryRequest) + every { productRepository.save(any()) } returns updateAllFields + setUp() + + val result = productService.updateProductFromStock(updateAllFieldsRequest, createProduct.sku) + + verifyOrder { + productRepository.findOne(createProduct.sku) + productRepository.save(updateAllFields) + } + + assert(result.name == updateProduct.name) + assert(result.inventory == InventoryResponse.fromDomain(updateAllFields.inventory)) + } + + @Test + fun `Given an update product request, When an invalid sku is input, Then must be thrown an exception in the find product step`() { + every { productRepository.findOne(any()) } returns null + + val result = assertThrows { + productService.updateProductFromStock( + updateProductRequest, + createProduct.sku + ) + } + + verifyOrder { + productRepository.findOne(createProduct.sku) + productRepository.save(any()) wasNot called + } + + assert(result.reason == "Fail to UPDATE product, sku not found in database!") + assert(result.statusCode == HttpStatus.BAD_REQUEST) + } + + @Test + fun `Given a search product request, When a valid sku is input, Then return found product`() { + val result = productService.getProductFromStock(createProduct.sku) + + verify { productRepository.findOne(createProduct.sku) } + assert(result == ProductResponse.fromDomain(createProduct)) + } + + @Test + fun `Given a search product request, When an invalid sku is input, Then must be thrown an exception`() { + every { productRepository.findOne(any()) } returns null + + val result = assertThrows { productService.getProductFromStock(createProduct.sku) } + + verify { productRepository.findOne(createProduct.sku) } + assert(result.reason == "Fail to FIND product, sku not found in database!") + assert(result.statusCode == HttpStatus.BAD_REQUEST) + } + + @Test + fun `Given a delete product request, When a valid sku is input, Then delete product repository must be invoke`() { + every { productRepository.deleteOne(any()) } just runs + + productService.deleteProductFromStock(createProduct.sku) + + verify { productRepository.deleteOne(createProduct.sku) } + } + + @Test + fun `Given a delete product request, When an invalid sku is input, Then must be thrown an exception`() { + every { productRepository.deleteOne(any()) } throws Exception() + + val result = assertThrows { productService.deleteProductFromStock(createProduct.sku) } + + verify { productRepository.deleteOne(createProduct.sku) } + assert(result.reason == "Fail to DELETE product, sku not found in database!") + assert(result.statusCode == HttpStatus.BAD_REQUEST) + } +} diff --git a/src/test/kotlin/com/boticario/testkotlin/application/web/controller/ProductControllerTest.kt b/src/test/kotlin/com/boticario/testkotlin/application/web/controller/ProductControllerTest.kt new file mode 100644 index 00000000..bf714918 --- /dev/null +++ b/src/test/kotlin/com/boticario/testkotlin/application/web/controller/ProductControllerTest.kt @@ -0,0 +1,53 @@ +package com.boticario.testkotlin.application.web.controller + +import com.boticario.testkotlin.application.request.CreateProductRequest +import com.boticario.testkotlin.application.request.UpdateProductRequest +import com.boticario.testkotlin.application.response.ProductResponse +import com.boticario.testkotlin.application.service.ProductService +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.runs +import io.mockk.verify +import org.junit.jupiter.api.Test + +internal class ProductControllerTest { + private val productService = mockk() + private val createProductRequest = mockk(relaxed = true) + private val updateProductRequest = mockk(relaxed = true) + private val sku = 1234L + private val productResponse = mockk(relaxed = true) + private val productController = ProductController(productService) + + @Test + fun `Given a product creation request, When valid parameters are input, Then insert product service must be invoked`() { + every { productService.insertProductInStock(any()) } returns productResponse + productController.createProduct(createProductRequest) + + verify { productService.insertProductInStock(any()) } + } + + @Test + fun `Given a product consult request, When valid parameters are input, Then get product service must be invoked`() { + every { productService.getProductFromStock(any()) } returns productResponse + productController.getProduct(sku) + + verify { productService.getProductFromStock(any()) } + } + + @Test + fun `Given a product change request, When valid parameters are input, Then update product service must be invoked`() { + every { productService.updateProductFromStock(any(), any()) } returns productResponse + productController.updateProduct(updateProductRequest, sku) + + verify { productService.updateProductFromStock(any(), any()) } + } + + @Test + fun `Given a product delete request, When valid parameters are input, Then delete product service must be invoked`() { + every { productService.deleteProductFromStock(any()) } just runs + productController.deleteProduct(sku) + + verify { productService.deleteProductFromStock(any()) } + } +} diff --git a/src/test/kotlin/com/boticario/testkotlin/domain/entity/InventoryTest.kt b/src/test/kotlin/com/boticario/testkotlin/domain/entity/InventoryTest.kt new file mode 100644 index 00000000..cc930061 --- /dev/null +++ b/src/test/kotlin/com/boticario/testkotlin/domain/entity/InventoryTest.kt @@ -0,0 +1,31 @@ +package com.boticario.testkotlin.domain.entity + +import com.boticario.testkotlin.factory.ProductFactory.warehousesRequest +import java.io.InvalidObjectException +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +internal class InventoryTest { + private val warehouses = warehousesRequest.map { it.toDomain() }.toMutableList() + private val inventory = Inventory(warehouses) + + @Test + fun `Given Inventory instance, When warehouses parameters are input, Then calculate inventory items quantity from warehouses`() { + + assert(inventory.quantity == warehousesRequest.sumOf { it.quantity }) + } + + @Test + fun `Given Inventory instance, When warehouses parameters are not input, Then inventory items quantity must be 0`() { + val inventory = Inventory() + + assert(inventory.quantity == 0) + } + + @Test + fun `Given Inventory instance, When invalid quantity parameter is input, Then throw Exception`() { + val result = assertThrows { Inventory(warehouses, 12) } + + assert(result.message == "Wrong inventory quantity!") + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/boticario/testkotlin/domain/entity/ProductTest.kt b/src/test/kotlin/com/boticario/testkotlin/domain/entity/ProductTest.kt new file mode 100644 index 00000000..dddcbe24 --- /dev/null +++ b/src/test/kotlin/com/boticario/testkotlin/domain/entity/ProductTest.kt @@ -0,0 +1,30 @@ +package com.boticario.testkotlin.domain.entity + +import com.boticario.testkotlin.factory.ProductFactory.createProduct +import java.io.InvalidObjectException +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +class ProductTest { + private val product = createProduct + + @Test + fun `Given product instance, When valid parameters are input, Then set isMarketable to true`() { + assert(product.isMarketable) + } + + @Test + fun `Given product instance, When only sku and name are input, Then set isMarketable to false`() { + + assert(!Product(1234, "test").isMarketable) + } + + @Test + fun `Given product instance, When invalid isMarketable parameter is input, Then throw Exception`() { + val result = assertThrows { Product(1234, "test", createProduct.inventory, false) } + val result2 = assertThrows { Product(sku = 1234, name = "test", isMarketable = true) } + + assert(result.message == "Wrong isMarketable flag for product, invalid data!") + assert(result2.message == "Wrong isMarketable flag for product, invalid data!") + } +} diff --git a/src/test/kotlin/com/boticario/testkotlin/factory/ProductFactory.kt b/src/test/kotlin/com/boticario/testkotlin/factory/ProductFactory.kt new file mode 100644 index 00000000..62d43422 --- /dev/null +++ b/src/test/kotlin/com/boticario/testkotlin/factory/ProductFactory.kt @@ -0,0 +1,32 @@ +package com.boticario.testkotlin.factory + +import com.boticario.testkotlin.application.request.CreateProductRequest +import com.boticario.testkotlin.application.request.InventoryRequest +import com.boticario.testkotlin.application.request.UpdateProductRequest +import com.boticario.testkotlin.application.request.WarehouseRequest +import com.boticario.testkotlin.domain.entity.Product +import com.boticario.testkotlin.domain.enum.WarehouseType + +object ProductFactory { + private const val SKU = 1234L + private const val UPDATE_NAME = "update to product 2" + + val warehousesRequest = mutableListOf( + WarehouseRequest("SP", 10, WarehouseType.PHYSICAL_STORE), + WarehouseRequest("RJ", 18, WarehouseType.ECOMMERCE) + ) + val inventoryRequest = InventoryRequest(warehousesRequest) + val createProductRequest = CreateProductRequest(SKU, "create product 1", inventoryRequest) + val updateProductRequest = UpdateProductRequest(UPDATE_NAME) + + val createProduct = Product( + createProductRequest.sku, + createProductRequest.name, + createProductRequest.inventory.toDomain() + ) + val updateProduct = Product( + SKU, + updateProductRequest.name!!, + createProduct.inventory + ) +} diff --git a/src/test/kotlin/com/boticario/testkotlin/infrastructure/adapters/repository/impl/ProductRepositoryImplTest.kt b/src/test/kotlin/com/boticario/testkotlin/infrastructure/adapters/repository/impl/ProductRepositoryImplTest.kt new file mode 100644 index 00000000..b8b6460d --- /dev/null +++ b/src/test/kotlin/com/boticario/testkotlin/infrastructure/adapters/repository/impl/ProductRepositoryImplTest.kt @@ -0,0 +1,54 @@ +package com.boticario.testkotlin.infrastructure.adapters.repository.impl + +import com.boticario.testkotlin.factory.ProductFactory.createProduct +import com.boticario.testkotlin.infrastructure.adapters.repository.ProductJpaRepository +import com.boticario.testkotlin.infrastructure.persistence.entity.ProductEntity +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.runs +import io.mockk.verify +import org.junit.jupiter.api.Test +import org.springframework.data.repository.findByIdOrNull + + +class ProductRepositoryImplTest { + private val productJpaRepository = mockk() + private val productRepositoryImpl = ProductRepositoryImpl(productJpaRepository) + private val productEntity = ProductEntity.fromDomain(createProduct) + + @Test + fun `Given a create product input, Then must be invoke save method from jpa repository and return the created product`() { + every { productJpaRepository.save(any()) } returns productEntity + val result = productRepositoryImpl.save(createProduct) + + verify { productJpaRepository.save(any()) } + assert(result == createProduct) + } + + @Test + fun `Given a delete product input, Then must be invoke delete method from jpa repository`() { + every { productJpaRepository.deleteById(any()) } just runs + productRepositoryImpl.deleteOne(createProduct.sku) + + verify { productJpaRepository.deleteById(createProduct.sku) } + } + + @Test + fun `Given a search entry by product, Then must be invoke the find method of the jpa repository and return the found product`() { + every { productJpaRepository.findByIdOrNull(any()) } returns productEntity + val result = productRepositoryImpl.findOne(createProduct.sku) + + verify { productJpaRepository.findByIdOrNull(any()) } + assert(result == createProduct) + } + + @Test + fun `Given a search entry by product, When invalid sku, Then must be invoke the find method of the jpa repository and return null`() { + every { productJpaRepository.findByIdOrNull(any()) } returns null + val result = productRepositoryImpl.findOne(createProduct.sku) + + verify { productJpaRepository.findByIdOrNull(any()) } + assert(result == null) + } +} diff --git a/test_boticario.postman_collection.json b/test_boticario.postman_collection.json new file mode 100644 index 00000000..493f529c --- /dev/null +++ b/test_boticario.postman_collection.json @@ -0,0 +1,107 @@ +{ + "info": { + "_postman_id": "855e1f53-b0f5-433a-a237-902cf2189ff7", + "name": "test boticario", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "19528947" + }, + "item": [ + { + "name": "create product", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"sku\": 5678,\n \"name\": \"produto 1\",\n \"inventory\": {\n \"warehouses\": [\n {\n \"locality\": \"RJ\",\n \"quantity\": 23,\n \"type\": \"ECOMMERCE\"\n },\n {\n \"locality\": \"MG\",\n \"quantity\": 12,\n \"type\": \"PHYSICAL_STORE\"\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8080/products", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "products" + ] + } + }, + "response": [] + }, + { + "name": "update product", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"produto 1\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8080/products/1234", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "products", + "1234" + ] + } + }, + "response": [] + }, + { + "name": "find product", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8080/products/1234", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "products", + "1234" + ] + } + }, + "response": [] + }, + { + "name": "delete product", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "http://localhost:8080/products/5678", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "products", + "5678" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file