diff --git a/frontend/.babelrc.json b/.babelrc.json similarity index 100% rename from frontend/.babelrc.json rename to .babelrc.json diff --git a/frontend/.eslintrc.js b/.eslintrc.js similarity index 100% rename from frontend/.eslintrc.js rename to .eslintrc.js diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index bd61107e9..16cd7428c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,7 +1,7 @@ --- name: 🐛 Bug Report about: 버그 리포트 이슈 템플릿입니다 -title: '[ALL/FE/BE] ' +title: '' labels: '' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 904cf1acc..fdc3eec22 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,7 +1,7 @@ --- name: ✨ Feature request about: feature 이슈 템플릿입니다 -title: '[ALL/FE/BE] ' +title: '' labels: '' assignees: '' --- diff --git a/.github/workflows/test-fe.yml b/.github/workflows/test-fe.yml index 4c2de2320..191c5cf2e 100644 --- a/.github/workflows/test-fe.yml +++ b/.github/workflows/test-fe.yml @@ -5,9 +5,6 @@ on: branches: - main - develop - paths: - - '.github/**' - - 'frontend/**' jobs: test: @@ -31,18 +28,16 @@ jobs: id: cache uses: actions/cache@v3 with: - path: '**/frontend/node_modules' + path: '**/node_modules' key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-node- - name: 의존성 설치 - working-directory: frontend/ if: steps.cache.outputs.cache-hit != 'true' run: yarn install --pure-lockfile - name: 테스트 실행 - working-directory: frontend/ run: yarn test continue-on-error: true @@ -50,13 +45,13 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: - files: '**/frontend/test-results/results.xml' + files: '**/test-results/results.xml' - name: 테스트 실패 시, 실패한 코드 라인에 Check 코멘트를 등록 uses: mikepenz/action-junit-report@v3 if: always() with: - report_paths: '**/frontend/test-results/results.xml' + report_paths: '**/test-results/results.xml' token: ${{ github.token }} - name: build 실패 시 Slack으로 알립니다 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 160ab01ed..000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: PR Test - -on: - pull_request: - branches: - - main - - develop - paths: - - '.github/**' - - 'backend/**' - -jobs: - test: - runs-on: ubuntu-latest - timeout-minutes: 10 - - permissions: - checks: write - pull-requests: write - - steps: - - name: Repository 체크아웃 - uses: actions/checkout@v3 - - - name: JDK 11 설정 - uses: actions/setup-java@v3 - with: - java-version: 11 - distribution: temurin - - - name: Gradle 캐싱 - uses: actions/cache@v3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - - name: Gradle 권한 부여 - working-directory: backend/ - run: chmod +x ./gradlew - - - name: 테스트 실행 - working-directory: backend/ - run: ./gradlew --info test - - - name: 테스트 결과 PR에 코멘트 등록 - uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() - with: - files: '**/backend/build/test-results/test/TEST-*.xml' - - - name: 테스트 실패 시, 실패한 코드 라인에 Check 코멘트를 등록 - uses: mikepenz/action-junit-report@v3 - if: always() - with: - report_paths: '**/backend/build/test-results/test/TEST-*.xml' - token: ${{ github.token }} - - - name: build 실패 시 Slack으로 알립니다 - uses: 8398a7/action-slack@v3 - with: - status: ${{ job.status }} - author_name: 백엔드 테스트 실패 알림 - fields: repo, message, commit, author, action, eventName, ref, workflow, job, took - env: - SLACK_CHANNEL: group-dev - SLACK_COLOR: '#FF2D00' - SLACK_USERNAME: 'Github Action' - SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png - SLACK_TITLE: Build Failure - ${{ github.event.pull_request.title }} - SLACK_MESSAGE: PR Url - ${{ github.event.pull_request.url }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - if: failure() diff --git a/frontend/.gitignore b/.gitignore similarity index 100% rename from frontend/.gitignore rename to .gitignore diff --git a/frontend/.nvmrc b/.nvmrc similarity index 100% rename from frontend/.nvmrc rename to .nvmrc diff --git a/frontend/.prettierrc b/.prettierrc similarity index 100% rename from frontend/.prettierrc rename to .prettierrc diff --git a/frontend/.storybook/main.ts b/.storybook/main.ts similarity index 100% rename from frontend/.storybook/main.ts rename to .storybook/main.ts diff --git a/frontend/.storybook/preview-body.html b/.storybook/preview-body.html similarity index 100% rename from frontend/.storybook/preview-body.html rename to .storybook/preview-body.html diff --git a/frontend/.storybook/preview.tsx b/.storybook/preview.tsx similarity index 100% rename from frontend/.storybook/preview.tsx rename to .storybook/preview.tsx diff --git a/frontend/.stylelintrc.js b/.stylelintrc.js similarity index 100% rename from frontend/.stylelintrc.js rename to .stylelintrc.js diff --git a/frontend/__tests__/hooks/useImageUploader.test.ts b/__tests__/hooks/useImageUploader.test.ts similarity index 100% rename from frontend/__tests__/hooks/useImageUploader.test.ts rename to __tests__/hooks/useImageUploader.test.ts diff --git a/frontend/__tests__/hooks/useStarRating.test.ts b/__tests__/hooks/useStarRating.test.ts similarity index 100% rename from frontend/__tests__/hooks/useStarRating.test.ts rename to __tests__/hooks/useStarRating.test.ts diff --git a/frontend/__tests__/hooks/useTabMenu.test.ts b/__tests__/hooks/useTabMenu.test.ts similarity index 100% rename from frontend/__tests__/hooks/useTabMenu.test.ts rename to __tests__/hooks/useTabMenu.test.ts diff --git a/backend/.gitignore b/backend/.gitignore deleted file mode 100644 index c2065bc26..000000000 --- a/backend/.gitignore +++ /dev/null @@ -1,37 +0,0 @@ -HELP.md -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ diff --git a/backend/build.gradle b/backend/build.gradle deleted file mode 100644 index 3d46c806e..000000000 --- a/backend/build.gradle +++ /dev/null @@ -1,40 +0,0 @@ -plugins { - id 'java' - id 'org.springframework.boot' version '2.7.13' - id 'io.spring.dependency-management' version '1.0.15.RELEASE' -} - -group = 'com.funeat' -version = '0.0.1-SNAPSHOT' - -java { - sourceCompatibility = '11' -} - -repositories { - mavenCentral() -} - -dependencies { - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-web' - runtimeOnly 'com.mysql:mysql-connector-j' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'io.rest-assured:rest-assured:4.4.0' - testRuntimeOnly 'com.h2database:h2' - - implementation 'org.springdoc:springdoc-openapi-ui:1.7.0' - implementation 'com.github.maricn:logback-slack-appender:1.4.0' - - implementation 'org.springframework.boot:spring-boot-starter-actuator' - runtimeOnly 'io.micrometer:micrometer-registry-prometheus' - - implementation 'com.amazonaws:aws-java-sdk-s3:1.12.547' - - implementation 'org.springframework.session:spring-session-jdbc' -} - -tasks.named('test') { - useJUnitPlatform() -} diff --git a/backend/gradle/wrapper/gradle-wrapper.jar b/backend/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index c1962a79e..000000000 Binary files a/backend/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/backend/gradle/wrapper/gradle-wrapper.properties b/backend/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 37aef8d3f..000000000 --- a/backend/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip -networkTimeout=10000 -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/backend/gradlew b/backend/gradlew deleted file mode 100755 index aeb74cbb4..000000000 --- a/backend/gradlew +++ /dev/null @@ -1,245 +0,0 @@ -#!/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/HEAD/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 - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# 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*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - 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 - - -# 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"' - -# 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/backend/gradlew.bat b/backend/gradlew.bat deleted file mode 100644 index 93e3f59f1..000000000 --- a/backend/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@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=. -@rem This is normally unused -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/backend/settings.gradle b/backend/settings.gradle deleted file mode 100644 index 7f580408d..000000000 --- a/backend/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'funeat' diff --git a/backend/src/main/java/com/funeat/FuneatApplication.java b/backend/src/main/java/com/funeat/FuneatApplication.java deleted file mode 100644 index 34909202c..000000000 --- a/backend/src/main/java/com/funeat/FuneatApplication.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.funeat; - -import com.funeat.common.repository.BaseRepositoryImpl; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; - -@EnableAsync -@SpringBootApplication -@EnableJpaRepositories(repositoryBaseClass = BaseRepositoryImpl.class) -public class FuneatApplication { - - public static void main(String[] args) { - SpringApplication.run(FuneatApplication.class, args); - } -} diff --git a/backend/src/main/java/com/funeat/admin/application/AdminChecker.java b/backend/src/main/java/com/funeat/admin/application/AdminChecker.java deleted file mode 100644 index 39045ce22..000000000 --- a/backend/src/main/java/com/funeat/admin/application/AdminChecker.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.funeat.admin.application; - -import com.funeat.admin.domain.AdminAuthInfo; -import com.funeat.auth.exception.AuthErrorCode; -import com.funeat.auth.exception.AuthException.NotLoggedInException; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -@Component -public class AdminChecker { - - @Value("${back-office.id}") - private String id; - - @Value("${back-office.key}") - private String key; - - public boolean check(final AdminAuthInfo adminAuthInfo) { - if (!id.equals(adminAuthInfo.getId())) { - throw new NotLoggedInException(AuthErrorCode.LOGIN_ADMIN_NOT_FOUND); - } - - if (!key.equals(adminAuthInfo.getKey())) { - throw new NotLoggedInException(AuthErrorCode.LOGIN_ADMIN_NOT_FOUND); - } - - return true; - } -} diff --git a/backend/src/main/java/com/funeat/admin/application/AdminService.java b/backend/src/main/java/com/funeat/admin/application/AdminService.java deleted file mode 100644 index 3c97c3c1b..000000000 --- a/backend/src/main/java/com/funeat/admin/application/AdminService.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.funeat.admin.application; - -import static com.funeat.product.exception.CategoryErrorCode.CATEGORY_NOT_FOUND; -import static com.funeat.product.exception.ProductErrorCode.PRODUCT_NOT_FOUND; - -import com.funeat.admin.dto.AdminProductResponse; -import com.funeat.admin.dto.AdminProductSearchResponse; -import com.funeat.admin.dto.AdminReviewResponse; -import com.funeat.admin.dto.AdminReviewSearchResponse; -import com.funeat.admin.dto.ProductCreateRequest; -import com.funeat.admin.dto.ProductSearchCondition; -import com.funeat.admin.dto.ProductUpdateRequest; -import com.funeat.admin.dto.ReviewSearchCondition; -import com.funeat.admin.repository.AdminProductRepository; -import com.funeat.admin.repository.AdminReviewRepository; -import com.funeat.admin.specification.AdminProductSpecification; -import com.funeat.admin.specification.AdminReviewSpecification; -import com.funeat.product.domain.Category; -import com.funeat.product.domain.Product; -import com.funeat.product.dto.CategoryResponse; -import com.funeat.product.exception.CategoryException.CategoryNotFoundException; -import com.funeat.product.exception.ProductException.ProductNotFoundException; -import com.funeat.product.persistence.CategoryRepository; -import com.funeat.product.persistence.ProductRepository; -import com.funeat.review.domain.Review; -import java.util.List; -import java.util.stream.Collectors; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@Transactional(readOnly = true) -public class AdminService { - - private static final int DEFAULT_PAGE_SIZE = 10; - - private final ProductRepository productRepository; - private final AdminProductRepository adminProductRepository; - private final CategoryRepository categoryRepository; - private final AdminReviewRepository adminReviewRepository; - - public AdminService(final ProductRepository productRepository, final AdminProductRepository adminProductRepository, - final CategoryRepository categoryRepository, final AdminReviewRepository adminReviewRepository) { - this.productRepository = productRepository; - this.adminProductRepository = adminProductRepository; - this.categoryRepository = categoryRepository; - this.adminReviewRepository = adminReviewRepository; - } - - @Transactional - public Long addProduct(final ProductCreateRequest request) { - final Category findCategory = categoryRepository.findById(request.getCategoryId()) - .orElseThrow(() -> new CategoryNotFoundException(CATEGORY_NOT_FOUND, request.getCategoryId())); - - final Product product = Product.create(request.getName(), request.getPrice(), request.getContent(), - findCategory); - - return productRepository.save(product).getId(); - } - - public List getAllCategories() { - final List findCategories = categoryRepository.findAll(); - - return findCategories.stream() - .map(CategoryResponse::toResponse) - .collect(Collectors.toList()); - } - - public AdminProductSearchResponse getSearchProducts(final ProductSearchCondition condition, - final Pageable pageable) { - final Specification specification = AdminProductSpecification.searchBy(condition); - - final Page findProducts = adminProductRepository.findAllForPagination(specification, pageable, - condition.getTotalElements()); - - final List productResponses = findProducts.stream() - .map(AdminProductResponse::toResponse) - .collect(Collectors.toList()); - - final Boolean isLastPage = isLastPage(findProducts, condition.getPrePage()); - - return new AdminProductSearchResponse(productResponses, findProducts.getTotalElements(), isLastPage); - } - - private boolean isLastPage(final Page findProducts, Long prePage) { - return prePage * DEFAULT_PAGE_SIZE + findProducts.getContent().size() == findProducts.getTotalElements(); - } - - @Transactional - public void updateProduct(final Long productId, final ProductUpdateRequest request) { - final Product findProduct = productRepository.findById(productId) - .orElseThrow(() -> new ProductNotFoundException(PRODUCT_NOT_FOUND, productId)); - - final Category findCategory = categoryRepository.findById(request.getCategoryId()) - .orElseThrow(() -> new CategoryNotFoundException(CATEGORY_NOT_FOUND, request.getCategoryId())); - - findProduct.update(request.getName(), request.getContent(), request.getPrice(), findCategory); - } - - public AdminReviewSearchResponse getSearchReviews(final ReviewSearchCondition condition, final Pageable pageable) { - final Specification specification = AdminReviewSpecification.searchBy(condition); - - final Page findReviews = adminReviewRepository.findAllForPagination(specification, pageable, - condition.getTotalElements()); - - final List reviewResponses = findReviews.stream() - .map(AdminReviewResponse::toResponse) - .collect(Collectors.toList()); - - final Boolean isLastPage = isLastPage(findReviews, condition.getPrePage()); - - return new AdminReviewSearchResponse(reviewResponses, findReviews.getTotalElements(), isLastPage); - } -} diff --git a/backend/src/main/java/com/funeat/admin/domain/AdminAuthInfo.java b/backend/src/main/java/com/funeat/admin/domain/AdminAuthInfo.java deleted file mode 100644 index 1a1fb23b8..000000000 --- a/backend/src/main/java/com/funeat/admin/domain/AdminAuthInfo.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.funeat.admin.domain; - -public class AdminAuthInfo { - - private final String id; - private final String key; - - public AdminAuthInfo(final String id, final String key) { - this.id = id; - this.key = key; - } - - public String getId() { - return id; - } - - public String getKey() { - return key; - } -} diff --git a/backend/src/main/java/com/funeat/admin/dto/AdminCategoryResponse.java b/backend/src/main/java/com/funeat/admin/dto/AdminCategoryResponse.java deleted file mode 100644 index 86c47a715..000000000 --- a/backend/src/main/java/com/funeat/admin/dto/AdminCategoryResponse.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.funeat.admin.dto; - -import com.funeat.product.domain.Category; - -public class AdminCategoryResponse { - - private final Long id; - private final String name; - private final String image; - - public AdminCategoryResponse(final Long id, final String name, final String image) { - this.id = id; - this.name = name; - this.image = image; - } - - public static AdminCategoryResponse toResponse(final Category category) { - return new AdminCategoryResponse(category.getId(), category.getName(), category.getImage()); - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getImage() { - return image; - } -} diff --git a/backend/src/main/java/com/funeat/admin/dto/AdminProductResponse.java b/backend/src/main/java/com/funeat/admin/dto/AdminProductResponse.java deleted file mode 100644 index 0f5d2dac5..000000000 --- a/backend/src/main/java/com/funeat/admin/dto/AdminProductResponse.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.funeat.admin.dto; - -import com.funeat.product.domain.Product; - -public class AdminProductResponse { - - private final Long id; - private final String name; - private final String content; - private final Long price; - private final AdminCategoryResponse categoryResponse; - - private AdminProductResponse(final Long id, final String name, final String content, - final Long price, final AdminCategoryResponse categoryResponse) { - this.id = id; - this.name = name; - this.content = content; - this.price = price; - this.categoryResponse = categoryResponse; - } - - public static AdminProductResponse toResponse(final Product product) { - final AdminCategoryResponse categoryResponse = AdminCategoryResponse.toResponse(product.getCategory()); - - return new AdminProductResponse(product.getId(), product.getName(), product.getContent(), product.getPrice(), - categoryResponse); - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getContent() { - return content; - } - - public Long getPrice() { - return price; - } - - public AdminCategoryResponse getCategoryResponse() { - return categoryResponse; - } -} diff --git a/backend/src/main/java/com/funeat/admin/dto/AdminProductSearchResponse.java b/backend/src/main/java/com/funeat/admin/dto/AdminProductSearchResponse.java deleted file mode 100644 index 0f543344e..000000000 --- a/backend/src/main/java/com/funeat/admin/dto/AdminProductSearchResponse.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.funeat.admin.dto; - -import java.util.List; - -public class AdminProductSearchResponse { - - private final List productResponses; - private final Long totalElements; - private final Boolean isLastPage; - - public AdminProductSearchResponse(final List productResponses, final Long totalElements, - final Boolean isLastPage) { - this.productResponses = productResponses; - this.totalElements = totalElements; - this.isLastPage = isLastPage; - } - - public List getProductResponses() { - return productResponses; - } - - public Long getTotalElements() { - return totalElements; - } - - public Boolean isLastPage() { - return isLastPage; - } -} diff --git a/backend/src/main/java/com/funeat/admin/dto/AdminReviewResponse.java b/backend/src/main/java/com/funeat/admin/dto/AdminReviewResponse.java deleted file mode 100644 index d9f4bddcf..000000000 --- a/backend/src/main/java/com/funeat/admin/dto/AdminReviewResponse.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.funeat.admin.dto; - -import com.funeat.review.domain.Review; -import java.time.LocalDateTime; - -public class AdminReviewResponse { - - private final Long id; - private final String userName; - private final String content; - private final String image; - private final String productName; - private final LocalDateTime createdAt; - - private AdminReviewResponse(final Long id, final String userName, final String content, - final String image, final String productName, - final LocalDateTime createdAt) { - this.id = id; - this.userName = userName; - this.content = content; - this.image = image; - this.productName = productName; - this.createdAt = createdAt; - } - - public static AdminReviewResponse toResponse(final Review review) { - return new AdminReviewResponse(review.getId(), review.getMember().getNickname(), review.getContent(), - review.getImage(), review.getProduct().getName(), review.getCreatedAt()); - } - - public Long getId() { - return id; - } - - public String getUserName() { - return userName; - } - - public String getContent() { - return content; - } - - public String getImage() { - return image; - } - - public String getProductName() { - return productName; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } -} diff --git a/backend/src/main/java/com/funeat/admin/dto/AdminReviewSearchResponse.java b/backend/src/main/java/com/funeat/admin/dto/AdminReviewSearchResponse.java deleted file mode 100644 index 1b5c0c12b..000000000 --- a/backend/src/main/java/com/funeat/admin/dto/AdminReviewSearchResponse.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.funeat.admin.dto; - -import java.util.List; - -public class AdminReviewSearchResponse { - - private final List reviewResponses; - private final Long totalElements; - private final Boolean isLastPage; - - public AdminReviewSearchResponse(final List reviewResponses, final Long totalElements, - final Boolean isLastPage) { - this.reviewResponses = reviewResponses; - this.totalElements = totalElements; - this.isLastPage = isLastPage; - } - - public List getReviewResponses() { - return reviewResponses; - } - - public Long getTotalElements() { - return totalElements; - } - - public Boolean getLastPage() { - return isLastPage; - } -} diff --git a/backend/src/main/java/com/funeat/admin/dto/ProductCreateRequest.java b/backend/src/main/java/com/funeat/admin/dto/ProductCreateRequest.java deleted file mode 100644 index dd835efa3..000000000 --- a/backend/src/main/java/com/funeat/admin/dto/ProductCreateRequest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.funeat.admin.dto; - -public class ProductCreateRequest { - - private final String name; - private final Long price; - private final String content; - private final Long categoryId; - - public ProductCreateRequest(final String name, final Long price, final String content, final Long categoryId) { - this.name = name; - this.price = price; - this.content = content; - this.categoryId = categoryId; - } - - public String getName() { - return name; - } - - public Long getPrice() { - return price; - } - - public String getContent() { - return content; - } - - public Long getCategoryId() { - return categoryId; - } -} diff --git a/backend/src/main/java/com/funeat/admin/dto/ProductSearchCondition.java b/backend/src/main/java/com/funeat/admin/dto/ProductSearchCondition.java deleted file mode 100644 index 6114176c2..000000000 --- a/backend/src/main/java/com/funeat/admin/dto/ProductSearchCondition.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.funeat.admin.dto; - -public class ProductSearchCondition { - - private final String name; - private final Long id; - private final Long categoryId; - private final Long totalElements; - private final Long prePage; - - public ProductSearchCondition(final String name, final Long id, final Long categoryId, - final Long totalElements, final Long prePage) { - this.name = name; - this.id = id; - this.categoryId = categoryId; - this.totalElements = totalElements; - this.prePage = prePage; - } - - public String getName() { - return name; - } - - public Long getId() { - return id; - } - - public Long getCategoryId() { - return categoryId; - } - - public Long getTotalElements() { - return totalElements; - } - - public Long getPrePage() { - return prePage; - } -} diff --git a/backend/src/main/java/com/funeat/admin/dto/ProductUpdateRequest.java b/backend/src/main/java/com/funeat/admin/dto/ProductUpdateRequest.java deleted file mode 100644 index 31d639be4..000000000 --- a/backend/src/main/java/com/funeat/admin/dto/ProductUpdateRequest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.funeat.admin.dto; - -public class ProductUpdateRequest { - - private final String name; - private final Long price; - private final String content; - private final Long categoryId; - - public ProductUpdateRequest(final String name, final Long price, final String content, final Long categoryId) { - this.name = name; - this.price = price; - this.content = content; - this.categoryId = categoryId; - } - - public String getName() { - return name; - } - - public Long getPrice() { - return price; - } - - public String getContent() { - return content; - } - - public Long getCategoryId() { - return categoryId; - } -} diff --git a/backend/src/main/java/com/funeat/admin/dto/ReviewSearchCondition.java b/backend/src/main/java/com/funeat/admin/dto/ReviewSearchCondition.java deleted file mode 100644 index 02ce7e902..000000000 --- a/backend/src/main/java/com/funeat/admin/dto/ReviewSearchCondition.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.funeat.admin.dto; - -import java.time.LocalDateTime; -import org.springframework.format.annotation.DateTimeFormat; - -public class ReviewSearchCondition { - - private final Long productId; - - @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private final LocalDateTime from; - - @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private final LocalDateTime to; - - private final Long id; - private final Long totalElements; - private final Long prePage; - - public ReviewSearchCondition(final Long productId, final LocalDateTime from, final LocalDateTime to, - final Long id, final Long totalElements, final Long prePage) { - this.productId = productId; - this.from = from; - this.to = to; - this.id = id; - this.totalElements = totalElements; - this.prePage = prePage; - } - - public Long getProductId() { - return productId; - } - - public LocalDateTime getFrom() { - return from; - } - - public LocalDateTime getTo() { - return to; - } - - public Long getId() { - return id; - } - - public Long getTotalElements() { - return totalElements; - } - - public Long getPrePage() { - return prePage; - } -} diff --git a/backend/src/main/java/com/funeat/admin/presentation/AdminController.java b/backend/src/main/java/com/funeat/admin/presentation/AdminController.java deleted file mode 100644 index 6b22890b3..000000000 --- a/backend/src/main/java/com/funeat/admin/presentation/AdminController.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.funeat.admin.presentation; - -import com.funeat.admin.application.AdminService; -import com.funeat.admin.dto.AdminProductSearchResponse; -import com.funeat.admin.dto.AdminReviewSearchResponse; -import com.funeat.admin.dto.ProductCreateRequest; -import com.funeat.admin.dto.ProductSearchCondition; -import com.funeat.admin.dto.ProductUpdateRequest; -import com.funeat.admin.dto.ReviewSearchCondition; -import com.funeat.product.dto.CategoryResponse; -import java.net.URI; -import java.util.List; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -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.RestController; - -@RestController -@RequestMapping("/api/admin") -public class AdminController { - - private final AdminService adminService; - - public AdminController(final AdminService adminService) { - this.adminService = adminService; - } - - @PostMapping("/products") - public ResponseEntity addProduct(@RequestBody final ProductCreateRequest request) { - final Long productId = adminService.addProduct(request); - - return ResponseEntity.created(URI.create("/api/products/" + productId)).build(); - } - - @GetMapping("/products") - public ResponseEntity getSearchProducts( - @ModelAttribute final ProductSearchCondition condition, - @PageableDefault final Pageable pageable) { - final AdminProductSearchResponse response = adminService.getSearchProducts(condition, pageable); - return ResponseEntity.ok(response); - } - - @PutMapping("/products/{productId}") - public ResponseEntity updateProduct(@PathVariable final Long productId, - @RequestBody final ProductUpdateRequest request) { - adminService.updateProduct(productId, request); - return ResponseEntity.noContent().build(); - } - - @GetMapping("/categories") - public ResponseEntity> getAllCategories() { - final List responses = adminService.getAllCategories(); - return ResponseEntity.ok(responses); - } - - @GetMapping("/reviews") - public ResponseEntity getSearchReviews( - @ModelAttribute final ReviewSearchCondition condition, - @PageableDefault final Pageable pageable) { - final AdminReviewSearchResponse responses = adminService.getSearchReviews(condition, pageable); - return ResponseEntity.ok(responses); - } -} diff --git a/backend/src/main/java/com/funeat/admin/presentation/AdminLoginController.java b/backend/src/main/java/com/funeat/admin/presentation/AdminLoginController.java deleted file mode 100644 index d23125a85..000000000 --- a/backend/src/main/java/com/funeat/admin/presentation/AdminLoginController.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.funeat.admin.presentation; - -import com.funeat.admin.application.AdminChecker; -import com.funeat.admin.domain.AdminAuthInfo; -import javax.servlet.http.HttpServletRequest; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api/admin") -public class AdminLoginController { - - private final AdminChecker adminChecker; - - public AdminLoginController(final AdminChecker adminChecker) { - this.adminChecker = adminChecker; - } - - @PostMapping("/login") - public ResponseEntity login(@RequestBody final AdminAuthInfo adminAuthInfo, - final HttpServletRequest request) { - adminChecker.check(adminAuthInfo); - - request.getSession().setAttribute("authId", adminAuthInfo.getId()); - request.getSession().setAttribute("authKey", adminAuthInfo.getKey()); - - return ResponseEntity.noContent().build(); - } - - @GetMapping("/logged-check") - public ResponseEntity validLoggedInAdmin() { - return ResponseEntity.ok(true); - } -} diff --git a/backend/src/main/java/com/funeat/admin/repository/AdminProductRepository.java b/backend/src/main/java/com/funeat/admin/repository/AdminProductRepository.java deleted file mode 100644 index 6dddc02a2..000000000 --- a/backend/src/main/java/com/funeat/admin/repository/AdminProductRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.funeat.admin.repository; - -import com.funeat.common.repository.BaseRepository; -import com.funeat.product.domain.Product; - -public interface AdminProductRepository extends BaseRepository { -} diff --git a/backend/src/main/java/com/funeat/admin/repository/AdminReviewRepository.java b/backend/src/main/java/com/funeat/admin/repository/AdminReviewRepository.java deleted file mode 100644 index 5da483f2e..000000000 --- a/backend/src/main/java/com/funeat/admin/repository/AdminReviewRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.funeat.admin.repository; - -import com.funeat.common.repository.BaseRepository; -import com.funeat.review.domain.Review; - -public interface AdminReviewRepository extends BaseRepository { -} diff --git a/backend/src/main/java/com/funeat/admin/specification/AdminProductSpecification.java b/backend/src/main/java/com/funeat/admin/specification/AdminProductSpecification.java deleted file mode 100644 index c48ea0305..000000000 --- a/backend/src/main/java/com/funeat/admin/specification/AdminProductSpecification.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.funeat.admin.specification; - -import com.funeat.admin.dto.ProductSearchCondition; -import com.funeat.product.domain.Category; -import com.funeat.product.domain.Product; -import java.util.List; -import java.util.Objects; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Path; -import org.springframework.data.jpa.domain.Specification; - -public class AdminProductSpecification { - - private static final List> COUNT_RESULT_TYPES = List.of(Long.class, long.class); - - private AdminProductSpecification() { - } - - public static Specification searchBy(final ProductSearchCondition condition) { - return (root, query, criteriaBuilder) -> { - if (!COUNT_RESULT_TYPES.contains(query.getResultType())) { - root.fetch("category", JoinType.LEFT); - } - - criteriaBuilder.desc(root.get("id")); - - return Specification - .where(like(condition.getName())) - .and(lessThan(condition.getId())) - .and(sameCategory(condition.getCategoryId())) - .toPredicate(root, query, criteriaBuilder); - }; - } - - private static Specification like(final String productName) { - return (root, query, criteriaBuilder) -> { - if (Objects.isNull(productName)) { - return null; - } - - final Path namePath = root.get("name"); - - return criteriaBuilder.like(namePath, "%" + productName + "%"); - }; - } - - private static Specification lessThan(final Long productId) { - return (root, query, criteriaBuilder) -> { - if (Objects.isNull(productId)) { - return null; - } - - final Path productIdPath = root.get("id"); - - return criteriaBuilder.lessThan(productIdPath, productId); - }; - } - - private static Specification sameCategory(final Long categoryId) { - return (root, query, criteriaBuilder) -> { - if (Objects.isNull(categoryId)) { - return null; - } - - final Path categoryPath = root.get("category"); - - return criteriaBuilder.equal(categoryPath, categoryId); - }; - } -} diff --git a/backend/src/main/java/com/funeat/admin/specification/AdminReviewSpecification.java b/backend/src/main/java/com/funeat/admin/specification/AdminReviewSpecification.java deleted file mode 100644 index 045147de5..000000000 --- a/backend/src/main/java/com/funeat/admin/specification/AdminReviewSpecification.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.funeat.admin.specification; - -import com.funeat.admin.dto.ReviewSearchCondition; -import com.funeat.product.domain.Product; -import com.funeat.review.domain.Review; -import java.time.LocalDateTime; -import java.util.Objects; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Path; -import org.springframework.data.jpa.domain.Specification; - -public class AdminReviewSpecification { - - private AdminReviewSpecification() { - } - - public static Specification searchBy(final ReviewSearchCondition condition) { - return (root, query, criteriaBuilder) -> { - if (query.getResultType() != Long.class && query.getResultType() != long.class) { - root.fetch("member", JoinType.LEFT); - root.fetch("product", JoinType.LEFT); - } - - criteriaBuilder.desc(root.get("id")); - - return Specification - .where(to(condition.getTo())) - .and(from(condition.getFrom())) - .and(sameProduct(condition.getProductId())) - .and(lessThan(condition.getId())) - .toPredicate(root, query, criteriaBuilder); - }; - } - - private static Specification to(final LocalDateTime to) { - return (root, query, criteriaBuilder) -> { - if (Objects.isNull(to)) { - return null; - } - - final Path toPath = root.get("createdAt"); - - return criteriaBuilder.lessThanOrEqualTo(toPath, to); - }; - } - - private static Specification from(final LocalDateTime from) { - return (root, query, criteriaBuilder) -> { - if (Objects.isNull(from)) { - return null; - } - - final Path fromPath = root.get("createdAt"); - - return criteriaBuilder.greaterThanOrEqualTo(fromPath, from); - }; - } - - private static Specification sameProduct(final Long productId) { - return (root, query, criteriaBuilder) -> { - if (Objects.isNull(productId)) { - return null; - } - - final Path productPath = root.get("product"); - - return criteriaBuilder.equal(productPath, productId); - }; - } - - private static Specification lessThan(final Long reviewId) { - return (root, query, criteriaBuilder) -> { - if (Objects.isNull(reviewId)) { - return null; - } - - final Path reviewIdPath = root.get("id"); - - return criteriaBuilder.lessThan(reviewIdPath, reviewId); - }; - } -} diff --git a/backend/src/main/java/com/funeat/admin/util/AdminCheckInterceptor.java b/backend/src/main/java/com/funeat/admin/util/AdminCheckInterceptor.java deleted file mode 100644 index 59ef45b49..000000000 --- a/backend/src/main/java/com/funeat/admin/util/AdminCheckInterceptor.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.funeat.admin.util; - -import static com.funeat.auth.exception.AuthErrorCode.LOGIN_ADMIN_NOT_FOUND; - -import com.funeat.admin.application.AdminChecker; -import com.funeat.admin.domain.AdminAuthInfo; -import com.funeat.auth.exception.AuthException.NotLoggedInException; -import java.util.Objects; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.HandlerInterceptor; - -@Component -public class AdminCheckInterceptor implements HandlerInterceptor { - - private final AdminChecker adminChecker; - - public AdminCheckInterceptor(final AdminChecker adminChecker) { - this.adminChecker = adminChecker; - } - - @Override - public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, - final Object handler) { - final HttpSession session = request.getSession(false); - - if (Objects.isNull(session)) { - throw new NotLoggedInException(LOGIN_ADMIN_NOT_FOUND); - } - - final String authId = String.valueOf(session.getAttribute("authId")); - final String authKey = String.valueOf(session.getAttribute("authKey")); - - if (Objects.isNull(authId) || Objects.isNull(authKey)) { - throw new NotLoggedInException(LOGIN_ADMIN_NOT_FOUND); - } - - return adminChecker.check(new AdminAuthInfo(authId, authKey)); - } -} diff --git a/backend/src/main/java/com/funeat/auth/application/AuthService.java b/backend/src/main/java/com/funeat/auth/application/AuthService.java deleted file mode 100644 index 20a24345c..000000000 --- a/backend/src/main/java/com/funeat/auth/application/AuthService.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.funeat.auth.application; - -import com.funeat.auth.dto.SignUserDto; -import com.funeat.auth.dto.UserInfoDto; -import com.funeat.auth.util.PlatformUserProvider; -import com.funeat.member.application.MemberService; -import javax.servlet.http.Cookie; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@Transactional(readOnly = true) -public class AuthService { - - private static final String COOKIE_NAME = "SESSION"; - - private final MemberService memberService; - private final PlatformUserProvider platformUserProvider; - - public AuthService(final MemberService memberService, final PlatformUserProvider platformUserProvider) { - this.memberService = memberService; - this.platformUserProvider = platformUserProvider; - } - - public SignUserDto loginWithKakao(final String code) { - final UserInfoDto userInfoDto = platformUserProvider.getPlatformUser(code); - return memberService.findOrCreateMember(userInfoDto); - } - - public String getLoginRedirectUri() { - return platformUserProvider.getRedirectURI(); - } - - public void logoutWithKakao(final Long memberId) { - final String platformId = memberService.findPlatformId(memberId); - platformUserProvider.logout(platformId); - } - - public Cookie expireCookie() { - final Cookie cookie = new Cookie(COOKIE_NAME, null); - cookie.setMaxAge(0); - cookie.setPath("/"); - - return cookie; - } -} diff --git a/backend/src/main/java/com/funeat/auth/dto/KakaoTokenDto.java b/backend/src/main/java/com/funeat/auth/dto/KakaoTokenDto.java deleted file mode 100644 index 6b4f0995e..000000000 --- a/backend/src/main/java/com/funeat/auth/dto/KakaoTokenDto.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.funeat.auth.dto; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class KakaoTokenDto { - - private final String accessToken; - private final String tokenType; - private final String refreshToken; - private final String expiresIn; - private final String scope; - private final String refreshTokenExpiresIn; - - @JsonCreator - public KakaoTokenDto(@JsonProperty("access_token") final String accessToken, - @JsonProperty("token_type") final String tokenType, - @JsonProperty("refresh_token") final String refreshToken, - @JsonProperty("expires_in") final String expiresIn, - @JsonProperty("scope") final String scope, - @JsonProperty("refresh_token_expires_in") final String refreshTokenExpiresIn) { - this.accessToken = accessToken; - this.tokenType = tokenType; - this.refreshToken = refreshToken; - this.expiresIn = expiresIn; - this.scope = scope; - this.refreshTokenExpiresIn = refreshTokenExpiresIn; - } - - public String getAccessToken() { - return accessToken; - } - - public String getTokenType() { - return tokenType; - } - - public String getRefreshToken() { - return refreshToken; - } - - public String getExpiresIn() { - return expiresIn; - } - - public String getScope() { - return scope; - } - - public String getRefreshTokenExpiresIn() { - return refreshTokenExpiresIn; - } -} diff --git a/backend/src/main/java/com/funeat/auth/dto/KakaoUserInfoDto.java b/backend/src/main/java/com/funeat/auth/dto/KakaoUserInfoDto.java deleted file mode 100644 index 8b9424045..000000000 --- a/backend/src/main/java/com/funeat/auth/dto/KakaoUserInfoDto.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.funeat.auth.dto; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class KakaoUserInfoDto { - - private final Long id; - private final KakaoAccount kakaoAccount; - - @JsonCreator - public KakaoUserInfoDto(@JsonProperty("id") final Long id, - @JsonProperty("kakao_account") final KakaoAccount kakaoAccount) { - this.id = id; - this.kakaoAccount = kakaoAccount; - } - - public Long getId() { - return id; - } - - public KakaoAccount getKakaoAccount() { - return kakaoAccount; - } - - public static class KakaoAccount { - - private final KakaoProfile profile; - - @JsonCreator - public KakaoAccount(@JsonProperty("profile") final KakaoProfile profile) { - this.profile = profile; - } - - public KakaoProfile getProfile() { - return profile; - } - } - - public static class KakaoProfile { - - private final String nickname; - private final String profileImageUrl; - - @JsonCreator - public KakaoProfile( - @JsonProperty("nickname") final String nickname, - @JsonProperty("profile_image_url") final String profileImageUrl) { - this.nickname = nickname; - this.profileImageUrl = profileImageUrl; - } - - public String getNickname() { - return nickname; - } - - public String getProfileImageUrl() { - return profileImageUrl; - } - } -} diff --git a/backend/src/main/java/com/funeat/auth/dto/LoginInfo.java b/backend/src/main/java/com/funeat/auth/dto/LoginInfo.java deleted file mode 100644 index 2987c111a..000000000 --- a/backend/src/main/java/com/funeat/auth/dto/LoginInfo.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.funeat.auth.dto; - -public class LoginInfo { - - private final Long id; - - public LoginInfo(final Long id) { - this.id = id; - } - - public Long getId() { - return id; - } -} diff --git a/backend/src/main/java/com/funeat/auth/dto/SignUserDto.java b/backend/src/main/java/com/funeat/auth/dto/SignUserDto.java deleted file mode 100644 index 2e910ee77..000000000 --- a/backend/src/main/java/com/funeat/auth/dto/SignUserDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.funeat.auth.dto; - -import com.funeat.member.domain.Member; - -public class SignUserDto { - - private final boolean isSignUp; - private final Member member; - - public SignUserDto(final boolean isSignUp, final Member member) { - this.isSignUp = isSignUp; - this.member = member; - } - - public static SignUserDto of(final boolean isSignUp, final Member member) { - return new SignUserDto(isSignUp, member); - } - - public boolean isSignUp() { - return isSignUp; - } - - public Member getMember() { - return member; - } -} diff --git a/backend/src/main/java/com/funeat/auth/dto/TokenResponse.java b/backend/src/main/java/com/funeat/auth/dto/TokenResponse.java deleted file mode 100644 index 842cea22b..000000000 --- a/backend/src/main/java/com/funeat/auth/dto/TokenResponse.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.funeat.auth.dto; - -public class TokenResponse { - - private String accessToken; - - public TokenResponse(final String accessToken) { - this.accessToken = accessToken; - } - - public String getAccessToken() { - return accessToken; - } -} diff --git a/backend/src/main/java/com/funeat/auth/dto/UserInfoDto.java b/backend/src/main/java/com/funeat/auth/dto/UserInfoDto.java deleted file mode 100644 index 3861eac64..000000000 --- a/backend/src/main/java/com/funeat/auth/dto/UserInfoDto.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.funeat.auth.dto; - -import com.funeat.member.domain.Member; - -public class UserInfoDto { - - private final Long id; - private final String nickname; - private final String profileImageUrl; - - public UserInfoDto(final Long id, final String nickname, final String profileImageUrl) { - this.id = id; - this.nickname = nickname; - this.profileImageUrl = profileImageUrl; - } - - public static UserInfoDto from(final KakaoUserInfoDto kakaoUserInfoDto) { - return new UserInfoDto( - kakaoUserInfoDto.getId(), - kakaoUserInfoDto.getKakaoAccount().getProfile().getNickname(), - kakaoUserInfoDto.getKakaoAccount().getProfile().getProfileImageUrl() - ); - } - - public Member toMember() { - return new Member(this.nickname, this.profileImageUrl, this.id.toString()); - } - - public Long getId() { - return id; - } - - public String getNickname() { - return nickname; - } - - public String getProfileImageUrl() { - return profileImageUrl; - } -} diff --git a/backend/src/main/java/com/funeat/auth/exception/AuthErrorCode.java b/backend/src/main/java/com/funeat/auth/exception/AuthErrorCode.java deleted file mode 100644 index e47350078..000000000 --- a/backend/src/main/java/com/funeat/auth/exception/AuthErrorCode.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.funeat.auth.exception; - -import org.springframework.http.HttpStatus; - -public enum AuthErrorCode { - - LOGIN_MEMBER_NOT_FOUND(HttpStatus.UNAUTHORIZED, "로그인 하지 않은 회원입니다. 로그인을 해주세요.", "6001"), - LOGIN_ADMIN_NOT_FOUND(HttpStatus.UNAUTHORIZED, "로그인 하지 않은 관리자입니다. 로그인을 해주세요.", "6002"), - ; - - private final HttpStatus status; - private final String message; - private final String code; - - AuthErrorCode(final HttpStatus status, final String message, final String code) { - this.status = status; - this.message = message; - this.code = code; - } - - public HttpStatus getStatus() { - return status; - } - - public String getMessage() { - return message; - } - - public String getCode() { - return code; - } -} diff --git a/backend/src/main/java/com/funeat/auth/exception/AuthException.java b/backend/src/main/java/com/funeat/auth/exception/AuthException.java deleted file mode 100644 index c8ee3ef92..000000000 --- a/backend/src/main/java/com/funeat/auth/exception/AuthException.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.funeat.auth.exception; - -import com.funeat.exception.ErrorCode; -import com.funeat.exception.GlobalException; -import org.springframework.http.HttpStatus; - -public class AuthException extends GlobalException { - - public AuthException(final HttpStatus status, final ErrorCode errorCode) { - super(status, errorCode); - } - - public static class NotLoggedInException extends AuthException { - public NotLoggedInException(final AuthErrorCode errorCode) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage())); - } - } -} diff --git a/backend/src/main/java/com/funeat/auth/presentation/AuthApiController.java b/backend/src/main/java/com/funeat/auth/presentation/AuthApiController.java deleted file mode 100644 index f7931460c..000000000 --- a/backend/src/main/java/com/funeat/auth/presentation/AuthApiController.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.funeat.auth.presentation; - -import com.funeat.auth.application.AuthService; -import com.funeat.auth.dto.LoginInfo; -import com.funeat.auth.dto.SignUserDto; -import com.funeat.auth.util.AuthenticationPrincipal; -import java.net.URI; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class AuthApiController implements AuthController { - - private static final String SESSION_ATTRIBUTE_NAME = "member"; - private static final String HOME = "/"; - private static final String MY_PAGE = "/members"; - - private final AuthService authService; - - public AuthApiController(final AuthService authService) { - this.authService = authService; - } - - @GetMapping("/api/auth/kakao") - public ResponseEntity kakaoLogin() { - return ResponseEntity.status(HttpStatus.FOUND) - .location(URI.create(authService.getLoginRedirectUri())) - .build(); - } - - @GetMapping("/api/login/oauth2/code/kakao") - public ResponseEntity loginAuthorizeUser(@RequestParam("code") final String code, - final HttpServletRequest request) { - final SignUserDto signUserDto = authService.loginWithKakao(code); - final Long memberId = signUserDto.getMember().getId(); - request.getSession().setAttribute(SESSION_ATTRIBUTE_NAME, memberId); - - if (signUserDto.isSignUp()) { - return ResponseEntity.ok() - .location(URI.create(MY_PAGE)) - .build(); - } - return ResponseEntity.ok() - .location(URI.create(HOME)) - .build(); - } - - @PostMapping("/api/logout") - public ResponseEntity logout(@AuthenticationPrincipal final LoginInfo loginInfo, - final HttpServletRequest request, final HttpServletResponse response) { - authService.logoutWithKakao(loginInfo.getId()); - - request.getSession().removeAttribute(SESSION_ATTRIBUTE_NAME); - - final Cookie cookie = authService.expireCookie(); - response.addCookie(cookie); - - return ResponseEntity.status(HttpStatus.FOUND) - .location(URI.create(HOME)) - .build(); - } -} diff --git a/backend/src/main/java/com/funeat/auth/presentation/AuthController.java b/backend/src/main/java/com/funeat/auth/presentation/AuthController.java deleted file mode 100644 index db05a7f8b..000000000 --- a/backend/src/main/java/com/funeat/auth/presentation/AuthController.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.funeat.auth.presentation; - -import com.funeat.auth.dto.LoginInfo; -import com.funeat.auth.util.AuthenticationPrincipal; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; - -@Tag(name = "06.Login", description = "로그인 관련 API 입니다.") -public interface AuthController { - - @Operation(summary = "카카오 로그인", description = "카카오 로그인을 한다") - @ApiResponse( - responseCode = "302", - description = "로그인이 성공하여 리다이렉트함" - ) - @GetMapping - ResponseEntity kakaoLogin(); - - @Operation(summary = "유저 인증", description = "유저 인증을 한다") - @ApiResponse( - responseCode = "302", - description = "기존 회원이면 홈으로 이동, 신규 회원이면 마이페이지로 이동." - ) - @GetMapping - ResponseEntity loginAuthorizeUser(@RequestParam("code") final String code, final HttpServletRequest request); - - @Operation(summary = "로그아웃", description = "로그아웃을 한다") - @ApiResponse( - responseCode = "302", - description = "로그아웃 성공." - ) - @PostMapping - ResponseEntity logout(@AuthenticationPrincipal final LoginInfo loginInfo, final HttpServletRequest request, - final HttpServletResponse response); -} diff --git a/backend/src/main/java/com/funeat/auth/util/AuthArgumentResolver.java b/backend/src/main/java/com/funeat/auth/util/AuthArgumentResolver.java deleted file mode 100644 index e5a3e0e9b..000000000 --- a/backend/src/main/java/com/funeat/auth/util/AuthArgumentResolver.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.funeat.auth.util; - -import com.funeat.auth.dto.LoginInfo; -import java.util.Objects; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import org.springframework.core.MethodParameter; -import org.springframework.stereotype.Component; -import org.springframework.web.bind.support.WebDataBinderFactory; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.method.support.ModelAndViewContainer; - -@Component -public class AuthArgumentResolver implements HandlerMethodArgumentResolver { - - @Override - public boolean supportsParameter(final MethodParameter parameter) { - return parameter.hasParameterAnnotation(AuthenticationPrincipal.class); - } - - @Override - public Object resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer, - final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) { - final HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); - final HttpSession session = Objects.requireNonNull(request).getSession(false); - final String id = String.valueOf(session.getAttribute("member")); - - return new LoginInfo(Long.valueOf(id)); - } -} diff --git a/backend/src/main/java/com/funeat/auth/util/AuthHandlerInterceptor.java b/backend/src/main/java/com/funeat/auth/util/AuthHandlerInterceptor.java deleted file mode 100644 index 48339aee9..000000000 --- a/backend/src/main/java/com/funeat/auth/util/AuthHandlerInterceptor.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.funeat.auth.util; - -import com.funeat.auth.exception.AuthErrorCode; -import com.funeat.auth.exception.AuthException.NotLoggedInException; -import java.util.Objects; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.HandlerInterceptor; - -@Component -public class AuthHandlerInterceptor implements HandlerInterceptor { - - @Override - public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, - final Object handler) { - final HttpSession session = request.getSession(false); - - if (Objects.isNull(session)) { - throw new NotLoggedInException(AuthErrorCode.LOGIN_MEMBER_NOT_FOUND); - } - - return true; - } -} diff --git a/backend/src/main/java/com/funeat/auth/util/AuthenticationPrincipal.java b/backend/src/main/java/com/funeat/auth/util/AuthenticationPrincipal.java deleted file mode 100644 index a7c0b0cdc..000000000 --- a/backend/src/main/java/com/funeat/auth/util/AuthenticationPrincipal.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.funeat.auth.util; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -public @interface AuthenticationPrincipal { -} diff --git a/backend/src/main/java/com/funeat/auth/util/KakaoPlatformUserProvider.java b/backend/src/main/java/com/funeat/auth/util/KakaoPlatformUserProvider.java deleted file mode 100644 index c1cc463ca..000000000 --- a/backend/src/main/java/com/funeat/auth/util/KakaoPlatformUserProvider.java +++ /dev/null @@ -1,142 +0,0 @@ -package com.funeat.auth.util; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.funeat.auth.dto.KakaoTokenDto; -import com.funeat.auth.dto.KakaoUserInfoDto; -import com.funeat.auth.dto.UserInfoDto; -import java.util.StringJoiner; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.context.annotation.Profile; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -@Component -@Profile("!test") -public class KakaoPlatformUserProvider implements PlatformUserProvider { - - private static final String AUTHORIZATION_BASE_URL = "https://kauth.kakao.com"; - private static final String RESOURCE_BASE_URL = "https://kapi.kakao.com"; - private static final String OAUTH_URI = "/oauth/authorize"; - private static final String ACCESS_TOKEN_URI = "/oauth/token"; - private static final String USER_INFO_URI = "/v2/user/me"; - private static final String LOGOUT_URI = "/v1/user/logout"; - private static final String AUTHORIZATION_CODE = "authorization_code"; - - private final RestTemplate restTemplate; - private final ObjectMapper objectMapper; - - @Value("${kakao.rest-api-key}") - private String kakaoRestApiKey; - - @Value("${kakao.redirect-uri}") - private String redirectUri; - - @Value("${kakao.admin-key}") - private String kakaoAdminKey; - - public KakaoPlatformUserProvider(final RestTemplateBuilder restTemplateBuilder, - final ObjectMapper objectMapper) { - this.restTemplate = restTemplateBuilder.build(); - this.objectMapper = objectMapper; - } - - @Override - public UserInfoDto getPlatformUser(final String code) { - final KakaoTokenDto accessTokenDto = findAccessToken(code); - final KakaoUserInfoDto kakaoUserInfoDto = findKakaoUserInfo(accessTokenDto.getAccessToken()); - return UserInfoDto.from(kakaoUserInfoDto); - } - - private KakaoTokenDto findAccessToken(final String code) { - final ResponseEntity response = requestAccessToken(code); - validateResponse(response, HttpStatus.OK); - return convertJsonToKakaoTokenDto(response.getBody()); - } - - private ResponseEntity requestAccessToken(final String code) { - final HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - - final MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("grant_type", AUTHORIZATION_CODE); - body.add("client_id", kakaoRestApiKey); - body.add("redirect_uri", redirectUri); - body.add("code", code); - - final HttpEntity> request = new HttpEntity<>(body, headers); - final ResponseEntity response = restTemplate.postForEntity(AUTHORIZATION_BASE_URL + ACCESS_TOKEN_URI, - request, String.class); - - return response; - } - - private void validateResponse(final ResponseEntity response, final HttpStatus status) { - if (response.getStatusCode() != status) { - throw new IllegalArgumentException(); - } - } - - private KakaoTokenDto convertJsonToKakaoTokenDto(final String responseBody) { - try { - return objectMapper.readValue(responseBody, KakaoTokenDto.class); - } catch (final JsonProcessingException e) { - throw new IllegalArgumentException(); - } - } - - private KakaoUserInfoDto findKakaoUserInfo(final String accessToken) { - final ResponseEntity response = requestKakaoUserInfo(accessToken); - validateResponse(response, HttpStatus.OK); - return convertJsonToKakaoUserDto(response.getBody()); - } - - private ResponseEntity requestKakaoUserInfo(final String accessToken) { - final HttpHeaders headers = new HttpHeaders(); - headers.setBearerAuth(accessToken); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - - final HttpEntity> request = new HttpEntity<>(null, headers); - return restTemplate.postForEntity(RESOURCE_BASE_URL + USER_INFO_URI, request, String.class); - } - - private KakaoUserInfoDto convertJsonToKakaoUserDto(final String responseBody) { - try { - return objectMapper.readValue(responseBody, KakaoUserInfoDto.class); - } catch (final JsonProcessingException e) { - throw new IllegalArgumentException(); - } - } - - @Override - public String getRedirectURI() { - final StringJoiner joiner = new StringJoiner("&") - .add("response_type=code") - .add("client_id=" + kakaoRestApiKey) - .add("redirect_uri=" + redirectUri); - - return AUTHORIZATION_BASE_URL + OAUTH_URI + "?" + joiner; - } - - @Override - public void logout(final String platformId) { - final HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.add("Authorization", "KakaoAK " + kakaoAdminKey); - - final MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("target_id_type", "user_id"); - body.add("target_id", platformId); - - final HttpEntity> request = new HttpEntity<>(body, headers); - restTemplate.postForEntity(RESOURCE_BASE_URL + LOGOUT_URI, request, String.class); - } -} diff --git a/backend/src/main/java/com/funeat/auth/util/PlatformUserProvider.java b/backend/src/main/java/com/funeat/auth/util/PlatformUserProvider.java deleted file mode 100644 index 1913b1570..000000000 --- a/backend/src/main/java/com/funeat/auth/util/PlatformUserProvider.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.funeat.auth.util; - -import com.funeat.auth.dto.UserInfoDto; - -public interface PlatformUserProvider { - - UserInfoDto getPlatformUser(final String code); - - String getRedirectURI(); - - void logout(final String platformId); -} diff --git a/backend/src/main/java/com/funeat/banner/application/BannerService.java b/backend/src/main/java/com/funeat/banner/application/BannerService.java deleted file mode 100644 index 48864670a..000000000 --- a/backend/src/main/java/com/funeat/banner/application/BannerService.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.funeat.banner.application; - -import com.funeat.banner.domain.Banner; -import com.funeat.banner.dto.BannerResponse; -import com.funeat.banner.persistence.BannerRepository; -import java.util.List; -import java.util.stream.Collectors; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@Transactional(readOnly = true) -public class BannerService { - - private final BannerRepository bannerRepository; - - public BannerService(final BannerRepository bannerRepository) { - this.bannerRepository = bannerRepository; - } - - public List getAllBanners() { - final List findBanners = bannerRepository.findAllByOrderByIdDesc(); - - return findBanners.stream() - .map(BannerResponse::toResponse) - .collect(Collectors.toList()); - } -} diff --git a/backend/src/main/java/com/funeat/banner/domain/Banner.java b/backend/src/main/java/com/funeat/banner/domain/Banner.java deleted file mode 100644 index 9ea8eabd2..000000000 --- a/backend/src/main/java/com/funeat/banner/domain/Banner.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.funeat.banner.domain; - -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; - -@Entity -public class Banner { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String link; - - private String image; - - protected Banner() { - } - - public Banner(final String link, final String image) { - this.link = link; - this.image = image; - } - - public Long getId() { - return id; - } - - public String getLink() { - return link; - } - - public String getImage() { - return image; - } -} diff --git a/backend/src/main/java/com/funeat/banner/dto/BannerResponse.java b/backend/src/main/java/com/funeat/banner/dto/BannerResponse.java deleted file mode 100644 index fcff62c2f..000000000 --- a/backend/src/main/java/com/funeat/banner/dto/BannerResponse.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.funeat.banner.dto; - -import com.funeat.banner.domain.Banner; - -public class BannerResponse { - - private final Long id; - private final String link; - private final String image; - - private BannerResponse(final Long id, final String link, final String image) { - this.id = id; - this.link = link; - this.image = image; - } - - public static BannerResponse toResponse(final Banner banner) { - return new BannerResponse(banner.getId(), banner.getLink(), banner.getImage()); - } - - public Long getId() { - return id; - } - - public String getLink() { - return link; - } - - public String getImage() { - return image; - } -} diff --git a/backend/src/main/java/com/funeat/banner/persistence/BannerRepository.java b/backend/src/main/java/com/funeat/banner/persistence/BannerRepository.java deleted file mode 100644 index 30f598755..000000000 --- a/backend/src/main/java/com/funeat/banner/persistence/BannerRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.funeat.banner.persistence; - -import com.funeat.banner.domain.Banner; -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface BannerRepository extends JpaRepository { - - List findAllByOrderByIdDesc(); -} diff --git a/backend/src/main/java/com/funeat/banner/presentation/BannerApiController.java b/backend/src/main/java/com/funeat/banner/presentation/BannerApiController.java deleted file mode 100644 index 7465189ab..000000000 --- a/backend/src/main/java/com/funeat/banner/presentation/BannerApiController.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.funeat.banner.presentation; - -import com.funeat.banner.dto.BannerResponse; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.List; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; - -@Tag(name = "08.Banner", description = "배너 관련 API 입니다.") -public interface BannerApiController { - - @Operation(summary = "배너 전체 조회", description = "배너 전체를 조회한다.") - @ApiResponse( - responseCode = "200", - description = "배너 전체 조회 성공." - ) - @GetMapping - ResponseEntity> getBanners(); -} diff --git a/backend/src/main/java/com/funeat/banner/presentation/BannerController.java b/backend/src/main/java/com/funeat/banner/presentation/BannerController.java deleted file mode 100644 index 164df97cd..000000000 --- a/backend/src/main/java/com/funeat/banner/presentation/BannerController.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.funeat.banner.presentation; - -import com.funeat.banner.application.BannerService; -import com.funeat.banner.dto.BannerResponse; -import java.util.List; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class BannerController implements BannerApiController { - - private final BannerService bannerService; - - public BannerController(final BannerService bannerService) { - this.bannerService = bannerService; - } - - @GetMapping("/api/banners") - public ResponseEntity> getBanners() { - final List responses = bannerService.getAllBanners(); - - return ResponseEntity.ok(responses); - } -} diff --git a/backend/src/main/java/com/funeat/comment/domain/Comment.java b/backend/src/main/java/com/funeat/comment/domain/Comment.java deleted file mode 100644 index 4e6798b9d..000000000 --- a/backend/src/main/java/com/funeat/comment/domain/Comment.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.funeat.comment.domain; - -import com.funeat.member.domain.Member; -import com.funeat.recipe.domain.Recipe; -import java.time.LocalDateTime; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; - -@Entity -public class Comment { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String comment; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "recipe_id") - private Recipe recipe; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") - private Member member; - - @Column(nullable = false) - private LocalDateTime createdAt = LocalDateTime.now(); - - protected Comment() { - } - - public Comment(final Recipe recipe, final Member member, final String comment) { - this.recipe = recipe; - this.member = member; - this.comment = comment; - } - - public Long getId() { - return id; - } - - public String getComment() { - return comment; - } - - public Member getMember() { - return member; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } -} diff --git a/backend/src/main/java/com/funeat/comment/persistence/CommentRepository.java b/backend/src/main/java/com/funeat/comment/persistence/CommentRepository.java deleted file mode 100644 index e40a47f67..000000000 --- a/backend/src/main/java/com/funeat/comment/persistence/CommentRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.funeat.comment.persistence; - -import com.funeat.comment.domain.Comment; -import com.funeat.common.repository.BaseRepository; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface CommentRepository extends JpaRepository, BaseRepository { -} diff --git a/backend/src/main/java/com/funeat/comment/specification/CommentSpecification.java b/backend/src/main/java/com/funeat/comment/specification/CommentSpecification.java deleted file mode 100644 index db6c734bb..000000000 --- a/backend/src/main/java/com/funeat/comment/specification/CommentSpecification.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.funeat.comment.specification; - -import com.funeat.comment.domain.Comment; -import com.funeat.recipe.domain.Recipe; -import java.util.List; -import java.util.Objects; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Path; -import org.springframework.data.jpa.domain.Specification; - -public class CommentSpecification { - - private CommentSpecification() { - } - - private static final List> COUNT_RESULT_TYPES = List.of(Long.class, long.class); - - public static Specification findAllByRecipe(final Recipe recipe, final Long lastCommentId) { - return (root, query, criteriaBuilder) -> { - if (!COUNT_RESULT_TYPES.contains(query.getResultType())) { - root.fetch("member", JoinType.LEFT); - } - - criteriaBuilder.desc(root.get("id")); - - return Specification - .where(lessThan(lastCommentId)) - .and(equalToRecipe(recipe)) - .toPredicate(root, query, criteriaBuilder); - }; - } - - private static Specification lessThan(final Long commentId) { - return (root, query, criteriaBuilder) -> { - if (Objects.isNull(commentId)) { - return null; - } - - final Path commentIdPath = root.get("id"); - - return criteriaBuilder.lessThan(commentIdPath, commentId); - }; - } - - private static Specification equalToRecipe(final Recipe recipe) { - return (root, query, criteriaBuilder) -> { - if (Objects.isNull(recipe)) { - return null; - } - - final Path recipePath = root.get("recipe"); - - return criteriaBuilder.equal(recipePath, recipe); - }; - } -} diff --git a/backend/src/main/java/com/funeat/common/CustomPageableHandlerMethodArgumentResolver.java b/backend/src/main/java/com/funeat/common/CustomPageableHandlerMethodArgumentResolver.java deleted file mode 100644 index acb481f97..000000000 --- a/backend/src/main/java/com/funeat/common/CustomPageableHandlerMethodArgumentResolver.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.funeat.common; - -import org.springframework.core.MethodParameter; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.web.PageableHandlerMethodArgumentResolver; -import org.springframework.stereotype.Component; -import org.springframework.web.bind.support.WebDataBinderFactory; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.method.support.ModelAndViewContainer; - -@Component -public class CustomPageableHandlerMethodArgumentResolver extends PageableHandlerMethodArgumentResolver { - - @Override - public Pageable resolveArgument(final MethodParameter methodParameter, final ModelAndViewContainer mavContainer, - final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) { - final Pageable pageable = super.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory); - - final Sort lastPrioritySort = Sort.by("id").descending(); - - return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), - pageable.getSort().and(lastPrioritySort)); - } -} diff --git a/backend/src/main/java/com/funeat/common/ImageUploader.java b/backend/src/main/java/com/funeat/common/ImageUploader.java deleted file mode 100644 index afd4b5c10..000000000 --- a/backend/src/main/java/com/funeat/common/ImageUploader.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.funeat.common; - -import org.springframework.web.multipart.MultipartFile; - -public interface ImageUploader { - - String upload(final MultipartFile image); - - void delete(final String fileName); -} diff --git a/backend/src/main/java/com/funeat/common/OpenApiConfig.java b/backend/src/main/java/com/funeat/common/OpenApiConfig.java deleted file mode 100644 index 51c42429d..000000000 --- a/backend/src/main/java/com/funeat/common/OpenApiConfig.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.funeat.common; - -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.info.Info; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.context.annotation.Configuration; - -@OpenAPIDefinition( - info = @Info( - title = "펀잇 API 명세서", - description = "펀잇팀 API 명세서입니다.", - version = "v1" - ), - tags = { - @Tag(name = "01.Product", description = "상품 기능"), - @Tag(name = "02.Category", description = "카테고리 기능"), - @Tag(name = "03.Review", description = "리뷰 기능"), - @Tag(name = "04.Tag", description = "태그 기능"), - @Tag(name = "05.Member", description = "사용자 기능"), - @Tag(name = "06.Login", description = "로그인 기능"), - @Tag(name = "07.Recipe", description = "꿀조합 기능"), - @Tag(name = "08.Banner", description = "배너 기능"), - } -) -@Configuration -public class OpenApiConfig { -} diff --git a/backend/src/main/java/com/funeat/common/StringToCategoryTypeConverter.java b/backend/src/main/java/com/funeat/common/StringToCategoryTypeConverter.java deleted file mode 100644 index 8873f0a7b..000000000 --- a/backend/src/main/java/com/funeat/common/StringToCategoryTypeConverter.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.funeat.common; - -import com.funeat.product.domain.CategoryType; -import org.springframework.core.convert.converter.Converter; - -public class StringToCategoryTypeConverter implements Converter { - - @Override - public CategoryType convert(final String source) { - return CategoryType.findCategoryType(source); - } -} diff --git a/backend/src/main/java/com/funeat/common/WebConfig.java b/backend/src/main/java/com/funeat/common/WebConfig.java deleted file mode 100644 index c6a0b0e7a..000000000 --- a/backend/src/main/java/com/funeat/common/WebConfig.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.funeat.common; - -import com.funeat.admin.util.AdminCheckInterceptor; -import com.funeat.auth.util.AuthArgumentResolver; -import com.funeat.auth.util.AuthHandlerInterceptor; -import com.funeat.recipe.util.RecipeDetailHandlerInterceptor; -import com.funeat.recipe.util.RecipeHandlerInterceptor; -import java.util.List; -import org.springframework.context.annotation.Configuration; -import org.springframework.format.FormatterRegistry; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class WebConfig implements WebMvcConfigurer { - - private final CustomPageableHandlerMethodArgumentResolver customPageableHandlerMethodArgumentResolver; - private final AuthArgumentResolver authArgumentResolver; - private final AuthHandlerInterceptor authHandlerInterceptor; - private final RecipeHandlerInterceptor recipeHandlerInterceptor; - private final RecipeDetailHandlerInterceptor recipeDetailHandlerInterceptor; - - private final AdminCheckInterceptor adminCheckInterceptor; - - public WebConfig(final CustomPageableHandlerMethodArgumentResolver customPageableHandlerMethodArgumentResolver, - final AuthArgumentResolver authArgumentResolver, - final AuthHandlerInterceptor authHandlerInterceptor, - final RecipeHandlerInterceptor recipeHandlerInterceptor, - final RecipeDetailHandlerInterceptor recipeDetailHandlerInterceptor, - final AdminCheckInterceptor adminCheckInterceptor) { - this.customPageableHandlerMethodArgumentResolver = customPageableHandlerMethodArgumentResolver; - this.authArgumentResolver = authArgumentResolver; - this.authHandlerInterceptor = authHandlerInterceptor; - this.recipeHandlerInterceptor = recipeHandlerInterceptor; - this.recipeDetailHandlerInterceptor = recipeDetailHandlerInterceptor; - this.adminCheckInterceptor = adminCheckInterceptor; - } - - @Override - public void addInterceptors(final InterceptorRegistry registry) { - registry.addInterceptor(authHandlerInterceptor) - .addPathPatterns("/api/products/**/reviews/**") - .addPathPatterns("/api/members/**") - .addPathPatterns("/api/logout"); - registry.addInterceptor(recipeHandlerInterceptor) - .addPathPatterns("/api/recipes"); - registry.addInterceptor(recipeDetailHandlerInterceptor) - .addPathPatterns("/api/recipes/**") - .excludePathPatterns("/api/recipes"); - registry.addInterceptor(adminCheckInterceptor) - .excludePathPatterns("/api/admin/login") - .addPathPatterns("/api/admin/**"); - } - - @Override - public void addFormatters(final FormatterRegistry registry) { - registry.addConverter(new StringToCategoryTypeConverter()); - } - - @Override - public void addArgumentResolvers(final List resolvers) { - resolvers.add(customPageableHandlerMethodArgumentResolver); - resolvers.add(authArgumentResolver); - } -} diff --git a/backend/src/main/java/com/funeat/common/dto/PageDto.java b/backend/src/main/java/com/funeat/common/dto/PageDto.java deleted file mode 100644 index 6d561c7cd..000000000 --- a/backend/src/main/java/com/funeat/common/dto/PageDto.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.funeat.common.dto; - -import org.springframework.data.domain.Page; - -public class PageDto { - - private final Long totalDataCount; - private final Long totalPages; - private final boolean firstPage; - private final boolean lastPage; - private final Long requestPage; - private final Long requestSize; - - public PageDto(final Long totalDataCount, final Long totalPages, final boolean firstPage, final boolean lastPage, - final Long requestPage, final Long requestSize) { - this.totalDataCount = totalDataCount; - this.totalPages = totalPages; - this.firstPage = firstPage; - this.lastPage = lastPage; - this.requestPage = requestPage; - this.requestSize = requestSize; - } - - public static PageDto toDto(final Page page) { - return new PageDto( - page.getTotalElements(), - Long.valueOf(page.getTotalPages()), - page.isFirst(), - page.isLast(), - Long.valueOf(page.getNumber()), - Long.valueOf(page.getSize()) - ); - } - - public Long getTotalDataCount() { - return totalDataCount; - } - - public Long getTotalPages() { - return totalPages; - } - - public boolean isFirstPage() { - return firstPage; - } - - public boolean isLastPage() { - return lastPage; - } - - public Long getRequestPage() { - return requestPage; - } - - public Long getRequestSize() { - return requestSize; - } -} diff --git a/backend/src/main/java/com/funeat/common/exception/CommonException.java b/backend/src/main/java/com/funeat/common/exception/CommonException.java deleted file mode 100644 index 55be12d5d..000000000 --- a/backend/src/main/java/com/funeat/common/exception/CommonException.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.funeat.common.exception; - -import com.funeat.exception.CommonErrorCode; -import com.funeat.exception.ErrorCode; -import com.funeat.exception.GlobalException; -import org.springframework.http.HttpStatus; - -public class CommonException extends GlobalException { - - public CommonException(final HttpStatus status, final ErrorCode errorCode) { - super(status, errorCode); - } - - public static class NotAllowedFileExtensionException extends CommonException { - public NotAllowedFileExtensionException(final CommonErrorCode errorCode, final String extension) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), extension)); - } - } - - public static class S3UploadFailException extends CommonException { - public S3UploadFailException(final CommonErrorCode errorCode) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage())); - } - } - - public static class S3DeleteFailException extends CommonException { - public S3DeleteFailException(final CommonErrorCode errorCode) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage())); - } - } -} diff --git a/backend/src/main/java/com/funeat/common/logging/Logging.java b/backend/src/main/java/com/funeat/common/logging/Logging.java deleted file mode 100644 index 475a43b4b..000000000 --- a/backend/src/main/java/com/funeat/common/logging/Logging.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.funeat.common.logging; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface Logging { -} diff --git a/backend/src/main/java/com/funeat/common/logging/LoggingAspect.java b/backend/src/main/java/com/funeat/common/logging/LoggingAspect.java deleted file mode 100644 index 13758c6c9..000000000 --- a/backend/src/main/java/com/funeat/common/logging/LoggingAspect.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.funeat.common.logging; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.AfterReturning; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.CodeSignature; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -@Aspect -@Component -public class LoggingAspect { - - private static final List excludeNames = Arrays.asList("image", "images", "request"); - - private final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()); - private final Logger log = LoggerFactory.getLogger(this.getClass()); - - @Pointcut("execution(public * com.funeat.*.presentation.*.*(..))") - private void allPresentation() { - } - - @Pointcut("@annotation(com.funeat.common.logging.Logging)") - private void logging() { - } - - @Before("allPresentation() && logging()") - public void requestLogging(final JoinPoint joinPoint) { - final HttpServletRequest request = getRequest(); - final Map args = getSpecificParameters(joinPoint); - - printRequestLog(request, args); - } - - private HttpServletRequest getRequest() { - final ServletRequestAttributes servletRequestAttributes - = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); - return servletRequestAttributes.getRequest(); - } - - private Map getSpecificParameters(final JoinPoint joinPoint) { - final CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature(); - final String[] parameterNames = codeSignature.getParameterNames(); - final Object[] args = joinPoint.getArgs(); - - final Map params = new HashMap<>(); - for (int i = 0; i < parameterNames.length; i++) { - if (!excludeNames.contains(parameterNames[i])) { - params.put(parameterNames[i], args[i]); - } - } - - return params; - } - - private void printRequestLog(final HttpServletRequest request, final Object value) { - try { - log.info("[REQUEST {}] [PATH {}] {}", - request.getMethod(), request.getRequestURI(), objectMapper.writeValueAsString(value)); - } catch (final JsonProcessingException e) { - log.warn("[LOGGING ERROR] Request 로깅에 실패했습니다"); - } - } - - @AfterReturning(value = "allPresentation() && logging()", returning = "responseEntity") - public void requestLogging(final ResponseEntity responseEntity) { - printResponseLog(responseEntity); - } - - private void printResponseLog(final ResponseEntity responseEntity) { - try { - final String responseStatus = responseEntity.getStatusCode().toString(); - log.info("[RESPONSE {}] {}", responseStatus, objectMapper.writeValueAsString(responseEntity.getBody())); - } catch (final JsonProcessingException e) { - log.warn("[LOGGING ERROR] Response 로깅에 실패했습니다"); - } - } -} diff --git a/backend/src/main/java/com/funeat/common/repository/BaseRepository.java b/backend/src/main/java/com/funeat/common/repository/BaseRepository.java deleted file mode 100644 index 448db7766..000000000 --- a/backend/src/main/java/com/funeat/common/repository/BaseRepository.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.funeat.common.repository; - -import java.io.Serializable; -import java.util.List; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.repository.NoRepositoryBean; - -@NoRepositoryBean -public interface BaseRepository extends JpaRepository { - - Page findAllForPagination(final Specification spec, final Pageable pageable, final Long totalElements); - - List findAllWithSpecification(final Specification spec, final int pageSize); -} diff --git a/backend/src/main/java/com/funeat/common/repository/BaseRepositoryImpl.java b/backend/src/main/java/com/funeat/common/repository/BaseRepositoryImpl.java deleted file mode 100644 index 64cd508f6..000000000 --- a/backend/src/main/java/com/funeat/common/repository/BaseRepositoryImpl.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.funeat.common.repository; - -import java.io.Serializable; -import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.TypedQuery; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; - -import org.springframework.data.jpa.domain.Specification; -import org.springframework.data.jpa.repository.support.JpaEntityInformation; -import org.springframework.data.jpa.repository.support.SimpleJpaRepository; - -public class BaseRepositoryImpl extends SimpleJpaRepository - implements BaseRepository { - - public BaseRepositoryImpl(final JpaEntityInformation entityInformation, final EntityManager entityManager) { - super(entityInformation, entityManager); - } - - @Override - public Page findAllForPagination(final Specification spec, final Pageable pageable, - final Long totalElements) { - final TypedQuery query = getQuery(spec, pageable.getSort()); - - final int pageSize = pageable.getPageSize(); - - if (totalElements == null) { - return findAll(spec, pageable); - } - - if (pageSize < 1) { - throw new IllegalArgumentException("페이지는 1미만이 될 수 없습니다."); - } - - query.setMaxResults(pageable.getPageSize()); - - return new PageImpl<>(query.getResultList(), PageRequest.of(0, pageSize), totalElements); - } - - @Override - public List findAllWithSpecification(final Specification spec, final int pageSize) { - final TypedQuery query = getQuery(spec, Sort.unsorted()); - query.setMaxResults(pageSize); - - return query.getResultList(); - } -} diff --git a/backend/src/main/java/com/funeat/common/s3/AwsConfig.java b/backend/src/main/java/com/funeat/common/s3/AwsConfig.java deleted file mode 100644 index 83a7e67d5..000000000 --- a/backend/src/main/java/com/funeat/common/s3/AwsConfig.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.funeat.common.s3; - -import com.amazonaws.auth.InstanceProfileCredentialsProvider; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class AwsConfig { - - @Value("${cloud.aws.region.static}") - private String region; - - @Bean - public InstanceProfileCredentialsProvider awsCredentialsProvider() { - return InstanceProfileCredentialsProvider.getInstance(); - } - - @Bean - public AmazonS3 amazonS3Client() { - return AmazonS3ClientBuilder.standard() - .withRegion(region) - .withCredentials(awsCredentialsProvider()) - .build(); - } -} diff --git a/backend/src/main/java/com/funeat/common/s3/S3Uploader.java b/backend/src/main/java/com/funeat/common/s3/S3Uploader.java deleted file mode 100644 index 97e6241b7..000000000 --- a/backend/src/main/java/com/funeat/common/s3/S3Uploader.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.funeat.common.s3; - -import static com.funeat.exception.CommonErrorCode.IMAGE_EXTENSION_ERROR_CODE; -import static com.funeat.exception.CommonErrorCode.UNKNOWN_SERVER_ERROR_CODE; - -import com.amazonaws.AmazonServiceException; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.funeat.common.ImageUploader; -import com.funeat.common.exception.CommonException.NotAllowedFileExtensionException; -import com.funeat.common.exception.CommonException.S3DeleteFailException; -import com.funeat.common.exception.CommonException.S3UploadFailException; -import java.io.IOException; -import java.util.List; -import java.util.UUID; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; - -@Component -@Profile("!test") -public class S3Uploader implements ImageUploader { - - private static final int BEGIN_FILE_NAME_INDEX_WITHOUT_CLOUDFRONT_PATH = 31; - private static final List INCLUDE_EXTENSIONS = List.of("image/jpeg", "image/png", "image/webp"); - - private final Logger log = LoggerFactory.getLogger(this.getClass()); - - @Value("${cloud.aws.s3.bucket}") - private String bucket; - - @Value("${cloud.aws.s3.folder}") - private String folder; - - @Value("${cloud.aws.s3.cloudfrontPath}") - private String cloudfrontPath; - - private final AmazonS3 amazonS3; - - public S3Uploader(final AmazonS3 amazonS3) { - this.amazonS3 = amazonS3; - } - - @Override - public String upload(final MultipartFile image) { - validateExtension(image); - final String randomImageName = getRandomImageName(image); - final ObjectMetadata metadata = getMetadata(image); - try { - final String key = folder + randomImageName; - amazonS3.putObject(getPutObjectRequest(image, key, metadata)); - - return getCloudfrontImagePath(randomImageName); - } catch (IOException e) { - throw new S3UploadFailException(UNKNOWN_SERVER_ERROR_CODE); - } - } - - @Override - public void delete(final String image) { - final String imageName = image.substring(BEGIN_FILE_NAME_INDEX_WITHOUT_CLOUDFRONT_PATH); - try { - final String key = folder + imageName; - amazonS3.deleteObject(bucket, key); - } catch (final AmazonServiceException e) { - log.error("S3 이미지 삭제에 실패했습니다. 이미지 경로 : {}", image); - throw new S3DeleteFailException(UNKNOWN_SERVER_ERROR_CODE); - } - } - - private void validateExtension(final MultipartFile image) { - final String contentType = image.getContentType(); - if (!INCLUDE_EXTENSIONS.contains(contentType)) { - throw new NotAllowedFileExtensionException(IMAGE_EXTENSION_ERROR_CODE, contentType); - } - } - - private String getRandomImageName(final MultipartFile image) { - return UUID.randomUUID() + image.getOriginalFilename(); - } - - private ObjectMetadata getMetadata(final MultipartFile image) { - final ObjectMetadata metadata = new ObjectMetadata(); - metadata.setContentType(image.getContentType()); - metadata.setContentLength(image.getSize()); - return metadata; - } - - private PutObjectRequest getPutObjectRequest(final MultipartFile image, final String key, - final ObjectMetadata metadata) throws IOException { - return new PutObjectRequest(bucket, key, image.getInputStream(), metadata); - } - - private String getCloudfrontImagePath(final String imageName) { - return cloudfrontPath + imageName; - } -} diff --git a/backend/src/main/java/com/funeat/exception/CommonErrorCode.java b/backend/src/main/java/com/funeat/exception/CommonErrorCode.java deleted file mode 100644 index 9bc4a5486..000000000 --- a/backend/src/main/java/com/funeat/exception/CommonErrorCode.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.funeat.exception; - -import org.springframework.http.HttpStatus; - -public enum CommonErrorCode { - - UNKNOWN_SERVER_ERROR_CODE(HttpStatus.INTERNAL_SERVER_ERROR, "알 수 없는 에러입니다.", "0000"), - REQUEST_VALID_ERROR_CODE(HttpStatus.BAD_REQUEST, "요청을 다시 확인해주세요.", "0001"), - IMAGE_EXTENSION_ERROR_CODE(HttpStatus.BAD_REQUEST, "파일 확장자를 확인해주세요.", "0002"), - ; - - private final HttpStatus status; - private final String message; - private final String code; - - CommonErrorCode(final HttpStatus status, final String message, final String code) { - this.status = status; - this.message = message; - this.code = code; - } - - public HttpStatus getStatus() { - return status; - } - - public String getMessage() { - return message; - } - - public String getCode() { - return code; - } -} diff --git a/backend/src/main/java/com/funeat/exception/ErrorCode.java b/backend/src/main/java/com/funeat/exception/ErrorCode.java deleted file mode 100644 index ee454207e..000000000 --- a/backend/src/main/java/com/funeat/exception/ErrorCode.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.funeat.exception; - -import com.fasterxml.jackson.annotation.JsonInclude; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class ErrorCode { - - private final String code; - private final String message; - private T info; - - public ErrorCode(final String code, final String message, final T info) { - this.code = code; - this.message = message; - this.info = info; - } - - public ErrorCode(final String code, final String message) { - this.code = code; - this.message = message; - } - - public String getCode() { - return code; - } - - public String getMessage() { - return message; - } - - public T getInfo() { - return info; - } -} diff --git a/backend/src/main/java/com/funeat/exception/GlobalException.java b/backend/src/main/java/com/funeat/exception/GlobalException.java deleted file mode 100644 index 976619a36..000000000 --- a/backend/src/main/java/com/funeat/exception/GlobalException.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.funeat.exception; - -import org.springframework.http.HttpStatus; - -public class GlobalException extends RuntimeException { - - private final HttpStatus status; - private final ErrorCode errorCode; - - public GlobalException(final HttpStatus status, final ErrorCode errorCode) { - super(errorCode.getMessage()); - this.status = status; - this.errorCode = errorCode; - } - - public HttpStatus getStatus() { - return status; - } - - public ErrorCode getErrorCode() { - return errorCode; - } -} diff --git a/backend/src/main/java/com/funeat/exception/presentation/GlobalControllerAdvice.java b/backend/src/main/java/com/funeat/exception/presentation/GlobalControllerAdvice.java deleted file mode 100644 index b6fed678c..000000000 --- a/backend/src/main/java/com/funeat/exception/presentation/GlobalControllerAdvice.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.funeat.exception.presentation; - -import static com.funeat.exception.CommonErrorCode.REQUEST_VALID_ERROR_CODE; -import static com.funeat.exception.CommonErrorCode.UNKNOWN_SERVER_ERROR_CODE; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.funeat.exception.ErrorCode; -import com.funeat.exception.GlobalException; -import java.util.stream.Collectors; -import javax.servlet.http.HttpServletRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.support.DefaultMessageSourceResolvable; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.validation.FieldError; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.MissingServletRequestParameterException; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; - -@RestControllerAdvice -public class GlobalControllerAdvice { - - private static final String ERROR_MESSAGE_DELIMITER = ", "; - private static final String RESPONSE_DELIMITER = ". "; - - private final Logger log = LoggerFactory.getLogger(this.getClass()); - private final ObjectMapper objectMapper; - - public GlobalControllerAdvice(final ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - } - - @ExceptionHandler({MethodArgumentTypeMismatchException.class, MissingServletRequestParameterException.class}) - public ResponseEntity handleParamValidationException(final Exception e, final HttpServletRequest request) { - log.warn("{} = {}, code = {} message = {}", request.getMethod(), request.getRequestURI(), - REQUEST_VALID_ERROR_CODE.getCode(), e.getMessage()); - - final ErrorCode errorCode = new ErrorCode<>(REQUEST_VALID_ERROR_CODE.getCode(), - REQUEST_VALID_ERROR_CODE.getMessage()); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorCode); - } - - @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity handleParamValidationException(final MethodArgumentNotValidException e, - final HttpServletRequest request) { - final String filedErrorLogMessages = getMethodArgumentExceptionLogMessage(e); - - final String errorMessage = e.getBindingResult() - .getAllErrors() - .stream() - .map(DefaultMessageSourceResolvable::getDefaultMessage) - .collect(Collectors.joining(ERROR_MESSAGE_DELIMITER)); - - final String responseErrorMessage = errorMessage + RESPONSE_DELIMITER + REQUEST_VALID_ERROR_CODE.getMessage(); - - final ErrorCode errorCode = new ErrorCode<>(REQUEST_VALID_ERROR_CODE.getCode(), responseErrorMessage); - - log.warn("{} = {}, message = {} ", request.getMethod(), request.getRequestURI(), - filedErrorLogMessages); - return ResponseEntity.status(REQUEST_VALID_ERROR_CODE.getStatus()).body(errorCode); - } - - private static String getMethodArgumentExceptionLogMessage(final MethodArgumentNotValidException e) { - final String filedErrorMessages = e.getBindingResult() - .getFieldErrors() - .stream() - .map(FieldError::getField) - .collect(Collectors.joining(ERROR_MESSAGE_DELIMITER)); - - return filedErrorMessages + " 요청 실패"; - } - - @ExceptionHandler(GlobalException.class) - public ResponseEntity handleGlobalException(final GlobalException e, final HttpServletRequest request) - throws JsonProcessingException { - final String exceptionSource = getExceptionSource(e); - log.warn("source = {} , {} = {} code = {} message = {} info = {}", exceptionSource, request.getMethod(), - request.getRequestURI(), e.getErrorCode().getCode(), e.getErrorCode().getMessage(), - objectMapper.writeValueAsString(e.getErrorCode().getInfo())); - - final ErrorCode errorCode = new ErrorCode<>(e.getErrorCode().getCode(), e.getMessage()); - return ResponseEntity.status(e.getStatus()).body(errorCode); - } - - private String getExceptionSource(final Exception e) { - final StackTraceElement[] stackTrace = e.getStackTrace(); - if (stackTrace.length > 0) { - return stackTrace[0].toString(); - } - return "Unknown location"; - } - - @ExceptionHandler(Exception.class) - public ResponseEntity handleServerException(final Exception e) { - log.error("", e); - - final ErrorCode errorCode = new ErrorCode<>(UNKNOWN_SERVER_ERROR_CODE.getCode(), - UNKNOWN_SERVER_ERROR_CODE.getMessage()); - return ResponseEntity.status(UNKNOWN_SERVER_ERROR_CODE.getStatus()).body(errorCode); - } -} diff --git a/backend/src/main/java/com/funeat/member/application/MemberService.java b/backend/src/main/java/com/funeat/member/application/MemberService.java deleted file mode 100644 index 43e1bf069..000000000 --- a/backend/src/main/java/com/funeat/member/application/MemberService.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.funeat.member.application; - -import static com.funeat.member.exception.MemberErrorCode.MEMBER_NOT_FOUND; -import static org.springframework.transaction.annotation.Propagation.REQUIRES_NEW; - -import com.funeat.auth.dto.SignUserDto; -import com.funeat.auth.dto.UserInfoDto; -import com.funeat.common.ImageUploader; -import com.funeat.member.domain.Member; -import com.funeat.member.dto.MemberProfileResponse; -import com.funeat.member.dto.MemberRequest; -import com.funeat.member.exception.MemberErrorCode; -import com.funeat.member.exception.MemberException.MemberNotFoundException; -import com.funeat.member.persistence.MemberRepository; -import java.util.Objects; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; - -@Service -@Transactional(readOnly = true) -public class MemberService { - - private final MemberRepository memberRepository; - private final ImageUploader imageUploader; - - public MemberService(final MemberRepository memberRepository, final ImageUploader imageUploader) { - this.memberRepository = memberRepository; - this.imageUploader = imageUploader; - } - - @Transactional(propagation = REQUIRES_NEW) - public SignUserDto findOrCreateMember(final UserInfoDto userInfoDto) { - final String platformId = userInfoDto.getId().toString(); - - return memberRepository.findByPlatformId(platformId) - .map(member -> SignUserDto.of(false, member)) - .orElseGet(() -> save(userInfoDto)); - } - - private SignUserDto save(final UserInfoDto userInfoDto) { - final Member member = userInfoDto.toMember(); - memberRepository.save(member); - - return SignUserDto.of(true, member); - } - - public MemberProfileResponse getMemberProfile(final Long memberId) { - final Member findMember = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MemberErrorCode.MEMBER_NOT_FOUND, memberId)); - - return MemberProfileResponse.toResponse(findMember); - } - - @Transactional - public void modify(final Long memberId, final MultipartFile image, final MemberRequest request) { - final Member findMember = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MemberErrorCode.MEMBER_NOT_FOUND, memberId)); - - final String nickname = request.getNickname(); - - if (Objects.isNull(image)) { - findMember.modifyNickname(nickname); - return; - } - final String imageUrl = imageUploader.upload(image); - findMember.modifyProfile(nickname, imageUrl); - } - - public String findPlatformId(final Long memberId) { - final Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId)); - - return member.getPlatformId(); - } -} diff --git a/backend/src/main/java/com/funeat/member/domain/Member.java b/backend/src/main/java/com/funeat/member/domain/Member.java deleted file mode 100644 index 2096d0ff6..000000000 --- a/backend/src/main/java/com/funeat/member/domain/Member.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.funeat.member.domain; - -import static com.funeat.member.exception.MemberErrorCode.MEMBER_UPDATE_ERROR; - -import com.funeat.member.domain.favorite.RecipeFavorite; -import com.funeat.member.domain.favorite.ReviewFavorite; -import com.funeat.member.exception.MemberException.MemberUpdateException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.OneToMany; -import org.springframework.util.StringUtils; - -@Entity -public class Member { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String nickname; - - private String profileImage; - - private String platformId; - - @OneToMany(mappedBy = "member") - private List reviewFavorites = new ArrayList<>(); - - @OneToMany(mappedBy = "member") - private List recipeFavorites; - - protected Member() { - } - - public Member(final String nickname, final String profileImage, final String platformId) { - this.nickname = nickname; - this.profileImage = profileImage; - this.platformId = platformId; - } - - public Long getId() { - return id; - } - - public String getNickname() { - return nickname; - } - - public String getProfileImage() { - return profileImage; - } - - public String getPlatformId() { - return platformId; - } - - public List getReviewFavorites() { - return reviewFavorites; - } - - public List getRecipeFavorites() { - return recipeFavorites; - } - - public void modifyProfile(final String nickname, final String profileImage) { - if (!StringUtils.hasText(nickname) || Objects.isNull(profileImage)) { - throw new MemberUpdateException(MEMBER_UPDATE_ERROR); - } - this.nickname = nickname; - this.profileImage = profileImage; - } - - public void modifyNickname(final String nickname) { - if (!StringUtils.hasText(nickname)) { - throw new MemberUpdateException(MEMBER_UPDATE_ERROR); - } - this.nickname = nickname; - } -} diff --git a/backend/src/main/java/com/funeat/member/domain/favorite/RecipeFavorite.java b/backend/src/main/java/com/funeat/member/domain/favorite/RecipeFavorite.java deleted file mode 100644 index 161f83483..000000000 --- a/backend/src/main/java/com/funeat/member/domain/favorite/RecipeFavorite.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.funeat.member.domain.favorite; - -import com.funeat.member.domain.Member; -import com.funeat.recipe.domain.Recipe; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; - -@Entity -@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"member_id", "recipe_id"})) -public class RecipeFavorite { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") - private Member member; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "recipe_id") - private Recipe recipe; - - private Boolean favorite; - - protected RecipeFavorite() { - } - - public RecipeFavorite(final Member member, final Recipe recipe, final Boolean favorite) { - this.member = member; - this.recipe = recipe; - this.favorite = favorite; - } - - public static RecipeFavorite create(final Member member, final Recipe recipe, final Boolean favorite) { - if (favorite == true) { - recipe.addFavoriteCount(); - } - return new RecipeFavorite(member, recipe, favorite); - } - - public void updateFavorite(final Boolean favorite) { - if (!this.favorite && favorite) { - this.recipe.addFavoriteCount(); - this.favorite = favorite; - return; - } - if (this.favorite && !favorite) { - this.recipe.minusFavoriteCount(); - this.favorite = favorite; - } - } - - public Long getId() { - return id; - } - - public Member getMember() { - return member; - } - - public Recipe getRecipe() { - return recipe; - } - - public Boolean getFavorite() { - return favorite; - } -} diff --git a/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java b/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java deleted file mode 100644 index 7e8768f47..000000000 --- a/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.funeat.member.domain.favorite; - -import com.funeat.member.domain.Member; -import com.funeat.review.domain.Review; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; - -@Entity -@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"member_id", "review_id"})) -public class ReviewFavorite { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") - private Member member; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "review_id") - private Review review; - - private Boolean favorite; - - protected ReviewFavorite() { - } - - public ReviewFavorite(final Member member, final Review review) { - this.member = member; - this.review = review; - } - - public static ReviewFavorite create(final Member member, final Review review, final Boolean favorite) { - final ReviewFavorite reviewFavorite = new ReviewFavorite(member, review); - reviewFavorite.review.getReviewFavorites().add(reviewFavorite); - reviewFavorite.member.getReviewFavorites().add(reviewFavorite); - reviewFavorite.favorite = favorite; - reviewFavorite.review.addFavoriteCount(); - return reviewFavorite; - } - - public void updateChecked(final Boolean favorite) { - if (!this.favorite && favorite) { - this.review.addFavoriteCount(); - this.favorite = favorite; - return; - } - if (this.favorite && !favorite) { - this.review.minusFavoriteCount(); - this.favorite = favorite; - } - } - - public Long getId() { - return id; - } - - public Member getMember() { - return member; - } - - public Review getReview() { - return review; - } - - public Boolean getFavorite() { - return favorite; - } -} diff --git a/backend/src/main/java/com/funeat/member/dto/MemberProfileResponse.java b/backend/src/main/java/com/funeat/member/dto/MemberProfileResponse.java deleted file mode 100644 index 84e915ebe..000000000 --- a/backend/src/main/java/com/funeat/member/dto/MemberProfileResponse.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.funeat.member.dto; - -import com.funeat.member.domain.Member; - -public class MemberProfileResponse { - - private final String nickname; - private final String profileImage; - - public MemberProfileResponse(final String nickname, final String profileImage) { - this.nickname = nickname; - this.profileImage = profileImage; - } - - public static MemberProfileResponse toResponse(final Member member) { - return new MemberProfileResponse(member.getNickname(), member.getProfileImage()); - } - - public String getNickname() { - return nickname; - } - - public String getProfileImage() { - return profileImage; - } -} diff --git a/backend/src/main/java/com/funeat/member/dto/MemberRecipeDto.java b/backend/src/main/java/com/funeat/member/dto/MemberRecipeDto.java deleted file mode 100644 index 4c01ce95d..000000000 --- a/backend/src/main/java/com/funeat/member/dto/MemberRecipeDto.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.funeat.member.dto; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.funeat.recipe.domain.Recipe; -import com.funeat.recipe.domain.RecipeImage; -import java.time.LocalDateTime; -import java.util.List; - -public class MemberRecipeDto { - - private final Long id; - private final String title; - private final String content; - private final LocalDateTime createdAt; - private final String image; - private final Long favoriteCount; - private final List products; - - private MemberRecipeDto(final Long id, final String title, final String content, final LocalDateTime createdAt, - final Long favoriteCount, final List products) { - this(id, title, content, createdAt, null, favoriteCount, products); - } - - @JsonCreator - private MemberRecipeDto(final Long id, final String title, final String content, final LocalDateTime createdAt, - final String image, final Long favoriteCount, final List products) { - this.id = id; - this.title = title; - this.content = content; - this.createdAt = createdAt; - this.image = image; - this.favoriteCount = favoriteCount; - this.products = products; - } - - public static MemberRecipeDto toDto(final Recipe recipe, final List findRecipeImages, - final List memberRecipeProductDtos) { - if (findRecipeImages.isEmpty()) { - return new MemberRecipeDto(recipe.getId(), recipe.getTitle(), recipe.getContent(), recipe.getCreatedAt(), - recipe.getFavoriteCount(), memberRecipeProductDtos); - } - return new MemberRecipeDto(recipe.getId(), recipe.getTitle(), recipe.getContent(), recipe.getCreatedAt(), - findRecipeImages.get(0).getImage(), recipe.getFavoriteCount(), memberRecipeProductDtos); - } - - public Long getId() { - return id; - } - - public String getTitle() { - return title; - } - - public String getContent() { - return content; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public String getImage() { - return image; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public List getProducts() { - return products; - } -} diff --git a/backend/src/main/java/com/funeat/member/dto/MemberRecipeProductDto.java b/backend/src/main/java/com/funeat/member/dto/MemberRecipeProductDto.java deleted file mode 100644 index eec5ef79a..000000000 --- a/backend/src/main/java/com/funeat/member/dto/MemberRecipeProductDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.funeat.member.dto; - -import com.funeat.product.domain.Product; - -public class MemberRecipeProductDto { - - private final Long id; - private final String name; - - private MemberRecipeProductDto(final Long id, final String name) { - this.id = id; - this.name = name; - } - - public static MemberRecipeProductDto toDto(final Product product) { - return new MemberRecipeProductDto(product.getId(), product.getName()); - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } -} diff --git a/backend/src/main/java/com/funeat/member/dto/MemberRecipesResponse.java b/backend/src/main/java/com/funeat/member/dto/MemberRecipesResponse.java deleted file mode 100644 index 29a28fbc2..000000000 --- a/backend/src/main/java/com/funeat/member/dto/MemberRecipesResponse.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.funeat.member.dto; - -import com.funeat.common.dto.PageDto; -import java.util.List; - -public class MemberRecipesResponse { - - private final PageDto page; - private final List recipes; - - private MemberRecipesResponse(final PageDto page, final List recipes) { - this.page = page; - this.recipes = recipes; - } - - public static MemberRecipesResponse toResponse(final PageDto page, - final List recipes) { - return new MemberRecipesResponse(page, recipes); - } - - public PageDto getPage() { - return page; - } - - public List getRecipes() { - return recipes; - } -} diff --git a/backend/src/main/java/com/funeat/member/dto/MemberRequest.java b/backend/src/main/java/com/funeat/member/dto/MemberRequest.java deleted file mode 100644 index c8a2f9726..000000000 --- a/backend/src/main/java/com/funeat/member/dto/MemberRequest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.funeat.member.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import javax.validation.constraints.NotBlank; - -public class MemberRequest { - - @NotBlank(message = "닉네임을 확인해주세요") - private final String nickname; - - public MemberRequest(@JsonProperty("nickname") final String nickname) { - this.nickname = nickname; - } - - public String getNickname() { - return nickname; - } -} diff --git a/backend/src/main/java/com/funeat/member/dto/MemberReviewDto.java b/backend/src/main/java/com/funeat/member/dto/MemberReviewDto.java deleted file mode 100644 index 082b4dd72..000000000 --- a/backend/src/main/java/com/funeat/member/dto/MemberReviewDto.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.funeat.member.dto; - -import com.funeat.review.domain.Review; - -public class MemberReviewDto { - - private final Long reviewId; - private final Long productId; - private final String categoryType; - private final String productName; - private final String content; - private final Long rating; - private final Long favoriteCount; - - private MemberReviewDto(final Long reviewId, final Long productId, final String categoryType, - final String productName, final String content, - final Long rating, final Long favoriteCount) { - this.reviewId = reviewId; - this.productId = productId; - this.categoryType = categoryType; - this.productName = productName; - this.content = content; - this.rating = rating; - this.favoriteCount = favoriteCount; - } - - public static MemberReviewDto toDto(final Review review) { - return new MemberReviewDto( - review.getId(), - review.getProduct().getId(), - review.getProduct().getCategory().getType().getName(), - review.getProduct().getName(), - review.getContent(), - review.getRating(), - review.getFavoriteCount() - ); - } - - public Long getReviewId() { - return reviewId; - } - - public Long getProductId() { - return productId; - } - - public String getProductName() { - return productName; - } - - public String getContent() { - return content; - } - - public Long getRating() { - return rating; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public String getCategoryType() { - return categoryType; - } -} diff --git a/backend/src/main/java/com/funeat/member/dto/MemberReviewsResponse.java b/backend/src/main/java/com/funeat/member/dto/MemberReviewsResponse.java deleted file mode 100644 index e21fe33b7..000000000 --- a/backend/src/main/java/com/funeat/member/dto/MemberReviewsResponse.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.funeat.member.dto; - -import com.funeat.common.dto.PageDto; -import java.util.List; - -public class MemberReviewsResponse { - - private final PageDto page; - private final List reviews; - - private MemberReviewsResponse(final PageDto page, final List reviews) { - this.page = page; - this.reviews = reviews; - } - - public static MemberReviewsResponse toResponse(final PageDto page, final List reviews) { - return new MemberReviewsResponse(page, reviews); - } - - public PageDto getPage() { - return page; - } - - public List getReviews() { - return reviews; - } -} diff --git a/backend/src/main/java/com/funeat/member/exception/MemberErrorCode.java b/backend/src/main/java/com/funeat/member/exception/MemberErrorCode.java deleted file mode 100644 index 91b37d3c8..000000000 --- a/backend/src/main/java/com/funeat/member/exception/MemberErrorCode.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.funeat.member.exception; - -import org.springframework.http.HttpStatus; - -public enum MemberErrorCode { - - MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 회원입니다. 회원 id를 확인하세요.", "5001"), - MEMBER_UPDATE_ERROR(HttpStatus.BAD_REQUEST, "닉네임 또는 이미지를 확인하세요.", "5002"), - MEMBER_DUPLICATE_FAVORITE(HttpStatus.CONFLICT, "이미 좋아요를 누른 상태입니다.", "5003"), - ; - - private final HttpStatus status; - private final String message; - private final String code; - - MemberErrorCode(final HttpStatus status, final String message, final String code) { - this.status = status; - this.message = message; - this.code = code; - } - - public HttpStatus getStatus() { - return status; - } - - public String getMessage() { - return message; - } - - public String getCode() { - return code; - } -} diff --git a/backend/src/main/java/com/funeat/member/exception/MemberException.java b/backend/src/main/java/com/funeat/member/exception/MemberException.java deleted file mode 100644 index c0dab99e6..000000000 --- a/backend/src/main/java/com/funeat/member/exception/MemberException.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.funeat.member.exception; - -import com.funeat.exception.ErrorCode; -import com.funeat.exception.GlobalException; -import org.springframework.http.HttpStatus; - -public class MemberException extends GlobalException { - - public MemberException(final HttpStatus status, final ErrorCode errorCode) { - super(status, errorCode); - } - - public static class MemberNotFoundException extends MemberException { - public MemberNotFoundException(final MemberErrorCode errorCode, final Long memberId) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), memberId)); - } - } - - public static class MemberUpdateException extends MemberException { - public MemberUpdateException(final MemberErrorCode errorCode) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage())); - } - } - - public static class MemberDuplicateFavoriteException extends MemberException { - public MemberDuplicateFavoriteException(final MemberErrorCode errorCode, final Long memberId) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), memberId)); - } - } -} diff --git a/backend/src/main/java/com/funeat/member/persistence/MemberRepository.java b/backend/src/main/java/com/funeat/member/persistence/MemberRepository.java deleted file mode 100644 index 832c48a38..000000000 --- a/backend/src/main/java/com/funeat/member/persistence/MemberRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.funeat.member.persistence; - -import com.funeat.member.domain.Member; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface MemberRepository extends JpaRepository { - - Optional findByPlatformId(final String platformId); -} diff --git a/backend/src/main/java/com/funeat/member/persistence/RecipeFavoriteRepository.java b/backend/src/main/java/com/funeat/member/persistence/RecipeFavoriteRepository.java deleted file mode 100644 index 8eddf02cb..000000000 --- a/backend/src/main/java/com/funeat/member/persistence/RecipeFavoriteRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.funeat.member.persistence; - -import com.funeat.member.domain.Member; -import com.funeat.member.domain.favorite.RecipeFavorite; -import com.funeat.recipe.domain.Recipe; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface RecipeFavoriteRepository extends JpaRepository { - - Optional findByMemberAndRecipe(final Member member, final Recipe recipe); - - boolean existsByMemberAndRecipeAndFavoriteTrue(final Member member, final Recipe recipe); -} diff --git a/backend/src/main/java/com/funeat/member/persistence/ReviewFavoriteRepository.java b/backend/src/main/java/com/funeat/member/persistence/ReviewFavoriteRepository.java deleted file mode 100644 index f1ae40e5d..000000000 --- a/backend/src/main/java/com/funeat/member/persistence/ReviewFavoriteRepository.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.funeat.member.persistence; - -import com.funeat.member.domain.Member; -import com.funeat.member.domain.favorite.ReviewFavorite; -import com.funeat.review.domain.Review; -import java.util.List; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface ReviewFavoriteRepository extends JpaRepository { - - Optional findByMemberAndReview(final Member member, final Review review); - - void deleteByReview(final Review review); - - List findByReview(final Review review); -} diff --git a/backend/src/main/java/com/funeat/member/presentation/MemberApiController.java b/backend/src/main/java/com/funeat/member/presentation/MemberApiController.java deleted file mode 100644 index af00932f7..000000000 --- a/backend/src/main/java/com/funeat/member/presentation/MemberApiController.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.funeat.member.presentation; - -import com.funeat.auth.dto.LoginInfo; -import com.funeat.auth.util.AuthenticationPrincipal; -import com.funeat.common.logging.Logging; -import com.funeat.member.application.MemberService; -import com.funeat.member.dto.MemberProfileResponse; -import com.funeat.member.dto.MemberRecipesResponse; -import com.funeat.member.dto.MemberRequest; -import com.funeat.member.dto.MemberReviewsResponse; -import com.funeat.recipe.application.RecipeService; -import com.funeat.review.application.ReviewService; -import javax.validation.Valid; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -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.PutMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -@RestController -@RequestMapping("/api/members") -public class MemberApiController implements MemberController { - - private final MemberService memberService; - private final ReviewService reviewService; - private final RecipeService recipeService; - - public MemberApiController(final MemberService memberService, final ReviewService reviewService, - final RecipeService recipeService) { - this.memberService = memberService; - this.reviewService = reviewService; - this.recipeService = recipeService; - } - - @GetMapping - public ResponseEntity getMemberProfile(@AuthenticationPrincipal final LoginInfo loginInfo) { - final MemberProfileResponse response = memberService.getMemberProfile(loginInfo.getId()); - - return ResponseEntity.ok(response); - } - - @Logging - @PutMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity putMemberProfile(@AuthenticationPrincipal final LoginInfo loginInfo, - @RequestPart(required = false) final MultipartFile image, - @RequestPart @Valid final MemberRequest memberRequest) { - memberService.modify(loginInfo.getId(), image, memberRequest); - - return ResponseEntity.noContent().build(); - } - - @GetMapping("/reviews") - public ResponseEntity getMemberReview(@AuthenticationPrincipal final LoginInfo loginInfo, - @PageableDefault final Pageable pageable) { - final MemberReviewsResponse response = reviewService.findReviewByMember(loginInfo.getId(), pageable); - - return ResponseEntity.ok().body(response); - } - - @GetMapping("/recipes") - public ResponseEntity getMemberRecipe(@AuthenticationPrincipal final LoginInfo loginInfo, - @PageableDefault final Pageable pageable) { - final MemberRecipesResponse response = recipeService.findRecipeByMember(loginInfo.getId(), pageable); - - return ResponseEntity.ok().body(response); - } - - @Logging - @DeleteMapping("/reviews/{reviewId}") - public ResponseEntity deleteReview(@PathVariable final Long reviewId, - @AuthenticationPrincipal final LoginInfo loginInfo) { - reviewService.deleteReview(reviewId, loginInfo.getId()); - - return ResponseEntity.noContent().build(); - } -} diff --git a/backend/src/main/java/com/funeat/member/presentation/MemberController.java b/backend/src/main/java/com/funeat/member/presentation/MemberController.java deleted file mode 100644 index 9c5e60763..000000000 --- a/backend/src/main/java/com/funeat/member/presentation/MemberController.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.funeat.member.presentation; - -import com.funeat.auth.dto.LoginInfo; -import com.funeat.auth.util.AuthenticationPrincipal; -import com.funeat.member.dto.MemberProfileResponse; -import com.funeat.member.dto.MemberRecipesResponse; -import com.funeat.member.dto.MemberRequest; -import com.funeat.member.dto.MemberReviewsResponse; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.ResponseEntity; -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.PutMapping; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.multipart.MultipartFile; - -@Tag(name = "05.Member", description = "사용자 기능") -public interface MemberController { - - @Operation(summary = "사용자 정보 조회", description = "사용자 닉네임과 프로필 사진을 조회한다.") - @ApiResponse( - responseCode = "200", - description = "사용자 정보 조회 성공." - ) - @GetMapping - ResponseEntity getMemberProfile(@AuthenticationPrincipal final LoginInfo loginInfo); - - @Operation(summary = "사용자 정보 수정", description = "사용자 닉네임과 프로필 사진을 수정한다.") - @ApiResponse( - responseCode = "204", - description = "사용자 정보 수정 성공." - ) - @PutMapping - public ResponseEntity putMemberProfile(@AuthenticationPrincipal final LoginInfo loginInfo, - @RequestPart final MultipartFile image, - @RequestPart final MemberRequest request); - - @Operation(summary = "사용자 리뷰 조회", description = "사용자가 작성한 리뷰를 조회한다.") - @ApiResponse( - responseCode = "200", - description = "사용자 리뷰 조회 성공." - ) - @GetMapping - ResponseEntity getMemberReview(@AuthenticationPrincipal final LoginInfo loginInfo, - @PageableDefault final Pageable pageable); - - @Operation(summary = "사용자 꿀조합 조회", description = "사용자가 작성한 꿀조합을 조회한다.") - @ApiResponse( - responseCode = "200", - description = "사용자 꿀조합 조회 성공." - ) - @GetMapping - ResponseEntity getMemberRecipe(@AuthenticationPrincipal final LoginInfo loginInfo, - @PageableDefault final Pageable pageable); - - @Operation(summary = "리뷰 삭제", description = "자신이 작성한 리뷰를 삭제한다.") - @ApiResponse( - responseCode = "204", - description = "리뷰 삭제 성공." - ) - @DeleteMapping - ResponseEntity deleteReview(@PathVariable final Long reviewId, - @AuthenticationPrincipal final LoginInfo loginInfo); -} diff --git a/backend/src/main/java/com/funeat/product/application/CategoryService.java b/backend/src/main/java/com/funeat/product/application/CategoryService.java deleted file mode 100644 index 7b99b6f3c..000000000 --- a/backend/src/main/java/com/funeat/product/application/CategoryService.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.funeat.product.application; - -import com.funeat.product.domain.Category; -import com.funeat.product.domain.CategoryType; -import com.funeat.product.persistence.CategoryRepository; -import java.util.List; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@Transactional(readOnly = true) -public class CategoryService { - - private final CategoryRepository categoryRepository; - - public CategoryService(final CategoryRepository categoryRepository) { - this.categoryRepository = categoryRepository; - } - - public List findAllCategoriesByType(final CategoryType type) { - return categoryRepository.findAllByType(type); - } -} diff --git a/backend/src/main/java/com/funeat/product/application/ProductService.java b/backend/src/main/java/com/funeat/product/application/ProductService.java deleted file mode 100644 index 021ffe126..000000000 --- a/backend/src/main/java/com/funeat/product/application/ProductService.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.funeat.product.application; - -import static com.funeat.product.exception.CategoryErrorCode.CATEGORY_NOT_FOUND; -import static com.funeat.product.exception.ProductErrorCode.PRODUCT_NOT_FOUND; - -import com.funeat.common.dto.PageDto; -import com.funeat.product.domain.Category; -import com.funeat.product.domain.Product; -import com.funeat.product.dto.ProductInCategoryDto; -import com.funeat.product.dto.ProductResponse; -import com.funeat.product.dto.ProductReviewCountDto; -import com.funeat.product.dto.ProductSortCondition; -import com.funeat.product.dto.ProductsInCategoryResponse; -import com.funeat.product.dto.RankingProductDto; -import com.funeat.product.dto.RankingProductsResponse; -import com.funeat.product.dto.SearchProductDto; -import com.funeat.product.dto.SearchProductResultDto; -import com.funeat.product.dto.SearchProductResultsResponse; -import com.funeat.product.dto.SearchProductsResponse; -import com.funeat.product.exception.CategoryException.CategoryNotFoundException; -import com.funeat.product.exception.ProductException.ProductNotFoundException; -import com.funeat.product.persistence.CategoryRepository; -import com.funeat.product.persistence.ProductRecipeRepository; -import com.funeat.product.persistence.ProductRepository; -import com.funeat.product.persistence.ProductSpecification; -import com.funeat.recipe.domain.Recipe; -import com.funeat.recipe.domain.RecipeImage; -import com.funeat.recipe.dto.RecipeDto; -import com.funeat.recipe.dto.SortingRecipesResponse; -import com.funeat.recipe.persistence.RecipeImageRepository; -import com.funeat.recipe.persistence.RecipeRepository; -import com.funeat.review.persistence.ReviewRepository; -import com.funeat.review.persistence.ReviewTagRepository; -import com.funeat.tag.domain.Tag; -import java.time.LocalDateTime; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@Transactional(readOnly = true) -public class ProductService { - - private static final int THREE = 3; - private static final int TOP = 0; - private static final int RANKING_SIZE = 3; - private static final int DEFAULT_PAGE_SIZE = 10; - private static final int DEFAULT_CURSOR_PAGINATION_SIZE = 11; - - private final CategoryRepository categoryRepository; - private final ProductRepository productRepository; - private final ReviewTagRepository reviewTagRepository; - private final ReviewRepository reviewRepository; - private final ProductRecipeRepository productRecipeRepository; - private final RecipeImageRepository recipeImageRepository; - private final RecipeRepository recipeRepository; - - public ProductService(final CategoryRepository categoryRepository, final ProductRepository productRepository, - final ReviewTagRepository reviewTagRepository, final ReviewRepository reviewRepository, - final ProductRecipeRepository productRecipeRepository, - final RecipeImageRepository recipeImageRepository, - final RecipeRepository recipeRepository) { - this.categoryRepository = categoryRepository; - this.productRepository = productRepository; - this.reviewTagRepository = reviewTagRepository; - this.reviewRepository = reviewRepository; - this.productRecipeRepository = productRecipeRepository; - this.recipeImageRepository = recipeImageRepository; - this.recipeRepository = recipeRepository; - } - - public ProductsInCategoryResponse getAllProductsInCategory(final Long categoryId, final Long lastProductId, - final ProductSortCondition sortCondition) { - final Category category = categoryRepository.findById(categoryId) - .orElseThrow(() -> new CategoryNotFoundException(CATEGORY_NOT_FOUND, categoryId)); - final Product lastProduct = productRepository.findById(lastProductId).orElse(null); - - final Specification specification = ProductSpecification.searchBy(category, lastProduct, sortCondition); - final List findResults = productRepository.findAllWithSpecification(specification, DEFAULT_CURSOR_PAGINATION_SIZE); - - final List productDtos = getProductInCategoryDtos(findResults); - final boolean hasNext = hasNextPage(findResults); - - return ProductsInCategoryResponse.toResponse(hasNext, productDtos); - } - - private List getProductInCategoryDtos(final List findProducts) { - final int resultSize = getResultSize(findProducts); - final List products = findProducts.subList(0, resultSize); - - return products.stream() - .map(ProductInCategoryDto::toDto) - .collect(Collectors.toList()); - } - - private int getResultSize(final List findProducts) { - if (findProducts.size() < DEFAULT_CURSOR_PAGINATION_SIZE) { - return findProducts.size(); - } - return DEFAULT_PAGE_SIZE; - } - - private boolean hasNextPage(final List findProducts) { - return findProducts.size() > DEFAULT_PAGE_SIZE; - } - - public ProductResponse findProductDetail(final Long productId) { - final Product product = productRepository.findById(productId) - .orElseThrow(() -> new ProductNotFoundException(PRODUCT_NOT_FOUND, productId)); - final List tags = reviewTagRepository.findTop3TagsByReviewIn(productId, PageRequest.of(TOP, THREE)); - - return ProductResponse.toResponse(product, tags); - } - - public RankingProductsResponse getTop3Products() { - final LocalDateTime endDateTime = LocalDateTime.now(); - final LocalDateTime startDateTime = endDateTime.minusWeeks(2L); - final List productsAndReviewCounts = productRepository.findAllByAverageRatingGreaterThan3(startDateTime, endDateTime); - final Comparator rankingScoreComparator = Comparator.comparing( - (ProductReviewCountDto it) -> it.getProduct().calculateRankingScore(it.getReviewCount()) - ).reversed(); - - final List rankingProductDtos = productsAndReviewCounts.stream() - .sorted(rankingScoreComparator) - .limit(RANKING_SIZE) - .map(it -> RankingProductDto.toDto(it.getProduct())) - .collect(Collectors.toList()); - - return RankingProductsResponse.toResponse(rankingProductDtos); - } - - public SearchProductsResponse searchProducts(final String query, final Pageable pageable) { - final Page products = productRepository.findAllByNameContaining(query, pageable); - - final PageDto pageDto = PageDto.toDto(products); - final List productDtos = products.stream() - .map(SearchProductDto::toDto) - .collect(Collectors.toList()); - - return SearchProductsResponse.toResponse(pageDto, productDtos); - } - - public SearchProductResultsResponse getSearchResults(final String query, final Pageable pageable) { - final Page products = productRepository.findAllWithReviewCountByNameContaining(query, - pageable); - - final PageDto pageDto = PageDto.toDto(products); - final List resultDtos = products.stream() - .map(it -> SearchProductResultDto.toDto(it.getProduct(), it.getReviewCount())) - .collect(Collectors.toList()); - - return SearchProductResultsResponse.toResponse(pageDto, resultDtos); - } - - public SortingRecipesResponse getProductRecipes(final Long productId, final Pageable pageable) { - final Product product = productRepository.findById(productId) - .orElseThrow(() -> new ProductNotFoundException(PRODUCT_NOT_FOUND, productId)); - - final Page recipes = recipeRepository.findRecipesByProduct(product, pageable); - - final PageDto pageDto = PageDto.toDto(recipes); - final List recipeDtos = recipes.stream() - .map(recipe -> { - final List images = recipeImageRepository.findByRecipe(recipe); - final List products = productRecipeRepository.findProductByRecipe(recipe); - return RecipeDto.toDto(recipe, images, products); - }) - .collect(Collectors.toList()); - return SortingRecipesResponse.toResponse(pageDto, recipeDtos); - } -} diff --git a/backend/src/main/java/com/funeat/product/domain/Category.java b/backend/src/main/java/com/funeat/product/domain/Category.java deleted file mode 100644 index 7504aad1d..000000000 --- a/backend/src/main/java/com/funeat/product/domain/Category.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.funeat.product.domain; - -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; - -@Entity -public class Category { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String name; - - @Enumerated(EnumType.STRING) - private CategoryType type; - - private String image; - - protected Category() { - } - - public Category(final String name, final CategoryType type, final String image) { - this.name = name; - this.type = type; - this.image = image; - } - - public boolean isFood() { - return type == CategoryType.FOOD; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public CategoryType getType() { - return type; - } - - public String getImage() { - return image; - } -} diff --git a/backend/src/main/java/com/funeat/product/domain/CategoryType.java b/backend/src/main/java/com/funeat/product/domain/CategoryType.java deleted file mode 100644 index f80311161..000000000 --- a/backend/src/main/java/com/funeat/product/domain/CategoryType.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.funeat.product.domain; - -import static com.funeat.product.exception.CategoryErrorCode.CATEGORY_TYPE_NOT_FOUND; - -import com.funeat.product.exception.CategoryException.CategoryTypeNotFoundException; -import java.util.Arrays; - -public enum CategoryType { - - FOOD("food"), - STORE("store"), - ; - - private final String name; - - CategoryType(final String name) { - this.name = name; - } - - public static CategoryType findCategoryType(final String type) { - return Arrays.stream(values()) - .filter(it -> it.name().equals(type.toUpperCase())) - .findFirst() - .orElseThrow(() -> new CategoryTypeNotFoundException(CATEGORY_TYPE_NOT_FOUND, type)); - } - - public String getName() { - return name; - } -} diff --git a/backend/src/main/java/com/funeat/product/domain/Product.java b/backend/src/main/java/com/funeat/product/domain/Product.java deleted file mode 100644 index a95a7b91e..000000000 --- a/backend/src/main/java/com/funeat/product/domain/Product.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.funeat.product.domain; - -import com.funeat.review.domain.Review; -import java.util.List; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; - -@Entity -public class Product { - - public static final String BASIC_IMAGE = null; - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String name; - - private Long price; - - private String image; - - private String content; - - private Double averageRating = 0.0; - - private Long reviewCount = 0L; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "category_id") - private Category category; - - @OneToMany(mappedBy = "product") - private List reviews; - - @OneToMany(mappedBy = "product") - private List productRecipes; - - protected Product() { - } - - public Product(final String name, final Long price, final String image, final String content, - final Category category) { - this.name = name; - this.price = price; - this.image = image; - this.content = content; - this.category = category; - } - - public Product(final String name, final Long price, final String image, final String content, - final Double averageRating, final Category category) { - this.name = name; - this.price = price; - this.image = image; - this.content = content; - this.averageRating = averageRating; - this.category = category; - } - - public Product(final String name, final Long price, final String image, final String content, - final Category category, final Long reviewCount) { - this.name = name; - this.price = price; - this.image = image; - this.content = content; - this.category = category; - this.reviewCount = reviewCount; - } - - public Product(final String name, final Long price, final String image, final String content, - final Double averageRating, final Category category, final Long reviewCount) { - this.name = name; - this.price = price; - this.image = image; - this.content = content; - this.averageRating = averageRating; - this.category = category; - this.reviewCount = reviewCount; - } - - public static Product create(final String name, final Long price, final String content, final Category category) { - return new Product(name, price, null, content, category); - } - - public void updateAverageRatingForInsert(final Long count, final Long rating) { - final double calculatedRating = ((count - 1) * averageRating + rating) / count; - this.averageRating = Math.round(calculatedRating * 10.0) / 10.0; - } - - public void updateAverageRatingForDelete(final Long deletedRating) { - if (reviewCount == 1) { - this.averageRating = 0.0; - return; - } - final double calculatedRating = (reviewCount * averageRating - deletedRating) / (reviewCount - 1); - this.averageRating = Math.round(calculatedRating * 10.0) / 10.0; - } - - public Double calculateRankingScore(final Long reviewCount) { - final double exponent = -Math.log10(reviewCount + 1); - final double factor = Math.pow(2, exponent); - return averageRating - (averageRating - 3.0) * factor; - } - - public void updateBasicImage() { - this.image = BASIC_IMAGE; - } - - public void updateFavoriteImage(final String topFavoriteImage) { - this.image = topFavoriteImage; - } - - public void update(final String name, final String content, final Long price, final Category category) { - this.name = name; - this.content = content; - this.price = price; - this.category = category; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public Long getPrice() { - return price; - } - - public String getImage() { - return image; - } - - public String getContent() { - return content; - } - - public Double getAverageRating() { - return averageRating; - } - - public Category getCategory() { - return category; - } - - public Long getReviewCount() { - return reviewCount; - } - - public void addReviewCount() { - reviewCount++; - } - - public void minusReviewCount() { - reviewCount--; - } -} diff --git a/backend/src/main/java/com/funeat/product/domain/ProductRecipe.java b/backend/src/main/java/com/funeat/product/domain/ProductRecipe.java deleted file mode 100644 index 4bca08c3d..000000000 --- a/backend/src/main/java/com/funeat/product/domain/ProductRecipe.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.funeat.product.domain; - -import com.funeat.recipe.domain.Recipe; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; - -@Entity -public class ProductRecipe { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "product_id") - private Product product; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "recipe_id") - private Recipe recipe; - - protected ProductRecipe() { - } - - public ProductRecipe(final Product product, final Recipe recipe) { - this.product = product; - this.recipe = recipe; - } -} diff --git a/backend/src/main/java/com/funeat/product/dto/CategoryResponse.java b/backend/src/main/java/com/funeat/product/dto/CategoryResponse.java deleted file mode 100644 index 3ea9e59a5..000000000 --- a/backend/src/main/java/com/funeat/product/dto/CategoryResponse.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.funeat.product.dto; - -import com.funeat.product.domain.Category; - -public class CategoryResponse { - - private final Long id; - private final String name; - private final String image; - - public CategoryResponse(final Long id, final String name, final String image) { - this.id = id; - this.name = name; - this.image = image; - } - - public static CategoryResponse toResponse(final Category category) { - return new CategoryResponse(category.getId(), category.getName(), category.getImage()); - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getImage() { - return image; - } -} diff --git a/backend/src/main/java/com/funeat/product/dto/ProductInCategoryDto.java b/backend/src/main/java/com/funeat/product/dto/ProductInCategoryDto.java deleted file mode 100644 index e4c73b606..000000000 --- a/backend/src/main/java/com/funeat/product/dto/ProductInCategoryDto.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.funeat.product.dto; - -import com.funeat.product.domain.Product; - -public class ProductInCategoryDto { - - private final Long id; - private final String name; - private final Long price; - private final String image; - private final Double averageRating; - private final Long reviewCount; - - public ProductInCategoryDto(final Long id, final String name, final Long price, final String image, - final Double averageRating, final Long reviewCount) { - this.id = id; - this.name = name; - this.price = price; - this.image = image; - this.averageRating = averageRating; - this.reviewCount = reviewCount; - } - - public static ProductInCategoryDto toDto(final Product product) { - return new ProductInCategoryDto(product.getId(), product.getName(), product.getPrice(), product.getImage(), - product.getAverageRating(), product.getReviewCount()); - } - - public static ProductInCategoryDto toDto(final Product product, final Long reviewCount) { - return new ProductInCategoryDto(product.getId(), product.getName(), product.getPrice(), product.getImage(), - product.getAverageRating(), reviewCount); - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public Long getPrice() { - return price; - } - - public String getImage() { - return image; - } - - public Double getAverageRating() { - return averageRating; - } - - public Long getReviewCount() { - return reviewCount; - } -} diff --git a/backend/src/main/java/com/funeat/product/dto/ProductResponse.java b/backend/src/main/java/com/funeat/product/dto/ProductResponse.java deleted file mode 100644 index d3c0ed264..000000000 --- a/backend/src/main/java/com/funeat/product/dto/ProductResponse.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.funeat.product.dto; - -import com.funeat.product.domain.Product; -import com.funeat.tag.domain.Tag; -import com.funeat.tag.dto.TagDto; -import java.util.ArrayList; -import java.util.List; - -public class ProductResponse { - - private final Long id; - private final String name; - private final Long price; - private final String image; - private final String content; - private final Double averageRating; - private final Long reviewCount; - private final List tags; - - public ProductResponse(final Long id, final String name, final Long price, final String image, final String content, - final Double averageRating, final Long reviewCount, final List tags) { - this.id = id; - this.name = name; - this.price = price; - this.image = image; - this.content = content; - this.averageRating = averageRating; - this.reviewCount = reviewCount; - this.tags = tags; - } - - public static ProductResponse toResponse(final Product product, final List tags) { - List tagDtos = new ArrayList<>(); - for (Tag tag : tags) { - tagDtos.add(TagDto.toDto(tag)); - } - return new ProductResponse(product.getId(), product.getName(), product.getPrice(), product.getImage(), - product.getContent(), product.getAverageRating(), product.getReviewCount(), tagDtos); - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public Long getPrice() { - return price; - } - - public String getImage() { - return image; - } - - public String getContent() { - return content; - } - - public Double getAverageRating() { - return averageRating; - } - - public Long getReviewCount() { - return reviewCount; - } - - public List getTags() { - return tags; - } -} diff --git a/backend/src/main/java/com/funeat/product/dto/ProductReviewCountDto.java b/backend/src/main/java/com/funeat/product/dto/ProductReviewCountDto.java deleted file mode 100644 index f769b708d..000000000 --- a/backend/src/main/java/com/funeat/product/dto/ProductReviewCountDto.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.funeat.product.dto; - -import com.funeat.product.domain.Product; - -public class ProductReviewCountDto { - - private final Product product; - private final Long reviewCount; - - public ProductReviewCountDto(final Product product, final Long reviewCount) { - this.product = product; - this.reviewCount = reviewCount; - } - - public Product getProduct() { - return product; - } - - public Long getReviewCount() { - return reviewCount; - } -} diff --git a/backend/src/main/java/com/funeat/product/dto/ProductSortCondition.java b/backend/src/main/java/com/funeat/product/dto/ProductSortCondition.java deleted file mode 100644 index 8a929f99c..000000000 --- a/backend/src/main/java/com/funeat/product/dto/ProductSortCondition.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.funeat.product.dto; - -public class ProductSortCondition { - - private final String by; - private final String order; - - private ProductSortCondition(final String by, final String order) { - this.by = by; - this.order = order; - } - - public static ProductSortCondition toDto(final String sort) { - final String[] split = sort.split(","); - return new ProductSortCondition(split[0], split[1]); - } - - public String getBy() { - return by; - } - - public String getOrder() { - return order; - } -} diff --git a/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryResponse.java b/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryResponse.java deleted file mode 100644 index 39c685268..000000000 --- a/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryResponse.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.funeat.product.dto; - -import java.util.List; - -public class ProductsInCategoryResponse { - - private final boolean hasNext; - private final List products; - - public ProductsInCategoryResponse(final boolean hasNext, final List products) { - this.hasNext = hasNext; - this.products = products; - } - - public static ProductsInCategoryResponse toResponse(final boolean hasNext, - final List products) { - return new ProductsInCategoryResponse(hasNext, products); - } - - public boolean isHasNext() { - return hasNext; - } - - public List getProducts() { - return products; - } -} diff --git a/backend/src/main/java/com/funeat/product/dto/RankingProductDto.java b/backend/src/main/java/com/funeat/product/dto/RankingProductDto.java deleted file mode 100644 index 0b14c0efd..000000000 --- a/backend/src/main/java/com/funeat/product/dto/RankingProductDto.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.funeat.product.dto; - -import com.funeat.product.domain.Product; - -public class RankingProductDto { - - private final Long id; - private final String name; - private final String image; - private final String categoryType; - - public RankingProductDto(final Long id, final String name, final String image, final String categoryType) { - this.id = id; - this.name = name; - this.image = image; - this.categoryType = categoryType; - } - - public static RankingProductDto toDto(final Product product) { - return new RankingProductDto(product.getId(), product.getName(), product.getImage(), - product.getCategory().getType().getName()); - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getImage() { - return image; - } - - public String getCategoryType() { - return categoryType; - } -} diff --git a/backend/src/main/java/com/funeat/product/dto/RankingProductsResponse.java b/backend/src/main/java/com/funeat/product/dto/RankingProductsResponse.java deleted file mode 100644 index 14bfcf158..000000000 --- a/backend/src/main/java/com/funeat/product/dto/RankingProductsResponse.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.funeat.product.dto; - -import java.util.List; - -public class RankingProductsResponse { - - private final List products; - - public RankingProductsResponse(final List products) { - this.products = products; - } - - public static RankingProductsResponse toResponse(final List products) { - return new RankingProductsResponse(products); - } - - public List getProducts() { - return products; - } -} diff --git a/backend/src/main/java/com/funeat/product/dto/SearchProductDto.java b/backend/src/main/java/com/funeat/product/dto/SearchProductDto.java deleted file mode 100644 index f41bfa110..000000000 --- a/backend/src/main/java/com/funeat/product/dto/SearchProductDto.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.funeat.product.dto; - -import com.funeat.product.domain.Product; - -public class SearchProductDto { - - private final Long id; - private final String name; - private final String categoryType; - - public SearchProductDto(final Long id, final String name, final String categoryType) { - this.id = id; - this.name = name; - this.categoryType = categoryType; - } - - public static SearchProductDto toDto(final Product product) { - return new SearchProductDto(product.getId(), product.getName(), product.getCategory().getType().getName()); - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getCategoryType() { - return categoryType; - } -} diff --git a/backend/src/main/java/com/funeat/product/dto/SearchProductResultDto.java b/backend/src/main/java/com/funeat/product/dto/SearchProductResultDto.java deleted file mode 100644 index d4119da49..000000000 --- a/backend/src/main/java/com/funeat/product/dto/SearchProductResultDto.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.funeat.product.dto; - -import com.funeat.product.domain.Product; - -public class SearchProductResultDto { - - private final Long id; - private final String name; - private final Long price; - private final String image; - private final Double averageRating; - private final Long reviewCount; - private final String categoryType; - - public SearchProductResultDto(final Long id, final String name, final Long price, final String image, - final Double averageRating, final Long reviewCount, final String categoryType) { - this.id = id; - this.name = name; - this.price = price; - this.image = image; - this.averageRating = averageRating; - this.reviewCount = reviewCount; - this.categoryType = categoryType; - } - - public static SearchProductResultDto toDto(final Product product, final Long reviewCount) { - return new SearchProductResultDto(product.getId(), product.getName(), product.getPrice(), product.getImage(), - product.getAverageRating(), reviewCount, product.getCategory().getType().getName()); - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public Long getPrice() { - return price; - } - - public String getImage() { - return image; - } - - public Double getAverageRating() { - return averageRating; - } - - public Long getReviewCount() { - return reviewCount; - } - - public String getCategoryType() { - return categoryType; - } -} diff --git a/backend/src/main/java/com/funeat/product/dto/SearchProductResultsResponse.java b/backend/src/main/java/com/funeat/product/dto/SearchProductResultsResponse.java deleted file mode 100644 index 98aa9eb7c..000000000 --- a/backend/src/main/java/com/funeat/product/dto/SearchProductResultsResponse.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.funeat.product.dto; - -import com.funeat.common.dto.PageDto; -import java.util.List; - -public class SearchProductResultsResponse { - - private final PageDto page; - private final List products; - - public SearchProductResultsResponse(final PageDto page, final List products) { - this.page = page; - this.products = products; - } - - public static SearchProductResultsResponse toResponse(final PageDto page, - final List products) { - return new SearchProductResultsResponse(page, products); - } - - public PageDto getPage() { - return page; - } - - public List getProducts() { - return products; - } -} diff --git a/backend/src/main/java/com/funeat/product/dto/SearchProductsResponse.java b/backend/src/main/java/com/funeat/product/dto/SearchProductsResponse.java deleted file mode 100644 index ccdeade5a..000000000 --- a/backend/src/main/java/com/funeat/product/dto/SearchProductsResponse.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.funeat.product.dto; - -import com.funeat.common.dto.PageDto; -import java.util.List; - -public class SearchProductsResponse { - - private final PageDto page; - private final List products; - - public SearchProductsResponse(final PageDto page, final List products) { - this.page = page; - this.products = products; - } - - public static SearchProductsResponse toResponse(final PageDto page, final List products) { - return new SearchProductsResponse(page, products); - } - - public PageDto getPage() { - return page; - } - - public List getProducts() { - return products; - } -} diff --git a/backend/src/main/java/com/funeat/product/exception/CategoryErrorCode.java b/backend/src/main/java/com/funeat/product/exception/CategoryErrorCode.java deleted file mode 100644 index 5214a03b6..000000000 --- a/backend/src/main/java/com/funeat/product/exception/CategoryErrorCode.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.funeat.product.exception; - -import org.springframework.http.HttpStatus; - -public enum CategoryErrorCode { - - CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 카테고리입니다. 카테고리 id를 확인하세요.", "2001"), - CATEGORY_TYPE_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 카테고리 타입입니다. 카테고리 타입을 확인하세요.", "2002"), - ; - - private final HttpStatus status; - private final String message; - private final String code; - - CategoryErrorCode(final HttpStatus status, final String message, final String code) { - this.status = status; - this.message = message; - this.code = code; - } - - public HttpStatus getStatus() { - return status; - } - - public String getMessage() { - return message; - } - - public String getCode() { - return code; - } -} diff --git a/backend/src/main/java/com/funeat/product/exception/CategoryException.java b/backend/src/main/java/com/funeat/product/exception/CategoryException.java deleted file mode 100644 index 6402c250a..000000000 --- a/backend/src/main/java/com/funeat/product/exception/CategoryException.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.funeat.product.exception; - -import com.funeat.exception.ErrorCode; -import com.funeat.exception.GlobalException; -import org.springframework.http.HttpStatus; - -public class CategoryException extends GlobalException { - - public CategoryException(final HttpStatus status, final ErrorCode errorCode) { - super(status, errorCode); - } - - public static class CategoryNotFoundException extends CategoryException { - public CategoryNotFoundException(final CategoryErrorCode errorCode, final Long categoryId) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), categoryId)); - } - } - - public static class CategoryTypeNotFoundException extends CategoryException { - public CategoryTypeNotFoundException(final CategoryErrorCode errorCode, final String type) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), type)); - } - } -} diff --git a/backend/src/main/java/com/funeat/product/exception/ProductErrorCode.java b/backend/src/main/java/com/funeat/product/exception/ProductErrorCode.java deleted file mode 100644 index e3b4d3ccc..000000000 --- a/backend/src/main/java/com/funeat/product/exception/ProductErrorCode.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.funeat.product.exception; - -import org.springframework.http.HttpStatus; - -public enum ProductErrorCode { - - PRODUCT_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 상품입니다. 상품 id를 확인하세요.", "1001"), - NOT_SUPPORTED_PRODUCT_SORTING_CONDITION(HttpStatus.BAD_REQUEST, "정렬 조건이 올바르지 않습니다. 정렬 조건을 확인하세요", "1002"); - ; - - private final HttpStatus status; - private final String message; - private final String code; - - ProductErrorCode(final HttpStatus status, final String message, final String code) { - this.status = status; - this.message = message; - this.code = code; - } - - public HttpStatus getStatus() { - return status; - } - - public String getMessage() { - return message; - } - - public String getCode() { - return code; - } -} diff --git a/backend/src/main/java/com/funeat/product/exception/ProductException.java b/backend/src/main/java/com/funeat/product/exception/ProductException.java deleted file mode 100644 index bdbb4782b..000000000 --- a/backend/src/main/java/com/funeat/product/exception/ProductException.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.funeat.product.exception; - -import com.funeat.exception.ErrorCode; -import com.funeat.exception.GlobalException; -import org.springframework.http.HttpStatus; - -public class ProductException extends GlobalException { - - public ProductException(final HttpStatus status, final ErrorCode errorCode) { - super(status, errorCode); - } - - public static class ProductNotFoundException extends ProductException { - public ProductNotFoundException(final ProductErrorCode errorCode, final Long productId) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), productId)); - } - } - - public static class NotSupportedProductSortingConditionException extends ProductException { - public NotSupportedProductSortingConditionException(final ProductErrorCode errorCode, final String sortBy) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), sortBy)); - } - } -} diff --git a/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java b/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java deleted file mode 100644 index 4da064517..000000000 --- a/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.funeat.product.persistence; - -import com.funeat.product.domain.Category; -import com.funeat.product.domain.CategoryType; -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface CategoryRepository extends JpaRepository { - - List findAllByType(final CategoryType type); -} diff --git a/backend/src/main/java/com/funeat/product/persistence/ProductRecipeRepository.java b/backend/src/main/java/com/funeat/product/persistence/ProductRecipeRepository.java deleted file mode 100644 index a5c819571..000000000 --- a/backend/src/main/java/com/funeat/product/persistence/ProductRecipeRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.funeat.product.persistence; - -import com.funeat.product.domain.Product; -import com.funeat.product.domain.ProductRecipe; -import com.funeat.recipe.domain.Recipe; -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; - -public interface ProductRecipeRepository extends JpaRepository { - - @Query("SELECT pr.product FROM ProductRecipe pr WHERE pr.recipe = :recipe") - List findProductByRecipe(final Recipe recipe); -} diff --git a/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java b/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java deleted file mode 100644 index 27208f254..000000000 --- a/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.funeat.product.persistence; - -import com.funeat.common.repository.BaseRepository; -import com.funeat.product.domain.Product; -import com.funeat.product.dto.ProductReviewCountDto; -import java.time.LocalDateTime; -import java.util.List; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -public interface ProductRepository extends BaseRepository { - - @Query("SELECT new com.funeat.product.dto.ProductReviewCountDto(p, COUNT(r.id)) " - + "FROM Product p " - + "LEFT JOIN Review r ON r.product.id = p.id " - + "WHERE p.averageRating > 3.0 " - + "AND r.createdAt BETWEEN :startDateTime AND :endDateTime " - + "GROUP BY p.id") - List findAllByAverageRatingGreaterThan3(final LocalDateTime startDateTime, - final LocalDateTime endDateTime); - - @Query("SELECT p FROM Product p " - + "WHERE p.name LIKE CONCAT('%', :name, '%') " - + "ORDER BY " - + "(CASE WHEN p.name LIKE CONCAT(:name, '%') THEN 1 ELSE 2 END), " - + "p.id DESC") - Page findAllByNameContaining(@Param("name") final String name, final Pageable pageable); - - @Query("SELECT new com.funeat.product.dto.ProductReviewCountDto(p, COUNT(r.id)) FROM Product p " - + "LEFT JOIN Review r ON r.product.id = p.id " - + "WHERE p.name LIKE CONCAT('%', :name, '%') " - + "GROUP BY p.id " - + "ORDER BY " - + "(CASE WHEN p.name LIKE CONCAT(:name, '%') THEN 1 ELSE 2 END), p.id DESC") - Page findAllWithReviewCountByNameContaining(@Param("name") final String name, - final Pageable pageable); -} diff --git a/backend/src/main/java/com/funeat/product/persistence/ProductSpecification.java b/backend/src/main/java/com/funeat/product/persistence/ProductSpecification.java deleted file mode 100644 index f2dbd2ff2..000000000 --- a/backend/src/main/java/com/funeat/product/persistence/ProductSpecification.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.funeat.product.persistence; - -import static com.funeat.product.exception.ProductErrorCode.NOT_SUPPORTED_PRODUCT_SORTING_CONDITION; - -import com.funeat.product.domain.Category; -import com.funeat.product.domain.Product; -import com.funeat.product.dto.ProductSortCondition; -import com.funeat.product.exception.ProductException.NotSupportedProductSortingConditionException; -import java.util.Objects; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Root; -import org.springframework.data.jpa.domain.Specification; - -public class ProductSpecification { - - private ProductSpecification() { - } - - private static final String DESC = "desc"; - private static final String CATEGORY = "category"; - private static final String ID = "id"; - private static final String REVIEW_COUNT = "reviewCount"; - private static final String AVERAGE_RATING = "averageRating"; - private static final String PRICE = "price"; - - public static Specification searchBy(final Category category, final Product lastProduct, - final ProductSortCondition sortCondition) { - return (root, query, builder) -> { - setOrderBy(sortCondition, root, query, builder); - - return Specification - .where(sameCategory(category)) - .and(nextCursor(lastProduct, sortCondition)) - .toPredicate(root, query, builder); - }; - } - - private static void setOrderBy(final ProductSortCondition sortCondition, final Root root, - final CriteriaQuery query, final CriteriaBuilder builder) { - final String sortBy = sortCondition.getBy(); - final String sortOrder = sortCondition.getOrder(); - - if (DESC.equals(sortOrder)) { - query.orderBy(builder.desc(root.get(sortBy)), builder.desc(root.get(ID))); - } else { - query.orderBy(builder.asc(root.get(sortBy)), builder.desc(root.get(ID))); - } - } - - private static Specification sameCategory(final Category category) { - return (root, query, builder) -> { - final Path categoryPath = root.get(CATEGORY); - - return builder.equal(categoryPath, category); - }; - } - - private static Specification nextCursor(final Product lastProduct, final ProductSortCondition sortCondition) { - final String sortBy = sortCondition.getBy(); - final String sortOrder = sortCondition.getOrder(); - - return (root, query, builder) -> { - if (Objects.isNull(lastProduct)) { - return null; - } - - final Comparable comparisonValue = (Comparable) getComparisonValue(lastProduct, sortBy); - - return builder.or( - sameValueAndSmallerId(sortBy, lastProduct.getId(), comparisonValue).toPredicate(root, query, builder), - nextValue(sortBy, sortOrder, comparisonValue).toPredicate(root, query, builder) - ); - }; - } - - private static Object getComparisonValue(final Product lastProduct, final String sortBy) { - if (PRICE.equals(sortBy)) { - return lastProduct.getPrice(); - } - if (AVERAGE_RATING.equals(sortBy)) { - return lastProduct.getAverageRating(); - } - if (REVIEW_COUNT.equals(sortBy)) { - return lastProduct.getReviewCount(); - } - throw new NotSupportedProductSortingConditionException(NOT_SUPPORTED_PRODUCT_SORTING_CONDITION, sortBy); - } - - private static Specification sameValueAndSmallerId(final String sortBy, final Long lastProductId, - final Comparable comparisonValue) { - return (root, query, builder) -> builder.and( - builder.equal(root.get(sortBy), comparisonValue), - builder.lessThan(root.get(ID), lastProductId)); - } - - private static Specification nextValue(final String sortBy, final String sortOrder, - final Comparable comparisonValue) { - return (root, query, builder) -> { - if (DESC.equals(sortOrder)) { - return builder.lessThan(root.get(sortBy), comparisonValue); - } else { - return builder.greaterThan(root.get(sortBy), comparisonValue); - } - }; - } -} diff --git a/backend/src/main/java/com/funeat/product/presentation/CategoryApiController.java b/backend/src/main/java/com/funeat/product/presentation/CategoryApiController.java deleted file mode 100644 index f084fc174..000000000 --- a/backend/src/main/java/com/funeat/product/presentation/CategoryApiController.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.funeat.product.presentation; - -import com.funeat.product.application.CategoryService; -import com.funeat.product.domain.CategoryType; -import com.funeat.product.dto.CategoryResponse; -import java.util.List; -import java.util.stream.Collectors; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api/categories") -public class CategoryApiController implements CategoryController { - - private final CategoryService categoryService; - - public CategoryApiController(final CategoryService categoryService) { - this.categoryService = categoryService; - } - - @GetMapping - public ResponseEntity> getAllCategoriesByType(@RequestParam final CategoryType type) { - final List responses = categoryService.findAllCategoriesByType(type).stream() - .map(CategoryResponse::toResponse) - .collect(Collectors.toList()); - return ResponseEntity.ok(responses); - } -} diff --git a/backend/src/main/java/com/funeat/product/presentation/CategoryController.java b/backend/src/main/java/com/funeat/product/presentation/CategoryController.java deleted file mode 100644 index 53625ffb6..000000000 --- a/backend/src/main/java/com/funeat/product/presentation/CategoryController.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.funeat.product.presentation; - -import com.funeat.product.domain.CategoryType; -import com.funeat.product.dto.CategoryResponse; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.List; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; - -@Tag(name = "02.Category", description = "카테고리 기능") -public interface CategoryController { - - @Operation(summary = "해당 type 카테고리 전체 조회", description = "FOOD 또는 STORE 를 받아 해당 카테고리를 전체 조회한다.") - @ApiResponse( - responseCode = "200", - description = "해당 type 카테고리 전체 조회 성공." - ) - @GetMapping - ResponseEntity> getAllCategoriesByType(@RequestParam final CategoryType type); -} diff --git a/backend/src/main/java/com/funeat/product/presentation/ProductApiController.java b/backend/src/main/java/com/funeat/product/presentation/ProductApiController.java deleted file mode 100644 index 46435aee7..000000000 --- a/backend/src/main/java/com/funeat/product/presentation/ProductApiController.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.funeat.product.presentation; - -import com.funeat.product.application.ProductService; -import com.funeat.product.dto.ProductResponse; -import com.funeat.product.dto.ProductSortCondition; -import com.funeat.product.dto.ProductsInCategoryResponse; -import com.funeat.product.dto.RankingProductsResponse; -import com.funeat.product.dto.SearchProductResultsResponse; -import com.funeat.product.dto.SearchProductsResponse; -import com.funeat.recipe.dto.SortingRecipesResponse; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api") -public class ProductApiController implements ProductController { - - private final ProductService productService; - - public ProductApiController(final ProductService productService) { - this.productService = productService; - } - - @GetMapping("/categories/{categoryId}/products") - public ResponseEntity getAllProductsInCategory(@PathVariable final Long categoryId, - @RequestParam final Long lastProductId, - @RequestParam final String sort) { - final ProductSortCondition sortCondition = ProductSortCondition.toDto(sort); - final ProductsInCategoryResponse response = productService.getAllProductsInCategory(categoryId, lastProductId, sortCondition); - return ResponseEntity.ok(response); - } - - @GetMapping("/products/{productId}") - public ResponseEntity getProductDetail(@PathVariable final Long productId) { - final ProductResponse response = productService.findProductDetail(productId); - return ResponseEntity.ok(response); - } - - @GetMapping("/ranks/products") - public ResponseEntity getRankingProducts() { - final RankingProductsResponse response = productService.getTop3Products(); - return ResponseEntity.ok(response); - } - - @GetMapping("/search/products") - public ResponseEntity searchProducts(@RequestParam final String query, - @PageableDefault final Pageable pageable) { - final PageRequest pageRequest = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize()); - final SearchProductsResponse response = productService.searchProducts(query, pageRequest); - return ResponseEntity.ok(response); - } - - @GetMapping("/search/products/results") - public ResponseEntity getSearchResults(@RequestParam final String query, - @PageableDefault final Pageable pageable) { - final PageRequest pageRequest = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize()); - final SearchProductResultsResponse response = productService.getSearchResults(query, pageRequest); - return ResponseEntity.ok(response); - } - - @GetMapping("/products/{productId}/recipes") - public ResponseEntity getProductRecipes(@PathVariable final Long productId, - @PageableDefault final Pageable pageable) { - final SortingRecipesResponse response = productService.getProductRecipes(productId, pageable); - return ResponseEntity.ok(response); - } -} diff --git a/backend/src/main/java/com/funeat/product/presentation/ProductController.java b/backend/src/main/java/com/funeat/product/presentation/ProductController.java deleted file mode 100644 index 3fa704eb7..000000000 --- a/backend/src/main/java/com/funeat/product/presentation/ProductController.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.funeat.product.presentation; - -import com.funeat.product.dto.ProductResponse; -import com.funeat.product.dto.ProductsInCategoryResponse; -import com.funeat.product.dto.RankingProductsResponse; -import com.funeat.product.dto.SearchProductResultsResponse; -import com.funeat.product.dto.SearchProductsResponse; -import com.funeat.recipe.dto.SortingRecipesResponse; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestParam; - -@Tag(name = "01.Product", description = "상품 기능") -public interface ProductController { - - @Operation(summary = "해당 카테고리 상품 조회", description = "해당 카테고리의 상품을 조회한다.") - @ApiResponse( - responseCode = "200", - description = "해당 카테고리 상품 조회 성공." - ) - @GetMapping - ResponseEntity getAllProductsInCategory( - @PathVariable final Long categoryId, - @RequestParam final Long lastProductId, - @RequestParam final String sort - ); - - @Operation(summary = "해당 상품 상세 조회", description = "해당 상품 상세정보를 조회한다.") - @ApiResponse( - responseCode = "200", - description = "해당 상품 상세 조회 성공." - ) - @GetMapping - ResponseEntity getProductDetail(@PathVariable final Long productId); - - @Operation(summary = "전체 상품 랭킹 조회", description = "전체 상품들 중에서 랭킹 TOP3를 조회한다.") - @ApiResponse( - responseCode = "200", - description = "전체 상품 랭킹 조회 성공." - ) - @GetMapping - ResponseEntity getRankingProducts(); - - @Operation(summary = "상품 자동 완성 검색", description = "공통 상품 및 PB 상품들의 이름 중에 해당하는 문자열이 포함되어 있는지 검색한다.") - @ApiResponse( - responseCode = "200", - description = "상품 검색 성공." - ) - @GetMapping - ResponseEntity searchProducts(@RequestParam final String query, - @PageableDefault final Pageable pageable); - - @Operation(summary = "상품 검색 결과 조회", description = "문자열을 받아 상품을 검색하고 검색 결과들을 조회한다.") - @ApiResponse( - responseCode = "200", - description = "상품 검색 결과 조회 성공." - ) - @GetMapping - ResponseEntity getSearchResults(@RequestParam final String query, - @PageableDefault final Pageable pageable); - - @Operation(summary = "해당 상품 꿀조합 목록 조회", description = "해당 상품이 포함된 꿀조합 목록을 조회한다.") - @ApiResponse( - responseCode = "200", - description = "해당 상품 꿀조합 목록 조회 성공." - ) - @GetMapping - ResponseEntity getProductRecipes(@PathVariable final Long productId, - @PageableDefault final Pageable pageable); -} diff --git a/backend/src/main/java/com/funeat/recipe/application/RecipeService.java b/backend/src/main/java/com/funeat/recipe/application/RecipeService.java deleted file mode 100644 index 19545b20b..000000000 --- a/backend/src/main/java/com/funeat/recipe/application/RecipeService.java +++ /dev/null @@ -1,282 +0,0 @@ -package com.funeat.recipe.application; - -import static com.funeat.member.exception.MemberErrorCode.MEMBER_DUPLICATE_FAVORITE; -import static com.funeat.member.exception.MemberErrorCode.MEMBER_NOT_FOUND; -import static com.funeat.product.exception.ProductErrorCode.PRODUCT_NOT_FOUND; -import static com.funeat.recipe.exception.RecipeErrorCode.RECIPE_NOT_FOUND; - -import com.funeat.comment.domain.Comment; -import com.funeat.comment.persistence.CommentRepository; -import com.funeat.comment.specification.CommentSpecification; -import com.funeat.common.ImageUploader; -import com.funeat.common.dto.PageDto; -import com.funeat.member.domain.Member; -import com.funeat.member.domain.favorite.RecipeFavorite; -import com.funeat.member.dto.MemberRecipeDto; -import com.funeat.member.dto.MemberRecipeProductDto; -import com.funeat.member.dto.MemberRecipesResponse; -import com.funeat.member.exception.MemberException.MemberDuplicateFavoriteException; -import com.funeat.member.exception.MemberException.MemberNotFoundException; -import com.funeat.member.persistence.MemberRepository; -import com.funeat.member.persistence.RecipeFavoriteRepository; -import com.funeat.product.domain.Product; -import com.funeat.product.domain.ProductRecipe; -import com.funeat.product.exception.ProductException.ProductNotFoundException; -import com.funeat.product.persistence.ProductRecipeRepository; -import com.funeat.product.persistence.ProductRepository; -import com.funeat.recipe.domain.Recipe; -import com.funeat.recipe.domain.RecipeImage; -import com.funeat.recipe.dto.RankingRecipeDto; -import com.funeat.recipe.dto.RankingRecipesResponse; -import com.funeat.recipe.dto.RecipeAuthorDto; -import com.funeat.recipe.dto.RecipeCommentCondition; -import com.funeat.recipe.dto.RecipeCommentCreateRequest; -import com.funeat.recipe.dto.RecipeCommentResponse; -import com.funeat.recipe.dto.RecipeCommentsResponse; -import com.funeat.recipe.dto.RecipeCreateRequest; -import com.funeat.recipe.dto.RecipeDetailResponse; -import com.funeat.recipe.dto.RecipeDto; -import com.funeat.recipe.dto.RecipeFavoriteRequest; -import com.funeat.recipe.dto.SearchRecipeResultDto; -import com.funeat.recipe.dto.SearchRecipeResultsResponse; -import com.funeat.recipe.dto.SortingRecipesResponse; -import com.funeat.recipe.exception.RecipeException.RecipeNotFoundException; -import com.funeat.recipe.persistence.RecipeImageRepository; -import com.funeat.recipe.persistence.RecipeRepository; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; - -@Service -@Transactional(readOnly = true) -public class RecipeService { - - private static final long RANKING_MINIMUM_FAVORITE_COUNT = 1L; - private static final int RANKING_SIZE = 3; - private static final int RECIPE_COMMENT_PAGE_SIZE = 10; - private static final int DEFAULT_CURSOR_PAGINATION_SIZE = 11; - - private final MemberRepository memberRepository; - private final ProductRepository productRepository; - private final ProductRecipeRepository productRecipeRepository; - private final RecipeRepository recipeRepository; - private final RecipeImageRepository recipeImageRepository; - private final RecipeFavoriteRepository recipeFavoriteRepository; - private final CommentRepository commentRepository; - private final ImageUploader imageUploader; - - public RecipeService(final MemberRepository memberRepository, final ProductRepository productRepository, - final ProductRecipeRepository productRecipeRepository, final RecipeRepository recipeRepository, - final RecipeImageRepository recipeImageRepository, - final RecipeFavoriteRepository recipeFavoriteRepository, - final CommentRepository commentRepository, final ImageUploader imageUploader) { - this.memberRepository = memberRepository; - this.productRepository = productRepository; - this.productRecipeRepository = productRecipeRepository; - this.recipeRepository = recipeRepository; - this.recipeImageRepository = recipeImageRepository; - this.recipeFavoriteRepository = recipeFavoriteRepository; - this.commentRepository = commentRepository; - this.imageUploader = imageUploader; - } - - @Transactional - public Long create(final Long memberId, final List images, final RecipeCreateRequest request) { - final Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId)); - - final Recipe savedRecipe = recipeRepository.save(new Recipe(request.getTitle(), request.getContent(), member)); - request.getProductIds() - .stream() - .map(productId -> productRepository.findById(productId) - .orElseThrow(() -> new ProductNotFoundException(PRODUCT_NOT_FOUND, productId))) - .forEach(product -> productRecipeRepository.save(new ProductRecipe(product, savedRecipe))); - - if (Objects.nonNull(images)) { - images.forEach(image -> { - final String imageUrl = imageUploader.upload(image); - recipeImageRepository.save(new RecipeImage(imageUrl, savedRecipe)); - }); - } - - return savedRecipe.getId(); - } - - public RecipeDetailResponse getRecipeDetail(final Long memberId, final Long recipeId) { - final Recipe recipe = recipeRepository.findById(recipeId) - .orElseThrow(() -> new RecipeNotFoundException(RECIPE_NOT_FOUND, recipeId)); - final List images = recipeImageRepository.findByRecipe(recipe); - final List products = productRecipeRepository.findProductByRecipe(recipe); - final Long totalPrice = products.stream() - .mapToLong(Product::getPrice) - .sum(); - - final Boolean favorite = calculateFavoriteChecked(memberId, recipe); - - return RecipeDetailResponse.toResponse(recipe, images, products, totalPrice, favorite); - } - - private Boolean calculateFavoriteChecked(final Long memberId, final Recipe recipe) { - final Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId)); - return recipeFavoriteRepository.existsByMemberAndRecipeAndFavoriteTrue(member, recipe); - } - - public MemberRecipesResponse findRecipeByMember(final Long memberId, final Pageable pageable) { - final Member findMember = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId)); - - final Page sortedRecipePages = recipeRepository.findRecipesByMember(findMember, pageable); - - final PageDto page = PageDto.toDto(sortedRecipePages); - final List dtos = sortedRecipePages.stream() - .map(recipe -> { - final List findRecipeImages = recipeImageRepository.findByRecipe(recipe); - final List productsByRecipe = productRecipeRepository.findProductByRecipe(recipe); - final List memberRecipeProductDtos = productsByRecipe.stream() - .map(MemberRecipeProductDto::toDto) - .collect(Collectors.toList()); - return MemberRecipeDto.toDto(recipe, findRecipeImages, memberRecipeProductDtos); - }) - .collect(Collectors.toList()); - - return MemberRecipesResponse.toResponse(page, dtos); - } - - public SortingRecipesResponse getSortingRecipes(final Pageable pageable) { - final Page pages = recipeRepository.findAll(pageable); - - final PageDto page = PageDto.toDto(pages); - final List recipes = pages.getContent().stream() - .map(recipe -> { - final List images = recipeImageRepository.findByRecipe(recipe); - final List products = productRecipeRepository.findProductByRecipe(recipe); - return RecipeDto.toDto(recipe, images, products); - }) - .collect(Collectors.toList()); - - return SortingRecipesResponse.toResponse(page, recipes); - } - - @Transactional - public void likeRecipe(final Long memberId, final Long recipeId, final RecipeFavoriteRequest request) { - final Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId)); - final Recipe recipe = recipeRepository.findByIdForUpdate(recipeId) - .orElseThrow(() -> new RecipeNotFoundException(RECIPE_NOT_FOUND, recipeId)); - - final RecipeFavorite recipeFavorite = recipeFavoriteRepository.findByMemberAndRecipe(member, recipe) - .orElseGet(() -> createAndSaveRecipeFavorite(member, recipe, request.getFavorite())); - - recipeFavorite.updateFavorite(request.getFavorite()); - } - - private RecipeFavorite createAndSaveRecipeFavorite(final Member member, final Recipe recipe, - final Boolean favorite) { - try { - final RecipeFavorite recipeFavorite = RecipeFavorite.create(member, recipe, favorite); - return recipeFavoriteRepository.save(recipeFavorite); - } catch (final DataIntegrityViolationException e) { - throw new MemberDuplicateFavoriteException(MEMBER_DUPLICATE_FAVORITE, member.getId()); - } - } - - public SearchRecipeResultsResponse getSearchResults(final String query, final Pageable pageable) { - final Page recipePages = recipeRepository.findAllByProductNameContaining(query, pageable); - - final PageDto page = PageDto.toDto(recipePages); - final List dtos = recipePages.stream() - .map(recipe -> { - final List findRecipeImages = recipeImageRepository.findByRecipe(recipe); - final List productsByRecipe = productRecipeRepository.findProductByRecipe(recipe); - return SearchRecipeResultDto.toDto(recipe, findRecipeImages, productsByRecipe); - }) - .collect(Collectors.toList()); - return SearchRecipeResultsResponse.toResponse(page, dtos); - } - - public RankingRecipesResponse getTop3Recipes() { - final List recipes = recipeRepository.findRecipesByFavoriteCountGreaterThanEqual(RANKING_MINIMUM_FAVORITE_COUNT); - - final List dtos = recipes.stream() - .sorted(Comparator.comparing(Recipe::calculateRankingScore).reversed()) - .limit(RANKING_SIZE) - .map(recipe -> { - final List findRecipeImages = recipeImageRepository.findByRecipe(recipe); - final RecipeAuthorDto author = RecipeAuthorDto.toDto(recipe.getMember()); - return RankingRecipeDto.toDto(recipe, findRecipeImages, author); - }) - .collect(Collectors.toList()); - return RankingRecipesResponse.toResponse(dtos); - } - - @Transactional - public Long writeCommentOfRecipe(final Long memberId, final Long recipeId, - final RecipeCommentCreateRequest request) { - final Member findMember = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId)); - - final Recipe findRecipe = recipeRepository.findById(recipeId) - .orElseThrow(() -> new RecipeNotFoundException(RECIPE_NOT_FOUND, recipeId)); - - final Comment comment = new Comment(findRecipe, findMember, request.getComment()); - - final Comment savedComment = commentRepository.save(comment); - return savedComment.getId(); - } - - public RecipeCommentsResponse getCommentsOfRecipe(final Long recipeId, final RecipeCommentCondition condition) { - final Recipe findRecipe = recipeRepository.findById(recipeId) - .orElseThrow(() -> new RecipeNotFoundException(RECIPE_NOT_FOUND, recipeId)); - - final Specification specification = CommentSpecification.findAllByRecipe(findRecipe, - condition.getLastId()); - - final PageRequest pageable = PageRequest.of(0, DEFAULT_CURSOR_PAGINATION_SIZE, Sort.by("id").descending()); - - final Page commentPaginationResult = commentRepository.findAllForPagination(specification, pageable, - condition.getTotalElements()); - - final List recipeCommentResponses = getRecipeCommentResponses( - commentPaginationResult.getContent()); - - final Boolean hasNext = hasNextPage(commentPaginationResult); - - return RecipeCommentsResponse.toResponse(recipeCommentResponses, hasNext, - commentPaginationResult.getTotalElements()); - } - - private List getRecipeCommentResponses(final List findComments) { - final List recipeCommentResponses = new ArrayList<>(); - final int resultSize = getResultSize(findComments); - final List comments = findComments.subList(0, resultSize); - - for (final Comment comment : comments) { - final RecipeCommentResponse recipeCommentResponse = RecipeCommentResponse.toResponse(comment); - recipeCommentResponses.add(recipeCommentResponse); - } - return recipeCommentResponses; - } - - private int getResultSize(final List findComments) { - if (findComments.size() < DEFAULT_CURSOR_PAGINATION_SIZE) { - return findComments.size(); - } - return RECIPE_COMMENT_PAGE_SIZE; - } - - private Boolean hasNextPage(final Page findComments) { - return findComments.getContent().size() > RECIPE_COMMENT_PAGE_SIZE; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/domain/Recipe.java b/backend/src/main/java/com/funeat/recipe/domain/Recipe.java deleted file mode 100644 index 5ffb0438b..000000000 --- a/backend/src/main/java/com/funeat/recipe/domain/Recipe.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.funeat.recipe.domain; - -import com.funeat.member.domain.Member; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; - -@Entity -public class Recipe { - - private static final double RANKING_GRAVITY = 0.1; - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String title; - - private String content; - - @Column(nullable = false) - private LocalDateTime createdAt = LocalDateTime.now(); - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") - private Member member; - - private Long favoriteCount = 0L; - - protected Recipe() { - } - - public Recipe(final String title, final String content, final Member member) { - this.title = title; - this.content = content; - this.member = member; - } - - public Recipe(final String title, final String content, final Member member, - final Long favoriteCount) { - this.title = title; - this.content = content; - this.member = member; - this.favoriteCount = favoriteCount; - } - - public Recipe(final String title, final String content, final Member member, final Long favoriteCount, - final LocalDateTime createdAt) { - this.title = title; - this.content = content; - this.member = member; - this.favoriteCount = favoriteCount; - this.createdAt = createdAt; - } - - public Double calculateRankingScore() { - final long age = ChronoUnit.DAYS.between(createdAt, LocalDateTime.now()); - final double denominator = Math.pow(age + 1.0, RANKING_GRAVITY); - return favoriteCount / denominator; - } - - public void addFavoriteCount() { - this.favoriteCount++; - } - - public void minusFavoriteCount() { - this.favoriteCount--; - } - - public Long getId() { - return id; - } - - public String getTitle() { - return title; - } - - public String getContent() { - return content; - } - - public Member getMember() { - return member; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/domain/RecipeImage.java b/backend/src/main/java/com/funeat/recipe/domain/RecipeImage.java deleted file mode 100644 index 0dffc61ee..000000000 --- a/backend/src/main/java/com/funeat/recipe/domain/RecipeImage.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.funeat.recipe.domain; - -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; - -@Entity -public class RecipeImage { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String image; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "recipe_id") - private Recipe recipe; - - protected RecipeImage() { - } - - public RecipeImage(final String image, final Recipe recipe) { - this.image = image; - this.recipe = recipe; - } - - public Long getId() { - return id; - } - - public String getImage() { - return image; - } - - public Recipe getRecipe() { - return recipe; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/ProductRecipeDto.java b/backend/src/main/java/com/funeat/recipe/dto/ProductRecipeDto.java deleted file mode 100644 index 60ab88ea9..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/ProductRecipeDto.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.funeat.recipe.dto; - -import com.funeat.product.domain.Product; - -public class ProductRecipeDto { - - private final Long id; - private final String name; - private final Long price; - - private ProductRecipeDto(final Long id, final String name, final Long price) { - this.id = id; - this.name = name; - this.price = price; - } - - public static ProductRecipeDto toDto(final Product product) { - return new ProductRecipeDto(product.getId(), product.getName(), product.getPrice()); - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public Long getPrice() { - return price; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/RankingRecipeDto.java b/backend/src/main/java/com/funeat/recipe/dto/RankingRecipeDto.java deleted file mode 100644 index 3f26a69ca..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/RankingRecipeDto.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.funeat.recipe.dto; - -import com.funeat.recipe.domain.Recipe; -import com.funeat.recipe.domain.RecipeImage; -import java.time.LocalDateTime; -import java.util.List; - -public class RankingRecipeDto { - - private final Long id; - private final String image; - private final String title; - private final RecipeAuthorDto author; - private final Long favoriteCount; - private final LocalDateTime createdAt; - - public RankingRecipeDto(final Long id, final String image, final String title, final RecipeAuthorDto author, - final Long favoriteCount, final LocalDateTime createdAt) { - this.id = id; - this.image = image; - this.title = title; - this.author = author; - this.favoriteCount = favoriteCount; - this.createdAt = createdAt; - } - - public static RankingRecipeDto toDto(final Recipe recipe, final List images, - final RecipeAuthorDto author) { - if (images.isEmpty()) { - return new RankingRecipeDto(recipe.getId(), null, recipe.getTitle(), author, - recipe.getFavoriteCount(), recipe.getCreatedAt()); - } - return new RankingRecipeDto(recipe.getId(), images.get(0).getImage(), recipe.getTitle(), author, - recipe.getFavoriteCount(), recipe.getCreatedAt()); - } - - public Long getId() { - return id; - } - - public String getImage() { - return image; - } - - public String getTitle() { - return title; - } - - public RecipeAuthorDto getAuthor() { - return author; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/RankingRecipesResponse.java b/backend/src/main/java/com/funeat/recipe/dto/RankingRecipesResponse.java deleted file mode 100644 index d446d340e..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/RankingRecipesResponse.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.funeat.recipe.dto; - -import java.util.List; - -public class RankingRecipesResponse { - - private final List recipes; - - public RankingRecipesResponse(final List recipes) { - this.recipes = recipes; - } - - public static RankingRecipesResponse toResponse(final List recipes) { - return new RankingRecipesResponse(recipes); - } - - public List getRecipes() { - return recipes; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/RecipeAuthorDto.java b/backend/src/main/java/com/funeat/recipe/dto/RecipeAuthorDto.java deleted file mode 100644 index 881cca1da..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/RecipeAuthorDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.funeat.recipe.dto; - -import com.funeat.member.domain.Member; - -public class RecipeAuthorDto { - - private final String profileImage; - private final String nickname; - - private RecipeAuthorDto(final String profileImage, final String nickname) { - this.profileImage = profileImage; - this.nickname = nickname; - } - - public static RecipeAuthorDto toDto(final Member member) { - return new RecipeAuthorDto(member.getProfileImage(), member.getNickname()); - } - - public String getProfileImage() { - return profileImage; - } - - public String getNickname() { - return nickname; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentCondition.java b/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentCondition.java deleted file mode 100644 index dcb3cf2d1..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentCondition.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.funeat.recipe.dto; - -public class RecipeCommentCondition { - - private final Long lastId; - private final Long totalElements; - - public RecipeCommentCondition(final Long lastId, final Long totalElements) { - this.lastId = lastId; - this.totalElements = totalElements; - } - - public Long getLastId() { - return lastId; - } - - public Long getTotalElements() { - return totalElements; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentCreateRequest.java b/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentCreateRequest.java deleted file mode 100644 index 2b24e9207..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentCreateRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.funeat.recipe.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; - -public class RecipeCommentCreateRequest { - - @NotBlank(message = "꿀조합 댓글을 확인해 주세요") - @Size(max = 200, message = "꿀조합 댓글은 최대 200자까지 입력 가능합니다") - private final String comment; - - public RecipeCommentCreateRequest(@JsonProperty("comment") final String comment) { - this.comment = comment; - } - - public String getComment() { - return comment; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentMemberResponse.java b/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentMemberResponse.java deleted file mode 100644 index ad66d7811..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentMemberResponse.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.funeat.recipe.dto; - -import com.funeat.member.domain.Member; - -public class RecipeCommentMemberResponse { - - private final String nickname; - private final String profileImage; - - private RecipeCommentMemberResponse(final String nickname, final String profileImage) { - this.nickname = nickname; - this.profileImage = profileImage; - } - - public static RecipeCommentMemberResponse toResponse(final Member member) { - return new RecipeCommentMemberResponse(member.getNickname(), member.getProfileImage()); - } - - public String getNickname() { - return nickname; - } - - public String getProfileImage() { - return profileImage; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentResponse.java b/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentResponse.java deleted file mode 100644 index 989e52bd5..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentResponse.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.funeat.recipe.dto; - -import com.funeat.comment.domain.Comment; -import java.time.LocalDateTime; - -public class RecipeCommentResponse { - - private final Long id; - private final String comment; - private final LocalDateTime createdAt; - private final RecipeCommentMemberResponse author; - - private RecipeCommentResponse(final Long id, final String comment, final LocalDateTime createdAt, - final RecipeCommentMemberResponse author) { - this.id = id; - this.comment = comment; - this.createdAt = createdAt; - this.author = author; - } - - public static RecipeCommentResponse toResponse(final Comment comment) { - final RecipeCommentMemberResponse author = RecipeCommentMemberResponse.toResponse(comment.getMember()); - - return new RecipeCommentResponse(comment.getId(), comment.getComment(), comment.getCreatedAt(), author); - } - - public Long getId() { - return id; - } - - public String getComment() { - return comment; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public RecipeCommentMemberResponse getAuthor() { - return author; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentsResponse.java b/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentsResponse.java deleted file mode 100644 index 7e7d6dc19..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/RecipeCommentsResponse.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.funeat.recipe.dto; - -import java.util.List; - -public class RecipeCommentsResponse { - - private final List comments; - private final boolean hasNext; - private final Long totalElements; - - private RecipeCommentsResponse(final List comments, final boolean hasNext, - final Long totalElements) { - this.comments = comments; - this.hasNext = hasNext; - this.totalElements = totalElements; - } - - public static RecipeCommentsResponse toResponse(final List comments, final boolean hasNext, - final Long totalElements) { - return new RecipeCommentsResponse(comments, hasNext, totalElements); - } - - public List getComments() { - return comments; - } - - public boolean getHasNext() { - return hasNext; - } - - public Long getTotalElements() { - return totalElements; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/RecipeCreateRequest.java b/backend/src/main/java/com/funeat/recipe/dto/RecipeCreateRequest.java deleted file mode 100644 index 201c28cf0..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/RecipeCreateRequest.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.funeat.recipe.dto; - -import java.util.List; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -public class RecipeCreateRequest { - - @NotBlank(message = "꿀조합 이름을 확인해 주세요") - private final String title; - - @NotNull(message = "상품 ID 목록을 확인해 주세요") - @Size(min = 1, message = "적어도 1개의 상품 ID가 필요합니다") - private final List productIds; - - @NotBlank(message = "꿀조합 내용을 확인해 주세요") - @Size(max = 500, message = "꿀조합 내용은 최대 500자까지 입력 가능합니다") - private final String content; - - public RecipeCreateRequest(final String title, final List productIds, final String content) { - this.title = title; - this.productIds = productIds; - this.content = content; - } - - public String getTitle() { - return title; - } - - public List getProductIds() { - return productIds; - } - - public String getContent() { - return content; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/RecipeDetailResponse.java b/backend/src/main/java/com/funeat/recipe/dto/RecipeDetailResponse.java deleted file mode 100644 index 6ccef9410..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/RecipeDetailResponse.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.funeat.recipe.dto; - -import com.funeat.product.domain.Product; -import com.funeat.recipe.domain.Recipe; -import com.funeat.recipe.domain.RecipeImage; -import java.time.LocalDateTime; -import java.util.List; -import java.util.stream.Collectors; - -public class RecipeDetailResponse { - - private final Long id; - private final List images; - private final String title; - private final String content; - private final RecipeAuthorDto author; - private final List products; - private final Long totalPrice; - private final Long favoriteCount; - private final Boolean favorite; - private final LocalDateTime createdAt; - - public RecipeDetailResponse(final Long id, final List images, final String title, final String content, - final RecipeAuthorDto author, final List products, - final Long totalPrice, final Long favoriteCount, final Boolean favorite, - final LocalDateTime createdAt) { - this.id = id; - this.images = images; - this.title = title; - this.content = content; - this.author = author; - this.products = products; - this.totalPrice = totalPrice; - this.favoriteCount = favoriteCount; - this.favorite = favorite; - this.createdAt = createdAt; - } - - public static RecipeDetailResponse toResponse(final Recipe recipe, final List recipeImages, - final List products, final Long totalPrice, - final Boolean favorite) { - final RecipeAuthorDto authorDto = RecipeAuthorDto.toDto(recipe.getMember()); - final List productDtos = products.stream() - .map(ProductRecipeDto::toDto) - .collect(Collectors.toList()); - final List images = recipeImages.stream() - .map(RecipeImage::getImage) - .collect(Collectors.toList()); - return new RecipeDetailResponse(recipe.getId(), images, recipe.getTitle(), recipe.getContent(), - authorDto, productDtos, totalPrice, recipe.getFavoriteCount(), favorite, recipe.getCreatedAt()); - } - - public Long getId() { - return id; - } - - public List getImages() { - return images; - } - - public String getTitle() { - return title; - } - - public String getContent() { - return content; - } - - public RecipeAuthorDto getAuthor() { - return author; - } - - public List getProducts() { - return products; - } - - public Long getTotalPrice() { - return totalPrice; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public Boolean getFavorite() { - return favorite; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/RecipeDto.java b/backend/src/main/java/com/funeat/recipe/dto/RecipeDto.java deleted file mode 100644 index 51cd78fc2..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/RecipeDto.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.funeat.recipe.dto; - -import com.funeat.product.domain.Product; -import com.funeat.recipe.domain.Recipe; -import com.funeat.recipe.domain.RecipeImage; -import java.time.LocalDateTime; -import java.util.List; -import java.util.stream.Collectors; - -public class RecipeDto { - - private final Long id; - private final String image; - private final String title; - private final RecipeAuthorDto author; - private final List products; - private final Long favoriteCount; - private final LocalDateTime createdAt; - - public RecipeDto(final Long id, final String image, final String title, final RecipeAuthorDto author, - final List products, - final Long favoriteCount, final LocalDateTime createdAt) { - this.id = id; - this.image = image; - this.title = title; - this.author = author; - this.products = products; - this.favoriteCount = favoriteCount; - this.createdAt = createdAt; - } - - public static RecipeDto toDto(final Recipe recipe, final List recipeImages, - final List products) { - final RecipeAuthorDto authorDto = RecipeAuthorDto.toDto(recipe.getMember()); - final List productDtos = products.stream() - .map(ProductRecipeDto::toDto) - .collect(Collectors.toList()); - if (recipeImages.isEmpty()) { - return new RecipeDto(recipe.getId(), null, recipe.getTitle(), authorDto, productDtos, - recipe.getFavoriteCount(), recipe.getCreatedAt()); - } - return new RecipeDto(recipe.getId(), recipeImages.get(0).getImage(), recipe.getTitle(), authorDto, productDtos, - recipe.getFavoriteCount(), recipe.getCreatedAt()); - } - - public Long getId() { - return id; - } - - public String getImage() { - return image; - } - - public String getTitle() { - return title; - } - - public RecipeAuthorDto getAuthor() { - return author; - } - - public List getProducts() { - return products; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/RecipeFavoriteRequest.java b/backend/src/main/java/com/funeat/recipe/dto/RecipeFavoriteRequest.java deleted file mode 100644 index f82ada428..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/RecipeFavoriteRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.funeat.recipe.dto; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import javax.validation.constraints.NotNull; - -public class RecipeFavoriteRequest { - - @NotNull(message = "좋아요를 확인해주세요") - private final Boolean favorite; - - @JsonCreator - public RecipeFavoriteRequest(@JsonProperty("favorite") final Boolean favorite) { - this.favorite = favorite; - } - - public Boolean getFavorite() { - return favorite; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/SearchRecipeResultDto.java b/backend/src/main/java/com/funeat/recipe/dto/SearchRecipeResultDto.java deleted file mode 100644 index 0feb4b71d..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/SearchRecipeResultDto.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.funeat.recipe.dto; - -import com.funeat.product.domain.Product; -import com.funeat.recipe.domain.Recipe; -import com.funeat.recipe.domain.RecipeImage; -import java.time.LocalDateTime; -import java.util.List; -import java.util.stream.Collectors; - -public class SearchRecipeResultDto { - - private final Long id; - private final String image; - private final String title; - private final RecipeAuthorDto author; - private final List products; - private final Long favoriteCount; - private final LocalDateTime createdAt; - - public SearchRecipeResultDto(final Long id, final String image, final String title, final RecipeAuthorDto author, - final List products, final Long favoriteCount, - final LocalDateTime createdAt) { - this.id = id; - this.image = image; - this.title = title; - this.author = author; - this.products = products; - this.favoriteCount = favoriteCount; - this.createdAt = createdAt; - } - - public static SearchRecipeResultDto toDto(final Recipe recipe, final List images, - final List products) { - final List productRecipes = products.stream() - .map(ProductRecipeDto::toDto) - .collect(Collectors.toList()); - - if (images.isEmpty()) { - return new SearchRecipeResultDto(recipe.getId(), null, recipe.getTitle(), - RecipeAuthorDto.toDto(recipe.getMember()), productRecipes, recipe.getFavoriteCount(), - recipe.getCreatedAt()); - } - return new SearchRecipeResultDto(recipe.getId(), images.get(0).getImage(), recipe.getTitle(), - RecipeAuthorDto.toDto(recipe.getMember()), productRecipes, recipe.getFavoriteCount(), - recipe.getCreatedAt()); - } - - public Long getId() { - return id; - } - - public String getImage() { - return image; - } - - public String getTitle() { - return title; - } - - public RecipeAuthorDto getAuthor() { - return author; - } - - public List getProducts() { - return products; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/SearchRecipeResultsResponse.java b/backend/src/main/java/com/funeat/recipe/dto/SearchRecipeResultsResponse.java deleted file mode 100644 index 3fb88dc53..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/SearchRecipeResultsResponse.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.funeat.recipe.dto; - -import com.funeat.common.dto.PageDto; -import java.util.List; - -public class SearchRecipeResultsResponse { - - private final PageDto page; - private final List recipes; - - public SearchRecipeResultsResponse(final PageDto page, final List recipes) { - this.page = page; - this.recipes = recipes; - } - - public static SearchRecipeResultsResponse toResponse(final PageDto page, - final List recipes) { - return new SearchRecipeResultsResponse(page, recipes); - } - - public PageDto getPage() { - return page; - } - - public List getRecipes() { - return recipes; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/dto/SortingRecipesResponse.java b/backend/src/main/java/com/funeat/recipe/dto/SortingRecipesResponse.java deleted file mode 100644 index e6e6003f0..000000000 --- a/backend/src/main/java/com/funeat/recipe/dto/SortingRecipesResponse.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.funeat.recipe.dto; - -import com.funeat.common.dto.PageDto; -import java.util.List; - -public class SortingRecipesResponse { - - private final PageDto page; - private final List recipes; - - public SortingRecipesResponse(final PageDto page, final List recipes) { - this.page = page; - this.recipes = recipes; - } - - public static SortingRecipesResponse toResponse(final PageDto page, final List recipes) { - return new SortingRecipesResponse(page, recipes); - } - - public PageDto getPage() { - return page; - } - - public List getRecipes() { - return recipes; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/exception/RecipeErrorCode.java b/backend/src/main/java/com/funeat/recipe/exception/RecipeErrorCode.java deleted file mode 100644 index 887da8864..000000000 --- a/backend/src/main/java/com/funeat/recipe/exception/RecipeErrorCode.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.funeat.recipe.exception; - -import org.springframework.http.HttpStatus; - -public enum RecipeErrorCode { - - RECIPE_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 꿀조합입니다. 꿀조합 id를 확인하세요.", "7001"), - ; - - private final HttpStatus status; - private final String message; - private final String code; - - RecipeErrorCode(final HttpStatus status, final String message, final String code) { - this.status = status; - this.message = message; - this.code = code; - } - - public HttpStatus getStatus() { - return status; - } - - public String getMessage() { - return message; - } - - public String getCode() { - return code; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/exception/RecipeException.java b/backend/src/main/java/com/funeat/recipe/exception/RecipeException.java deleted file mode 100644 index f7352c746..000000000 --- a/backend/src/main/java/com/funeat/recipe/exception/RecipeException.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.funeat.recipe.exception; - -import com.funeat.exception.ErrorCode; -import com.funeat.exception.GlobalException; -import org.springframework.http.HttpStatus; - -public class RecipeException extends GlobalException { - - public RecipeException(final HttpStatus status, final ErrorCode errorCode) { - super(status, errorCode); - } - - public static class RecipeNotFoundException extends RecipeException { - public RecipeNotFoundException(final RecipeErrorCode errorCode, final Long RecipeId) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), RecipeId)); - } - } -} diff --git a/backend/src/main/java/com/funeat/recipe/persistence/RecipeImageRepository.java b/backend/src/main/java/com/funeat/recipe/persistence/RecipeImageRepository.java deleted file mode 100644 index 04d2ca6a1..000000000 --- a/backend/src/main/java/com/funeat/recipe/persistence/RecipeImageRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.funeat.recipe.persistence; - -import com.funeat.recipe.domain.Recipe; -import com.funeat.recipe.domain.RecipeImage; -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface RecipeImageRepository extends JpaRepository { - - List findByRecipe(final Recipe recipe); -} diff --git a/backend/src/main/java/com/funeat/recipe/persistence/RecipeRepository.java b/backend/src/main/java/com/funeat/recipe/persistence/RecipeRepository.java deleted file mode 100644 index ce5ef3c31..000000000 --- a/backend/src/main/java/com/funeat/recipe/persistence/RecipeRepository.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.funeat.recipe.persistence; - -import static javax.persistence.LockModeType.PESSIMISTIC_WRITE; - -import com.funeat.member.domain.Member; -import com.funeat.product.domain.Product; -import com.funeat.recipe.domain.Recipe; -import java.util.List; -import java.util.Optional; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Lock; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -public interface RecipeRepository extends JpaRepository { - - @Query("SELECT r FROM Recipe r " - + "JOIN FETCH r.member m") - Recipe findRecipeWithMemberById(final Long id); - - Page findRecipesByMember(final Member member, final Pageable pageable); - - @Query("SELECT DISTINCT r FROM Recipe r " - + "LEFT JOIN ProductRecipe pr ON pr.recipe.id = r.id " - + "WHERE pr.product.name LIKE CONCAT('%', :name, '%')") - Page findAllByProductNameContaining(@Param("name") final String name, final Pageable pageable); - - Page findAll(final Pageable pageable); - - @Query("SELECT r FROM Recipe r LEFT JOIN ProductRecipe pr ON pr.product = :product WHERE pr.recipe.id = r.id") - Page findRecipesByProduct(final Product product, final Pageable pageable); - - @Lock(PESSIMISTIC_WRITE) - @Query("SELECT r FROM Recipe r WHERE r.id=:id") - Optional findByIdForUpdate(final Long id); - - List findRecipesByFavoriteCountGreaterThanEqual(final Long favoriteCount); -} diff --git a/backend/src/main/java/com/funeat/recipe/presentation/RecipeApiController.java b/backend/src/main/java/com/funeat/recipe/presentation/RecipeApiController.java deleted file mode 100644 index 17eb1f1d6..000000000 --- a/backend/src/main/java/com/funeat/recipe/presentation/RecipeApiController.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.funeat.recipe.presentation; - -import com.funeat.auth.dto.LoginInfo; -import com.funeat.auth.util.AuthenticationPrincipal; -import com.funeat.common.logging.Logging; -import com.funeat.recipe.application.RecipeService; -import com.funeat.recipe.dto.RankingRecipesResponse; -import com.funeat.recipe.dto.RecipeCommentCondition; -import com.funeat.recipe.dto.RecipeCommentCreateRequest; -import com.funeat.recipe.dto.RecipeCommentsResponse; -import com.funeat.recipe.dto.RecipeCreateRequest; -import com.funeat.recipe.dto.RecipeDetailResponse; -import com.funeat.recipe.dto.RecipeFavoriteRequest; -import com.funeat.recipe.dto.SearchRecipeResultsResponse; -import com.funeat.recipe.dto.SortingRecipesResponse; -import java.net.URI; -import java.util.List; -import javax.validation.Valid; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -@RestController -public class RecipeApiController implements RecipeController { - - private final RecipeService recipeService; - - public RecipeApiController(final RecipeService recipeService) { - this.recipeService = recipeService; - } - - @Logging - @PostMapping(value = "/api/recipes", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, - MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity writeRecipe(@AuthenticationPrincipal final LoginInfo loginInfo, - @RequestPart(required = false) final List images, - @RequestPart @Valid final RecipeCreateRequest recipeRequest) { - final Long recipeId = recipeService.create(loginInfo.getId(), images, recipeRequest); - - return ResponseEntity.created(URI.create("/api/recipes/" + recipeId)).build(); - } - - @GetMapping(value = "/api/recipes/{recipeId}") - public ResponseEntity getRecipeDetail(@AuthenticationPrincipal final LoginInfo loginInfo, - @PathVariable final Long recipeId) { - final RecipeDetailResponse response = recipeService.getRecipeDetail(loginInfo.getId(), recipeId); - - return ResponseEntity.ok(response); - } - - @GetMapping(value = "/api/recipes") - public ResponseEntity getSortingRecipes(@PageableDefault final Pageable pageable) { - final SortingRecipesResponse response = recipeService.getSortingRecipes(pageable); - - return ResponseEntity.ok(response); - } - - @Logging - @PatchMapping(value = "/api/recipes/{recipeId}") - public ResponseEntity likeRecipe(@AuthenticationPrincipal final LoginInfo loginInfo, - @PathVariable final Long recipeId, - @RequestBody @Valid final RecipeFavoriteRequest request) { - recipeService.likeRecipe(loginInfo.getId(), recipeId, request); - - return ResponseEntity.noContent().build(); - } - - @GetMapping("/api/ranks/recipes") - public ResponseEntity getRankingRecipes() { - final RankingRecipesResponse response = recipeService.getTop3Recipes(); - - return ResponseEntity.ok(response); - } - - @GetMapping("/api/search/recipes/results") - public ResponseEntity getSearchResults(@RequestParam final String query, - @PageableDefault final Pageable pageable) { - final PageRequest pageRequest = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize()); - final SearchRecipeResultsResponse response = recipeService.getSearchResults(query, pageRequest); - - return ResponseEntity.ok(response); - } - - @PostMapping("/api/recipes/{recipeId}/comments") - public ResponseEntity writeComment(@AuthenticationPrincipal final LoginInfo loginInfo, - @PathVariable final Long recipeId, - @RequestBody @Valid final RecipeCommentCreateRequest request) { - final Long savedCommentId = recipeService.writeCommentOfRecipe(loginInfo.getId(), recipeId, request); - - return ResponseEntity.created(URI.create("/api/recipes/" + recipeId + "/" + savedCommentId)).build(); - } - - @GetMapping("/api/recipes/{recipeId}/comments") - public ResponseEntity getCommentsOfRecipe( - @AuthenticationPrincipal final LoginInfo loginInfo, @PathVariable final Long recipeId, - @ModelAttribute final RecipeCommentCondition condition) { - final RecipeCommentsResponse response = recipeService.getCommentsOfRecipe(recipeId, condition); - - return ResponseEntity.ok(response); - } -} diff --git a/backend/src/main/java/com/funeat/recipe/presentation/RecipeController.java b/backend/src/main/java/com/funeat/recipe/presentation/RecipeController.java deleted file mode 100644 index 05602cc7f..000000000 --- a/backend/src/main/java/com/funeat/recipe/presentation/RecipeController.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.funeat.recipe.presentation; - -import com.funeat.auth.dto.LoginInfo; -import com.funeat.auth.util.AuthenticationPrincipal; -import com.funeat.recipe.dto.RankingRecipesResponse; -import com.funeat.recipe.dto.RecipeCommentCondition; -import com.funeat.recipe.dto.RecipeCommentCreateRequest; -import com.funeat.recipe.dto.RecipeCommentsResponse; -import com.funeat.recipe.dto.RecipeCreateRequest; -import com.funeat.recipe.dto.RecipeDetailResponse; -import com.funeat.recipe.dto.RecipeFavoriteRequest; -import com.funeat.recipe.dto.SearchRecipeResultsResponse; -import com.funeat.recipe.dto.SortingRecipesResponse; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.List; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.multipart.MultipartFile; - -@Tag(name = "07.Recipe", description = "꿀조합 관련 API 입니다.") -public interface RecipeController { - - @Operation(summary = "꿀조합 추가", description = "꿀조합을 작성한다.") - @ApiResponse( - responseCode = "201", - description = "꿀조합 작성 성공." - ) - @PostMapping - ResponseEntity writeRecipe(@AuthenticationPrincipal final LoginInfo loginInfo, - @RequestPart final List images, - @RequestPart final RecipeCreateRequest recipeRequest); - - @Operation(summary = "꿀조합 상세 조회", description = "꿀조합의 상세 정보를 조회한다.") - @ApiResponse( - responseCode = "200", - description = "꿀조합 상세 조회 성공." - ) - @GetMapping - ResponseEntity getRecipeDetail(@AuthenticationPrincipal final LoginInfo loginInfo, - @PathVariable final Long recipeId); - - @Operation(summary = "꿀조합 목록 조회", description = "꿀조합의 목록을 조회한다.") - @ApiResponse( - responseCode = "200", - description = "꿀조합 목록 조회 성공." - ) - @GetMapping - ResponseEntity getSortingRecipes(@PageableDefault final Pageable pageable); - - @Operation(summary = "꿀조합 좋아요", description = "꿀조합에 좋아요 또는 취소를 한다.") - @ApiResponse( - responseCode = "204", - description = "꿀조합 좋아요(또는 좋아요 취소) 성공." - ) - @PatchMapping - ResponseEntity likeRecipe(@AuthenticationPrincipal final LoginInfo loginInfo, - @PathVariable final Long recipeId, - @RequestBody final RecipeFavoriteRequest request); - - @Operation(summary = "꿀조합 랭킹 조회", description = "전체 꿀조합들 중에서 랭킹 TOP3를 조회한다.") - @ApiResponse( - responseCode = "200", - description = "꿀조합 랭킹 조회 성공." - ) - @GetMapping - ResponseEntity getRankingRecipes(); - - @Operation(summary = "꿀조합 검색 결과 조회", description = "검색어가 포함된 상품이 있는 꿀조합 목록을 조회한다.") - @ApiResponse( - responseCode = "200", - description = "꿀조합 검색 결과 조회 성공." - ) - @GetMapping - ResponseEntity getSearchResults(@RequestParam final String query, - @PageableDefault final Pageable pageable); - - @Operation(summary = "꿀조합 댓글 작성", description = "꿀조합 상세에서 댓글을 작성한다.") - @ApiResponse( - responseCode = "201", - description = "꿀조합 댓글 작성 성공." - ) - @PostMapping("/api/recipes/{recipeId}/comments") - ResponseEntity writeComment(@AuthenticationPrincipal final LoginInfo loginInfo, - @PathVariable final Long recipeId, - @RequestBody final RecipeCommentCreateRequest request); - - @Operation(summary = "꿀조합 댓글 조회", description = "꿀조합 상세에서 댓글을 조회한다.") - @ApiResponse( - responseCode = "200", - description = "꿀조합 댓글 조회 성공." - ) - @GetMapping("/api/recipes/{recipeId}/comments") - ResponseEntity getCommentsOfRecipe(@AuthenticationPrincipal final LoginInfo loginInfo, - @PathVariable final Long recipeId, - @ModelAttribute final RecipeCommentCondition condition); -} diff --git a/backend/src/main/java/com/funeat/recipe/util/RecipeDetailHandlerInterceptor.java b/backend/src/main/java/com/funeat/recipe/util/RecipeDetailHandlerInterceptor.java deleted file mode 100644 index 9bca8ce25..000000000 --- a/backend/src/main/java/com/funeat/recipe/util/RecipeDetailHandlerInterceptor.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.funeat.recipe.util; - -import com.funeat.auth.exception.AuthErrorCode; -import com.funeat.auth.exception.AuthException.NotLoggedInException; -import java.util.Objects; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.HandlerInterceptor; - -@Component -public class RecipeDetailHandlerInterceptor implements HandlerInterceptor { - - @Override - public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, - final Object handler) { - final HttpSession session = request.getSession(false); - - if (Objects.isNull(session)) { - throw new NotLoggedInException(AuthErrorCode.LOGIN_MEMBER_NOT_FOUND); - } - - return true; - } -} diff --git a/backend/src/main/java/com/funeat/recipe/util/RecipeHandlerInterceptor.java b/backend/src/main/java/com/funeat/recipe/util/RecipeHandlerInterceptor.java deleted file mode 100644 index 192f5714d..000000000 --- a/backend/src/main/java/com/funeat/recipe/util/RecipeHandlerInterceptor.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.funeat.recipe.util; - -import com.funeat.auth.exception.AuthErrorCode; -import com.funeat.auth.exception.AuthException.NotLoggedInException; -import java.util.Objects; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.HandlerInterceptor; - -@Component -public class RecipeHandlerInterceptor implements HandlerInterceptor { - - private static final String GET = "GET"; - - @Override - public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, - final Object handler) { - if (GET.equals(request.getMethod())) { - return true; - } - - final HttpSession session = request.getSession(false); - - if (Objects.isNull(session)) { - throw new NotLoggedInException(AuthErrorCode.LOGIN_MEMBER_NOT_FOUND); - } - - return true; - } -} diff --git a/backend/src/main/java/com/funeat/review/application/ReviewDeleteEvent.java b/backend/src/main/java/com/funeat/review/application/ReviewDeleteEvent.java deleted file mode 100644 index 7c69eee3c..000000000 --- a/backend/src/main/java/com/funeat/review/application/ReviewDeleteEvent.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.funeat.review.application; - -public class ReviewDeleteEvent { - - private final String image; - - public ReviewDeleteEvent(final String image) { - this.image = image; - } - - public String getImage() { - return image; - } -} diff --git a/backend/src/main/java/com/funeat/review/application/ReviewDeleteEventListener.java b/backend/src/main/java/com/funeat/review/application/ReviewDeleteEventListener.java deleted file mode 100644 index a92c4f943..000000000 --- a/backend/src/main/java/com/funeat/review/application/ReviewDeleteEventListener.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.funeat.review.application; - -import com.funeat.common.ImageUploader; -import io.micrometer.core.instrument.util.StringUtils; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; -import org.springframework.transaction.event.TransactionPhase; -import org.springframework.transaction.event.TransactionalEventListener; - -@Component -public class ReviewDeleteEventListener { - - private final ImageUploader imageUploader; - - public ReviewDeleteEventListener(final ImageUploader imageUploader) { - this.imageUploader = imageUploader; - } - - @Async - @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) - public void deleteReviewImageInS3(final ReviewDeleteEvent event) { - final String image = event.getImage(); - if (!StringUtils.isBlank(image)) { - imageUploader.delete(image); - } - } -} diff --git a/backend/src/main/java/com/funeat/review/application/ReviewService.java b/backend/src/main/java/com/funeat/review/application/ReviewService.java deleted file mode 100644 index a65922c82..000000000 --- a/backend/src/main/java/com/funeat/review/application/ReviewService.java +++ /dev/null @@ -1,301 +0,0 @@ -package com.funeat.review.application; - -import static com.funeat.member.exception.MemberErrorCode.MEMBER_DUPLICATE_FAVORITE; -import static com.funeat.member.exception.MemberErrorCode.MEMBER_NOT_FOUND; -import static com.funeat.product.exception.ProductErrorCode.PRODUCT_NOT_FOUND; -import static com.funeat.review.exception.ReviewErrorCode.NOT_AUTHOR_OF_REVIEW; -import static com.funeat.review.exception.ReviewErrorCode.REVIEW_NOT_FOUND; - -import com.funeat.common.ImageUploader; -import com.funeat.common.dto.PageDto; -import com.funeat.member.domain.Member; -import com.funeat.member.domain.favorite.ReviewFavorite; -import com.funeat.member.dto.MemberReviewDto; -import com.funeat.member.dto.MemberReviewsResponse; -import com.funeat.member.exception.MemberException.MemberDuplicateFavoriteException; -import com.funeat.member.exception.MemberException.MemberNotFoundException; -import com.funeat.member.persistence.MemberRepository; -import com.funeat.member.persistence.ReviewFavoriteRepository; -import com.funeat.product.domain.Product; -import com.funeat.product.exception.ProductException.ProductNotFoundException; -import com.funeat.product.persistence.ProductRepository; -import com.funeat.review.domain.Review; -import com.funeat.review.domain.ReviewTag; -import com.funeat.review.dto.MostFavoriteReviewResponse; -import com.funeat.review.dto.RankingReviewDto; -import com.funeat.review.dto.RankingReviewsResponse; -import com.funeat.review.dto.ReviewCreateRequest; -import com.funeat.review.dto.ReviewDetailResponse; -import com.funeat.review.dto.ReviewFavoriteRequest; -import com.funeat.review.dto.SortingReviewDto; -import com.funeat.review.dto.SortingReviewDtoWithoutTag; -import com.funeat.review.dto.SortingReviewRequest; -import com.funeat.review.dto.SortingReviewsResponse; -import com.funeat.review.exception.ReviewException.NotAuthorOfReviewException; -import com.funeat.review.exception.ReviewException.ReviewNotFoundException; -import com.funeat.review.persistence.ReviewRepository; -import com.funeat.review.persistence.ReviewTagRepository; -import com.funeat.review.specification.SortingReviewSpecification; -import com.funeat.tag.domain.Tag; -import com.funeat.tag.persistence.TagRepository; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; - -@Service -@Transactional(readOnly = true) -public class ReviewService { - - private static final int FIRST_PAGE = 0; - private static final int START_INDEX = 0; - private static final int ONE = 1; - private static final String EMPTY_URL = ""; - private static final int RANKING_SIZE = 3; - private static final long RANKING_MINIMUM_FAVORITE_COUNT = 1L; - private static final int REVIEW_PAGE_SIZE = 10; - - private final ReviewRepository reviewRepository; - private final TagRepository tagRepository; - private final ReviewTagRepository reviewTagRepository; - private final MemberRepository memberRepository; - private final ProductRepository productRepository; - private final ReviewFavoriteRepository reviewFavoriteRepository; - private final ImageUploader imageUploader; - private final ApplicationEventPublisher eventPublisher; - - public ReviewService(final ReviewRepository reviewRepository, final TagRepository tagRepository, - final ReviewTagRepository reviewTagRepository, final MemberRepository memberRepository, - final ProductRepository productRepository, - final ReviewFavoriteRepository reviewFavoriteRepository, - final ImageUploader imageUploader, final ApplicationEventPublisher eventPublisher) { - this.reviewRepository = reviewRepository; - this.tagRepository = tagRepository; - this.reviewTagRepository = reviewTagRepository; - this.memberRepository = memberRepository; - this.productRepository = productRepository; - this.reviewFavoriteRepository = reviewFavoriteRepository; - this.imageUploader = imageUploader; - this.eventPublisher = eventPublisher; - } - - @Transactional - public void create(final Long productId, final Long memberId, final MultipartFile image, - final ReviewCreateRequest reviewRequest) { - final Member findMember = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId)); - final Product findProduct = productRepository.findById(productId) - .orElseThrow(() -> new ProductNotFoundException(PRODUCT_NOT_FOUND, productId)); - - final String imageUrl = Optional.ofNullable(image) - .map(imageUploader::upload) - .orElse(EMPTY_URL); - final Review savedReview = reviewRepository.save( - new Review(findMember, findProduct, imageUrl, reviewRequest.getRating(), reviewRequest.getContent(), - reviewRequest.getRebuy())); - - final List findTags = tagRepository.findTagsByIdIn(reviewRequest.getTagIds()); - - final List reviewTags = findTags.stream() - .map(findTag -> ReviewTag.createReviewTag(savedReview, findTag)) - .collect(Collectors.toList()); - - final Long countByProduct = reviewRepository.countByProduct(findProduct); - - findProduct.updateAverageRatingForInsert(countByProduct, savedReview.getRating()); - findProduct.addReviewCount(); - reviewTagRepository.saveAll(reviewTags); - } - - @Transactional - public void likeReview(final Long reviewId, final Long memberId, final ReviewFavoriteRequest request) { - final Member findMember = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId)); - final Review findReview = reviewRepository.findByIdForUpdate(reviewId) - .orElseThrow(() -> new ReviewNotFoundException(REVIEW_NOT_FOUND, reviewId)); - - final ReviewFavorite savedReviewFavorite = reviewFavoriteRepository.findByMemberAndReview(findMember, - findReview).orElseGet(() -> saveReviewFavorite(findMember, findReview, request.getFavorite())); - - savedReviewFavorite.updateChecked(request.getFavorite()); - } - - private ReviewFavorite saveReviewFavorite(final Member member, final Review review, final Boolean favorite) { - try { - final ReviewFavorite reviewFavorite = ReviewFavorite.create(member, review, favorite); - return reviewFavoriteRepository.save(reviewFavorite); - } catch (final DataIntegrityViolationException e) { - throw new MemberDuplicateFavoriteException(MEMBER_DUPLICATE_FAVORITE, member.getId()); - } - } - - @Transactional - public void updateProductImage(final Long productId) { - final Product product = productRepository.findById(productId) - .orElseThrow(() -> new ProductNotFoundException(PRODUCT_NOT_FOUND, productId)); - - final PageRequest pageRequest = PageRequest.of(FIRST_PAGE, ONE); - - final List topFavoriteReview = reviewRepository.findPopularReviewWithImage(productId, pageRequest); - if (topFavoriteReview.isEmpty()) { - product.updateBasicImage(); - return; - } - final String topFavoriteReviewImage = topFavoriteReview.get(START_INDEX).getImage(); - product.updateFavoriteImage(topFavoriteReviewImage); - } - - public SortingReviewsResponse sortingReviews(final Long productId, final Long memberId, - final SortingReviewRequest request) { - final Member findMember = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId)); - final Product findProduct = productRepository.findById(productId) - .orElseThrow(() -> new ProductNotFoundException(PRODUCT_NOT_FOUND, productId)); - - final List sortingReviews = getSortingReviews(findMember, findProduct, request); - final int resultSize = getResultSize(sortingReviews); - - final List resizeSortingReviews = sortingReviews.subList(START_INDEX, resultSize); - final Boolean hasNext = hasNextPage(sortingReviews); - - return SortingReviewsResponse.toResponse(resizeSortingReviews, hasNext); - } - - private List getSortingReviews(final Member member, final Product product, - final SortingReviewRequest request) { - final Long lastReviewId = request.getLastReviewId(); - final String sortOption = request.getSort(); - - final Specification specification = getSortingSpecification(product, sortOption, lastReviewId); - final List sortingReviewDtoWithoutTags = reviewRepository.getSortingReview(member, - specification, sortOption); - - return addTagsToSortingReviews(sortingReviewDtoWithoutTags); - } - - private List addTagsToSortingReviews( - final List sortingReviewDtoWithoutTags) { - return sortingReviewDtoWithoutTags.stream() - .map(reviewDto -> SortingReviewDto.toDto(reviewDto, - tagRepository.findTagsByReviewId(reviewDto.getId()))) - .collect(Collectors.toList()); - } - - private Specification getSortingSpecification(final Product product, final String sortOption, - final Long lastReviewId) { - if (lastReviewId == FIRST_PAGE) { - return SortingReviewSpecification.sortingFirstPageBy(product); - } - - final Review lastReview = reviewRepository.findById(lastReviewId) - .orElseThrow(() -> new ReviewNotFoundException(REVIEW_NOT_FOUND, lastReviewId)); - - return SortingReviewSpecification.sortingBy(product, sortOption, lastReview); - } - - private int getResultSize(final List sortingReviews) { - if (sortingReviews.size() <= REVIEW_PAGE_SIZE) { - return sortingReviews.size(); - } - return REVIEW_PAGE_SIZE; - } - - private Boolean hasNextPage(final List sortingReviews) { - return sortingReviews.size() > REVIEW_PAGE_SIZE; - } - - public RankingReviewsResponse getTopReviews() { - final List reviews = reviewRepository.findReviewsByFavoriteCountGreaterThanEqual(RANKING_MINIMUM_FAVORITE_COUNT); - final List dtos = reviews.stream() - .sorted(Comparator.comparing(Review::calculateRankingScore).reversed()) - .limit(RANKING_SIZE) - .map(RankingReviewDto::toDto) - .collect(Collectors.toList()); - - return RankingReviewsResponse.toResponse(dtos); - } - - public MemberReviewsResponse findReviewByMember(final Long memberId, final Pageable pageable) { - final Member findMember = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId)); - - final Page sortedReviewPages = reviewRepository.findReviewsByMember(findMember, pageable); - final PageDto pageDto = PageDto.toDto(sortedReviewPages); - - final List dtos = sortedReviewPages.stream() - .map(MemberReviewDto::toDto) - .collect(Collectors.toList()); - - return MemberReviewsResponse.toResponse(pageDto, dtos); - } - - @Transactional - public void deleteReview(final Long reviewId, final Long memberId) { - final Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId)); - final Review review = reviewRepository.findById(reviewId) - .orElseThrow(() -> new ReviewNotFoundException(REVIEW_NOT_FOUND, reviewId)); - final Product product = review.getProduct(); - final String image = review.getImage(); - - if (review.checkAuthor(member)) { - eventPublisher.publishEvent(new ReviewDeleteEvent(image)); - deleteThingsRelatedToReview(review); - updateProduct(product, review.getRating()); - return; - } - throw new NotAuthorOfReviewException(NOT_AUTHOR_OF_REVIEW, memberId); - } - - private void deleteThingsRelatedToReview(final Review review) { - deleteReviewTags(review); - deleteReviewFavorites(review); - reviewRepository.delete(review); - } - - private void deleteReviewTags(final Review review) { - final List reviewTags = reviewTagRepository.findByReview(review); - final List ids = reviewTags.stream() - .map(ReviewTag::getId) - .collect(Collectors.toList()); - reviewTagRepository.deleteAllByIdInBatch(ids); - } - - private void deleteReviewFavorites(final Review review) { - final List reviewFavorites = reviewFavoriteRepository.findByReview(review); - final List ids = reviewFavorites.stream() - .map(ReviewFavorite::getId) - .collect(Collectors.toList()); - reviewFavoriteRepository.deleteAllByIdInBatch(ids); - } - - private void updateProduct(final Product product, final Long deletedRating) { - product.updateAverageRatingForDelete(deletedRating); - product.minusReviewCount(); - updateProductImage(product.getId()); - } - - public Optional getMostFavoriteReview(final Long productId) { - final Product findProduct = productRepository.findById(productId) - .orElseThrow(() -> new ProductNotFoundException(PRODUCT_NOT_FOUND, productId)); - - final Optional review = reviewRepository.findTopByProductOrderByFavoriteCountDescIdDesc(findProduct); - - return MostFavoriteReviewResponse.toResponse(review); - } - - public ReviewDetailResponse getReviewDetail(final Long reviewId) { - final Review review = reviewRepository.findById(reviewId) - .orElseThrow(() -> new ReviewNotFoundException(REVIEW_NOT_FOUND, reviewId)); - return ReviewDetailResponse.toResponse(review); - } -} diff --git a/backend/src/main/java/com/funeat/review/domain/Review.java b/backend/src/main/java/com/funeat/review/domain/Review.java deleted file mode 100644 index 9b1a458d3..000000000 --- a/backend/src/main/java/com/funeat/review/domain/Review.java +++ /dev/null @@ -1,159 +0,0 @@ -package com.funeat.review.domain; - -import com.funeat.member.domain.Member; -import com.funeat.member.domain.favorite.ReviewFavorite; -import com.funeat.product.domain.Product; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; - -@Entity -public class Review { - - private static final double RANKING_GRAVITY = 0.5; - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String image; - - private Long rating; - - private String content; - - private Boolean reBuy; - - @Column(nullable = false) - private LocalDateTime createdAt = LocalDateTime.now(); - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "product_id") - private Product product; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") - private Member member; - - @OneToMany(mappedBy = "review") - private List reviewTags = new ArrayList<>(); - - @OneToMany(mappedBy = "review") - private List reviewFavorites = new ArrayList<>(); - - private Long favoriteCount = 0L; - - protected Review() { - } - - public Review(final Member member, final Product findProduct, final Long rating, final String content, - final Boolean reBuy) { - this(member, findProduct, null, rating, content, reBuy); - } - - public Review(final Member member, final Product findProduct, final String image, final Long rating, - final String content, final Boolean reBuy) { - this.member = member; - this.product = findProduct; - this.image = image; - this.rating = rating; - this.content = content; - this.reBuy = reBuy; - } - - public Review(final Member member, final Product findProduct, final String image, final Long rating, - final String content, final Boolean reBuy, final Long favoriteCount) { - this.member = member; - this.product = findProduct; - this.image = image; - this.rating = rating; - this.content = content; - this.reBuy = reBuy; - this.favoriteCount = favoriteCount; - } - - public Review(final Member member, final Product findProduct, final String image, final Long rating, - final String content, final Boolean reBuy, final Long favoriteCount, final LocalDateTime createdAt) { - this.member = member; - this.product = findProduct; - this.image = image; - this.rating = rating; - this.content = content; - this.reBuy = reBuy; - this.favoriteCount = favoriteCount; - this.createdAt = createdAt; - } - - public void addFavoriteCount() { - this.favoriteCount++; - } - - public void minusFavoriteCount() { - this.favoriteCount--; - } - - public Double calculateRankingScore() { - final long age = ChronoUnit.DAYS.between(createdAt, LocalDateTime.now()); - final double denominator = Math.pow(age + 1.0, RANKING_GRAVITY); - return favoriteCount / denominator; - } - - public boolean checkAuthor(final Member member) { - return Objects.equals(this.member, member); - } - - public Long getId() { - return id; - } - - public String getImage() { - return image; - } - - public Long getRating() { - return rating; - } - - public String getContent() { - return content; - } - - public Boolean getReBuy() { - return reBuy; - } - - public Product getProduct() { - return product; - } - - public Member getMember() { - return member; - } - - public List getReviewFavorites() { - return reviewFavorites; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public List getReviewTags() { - return reviewTags; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } -} diff --git a/backend/src/main/java/com/funeat/review/domain/ReviewTag.java b/backend/src/main/java/com/funeat/review/domain/ReviewTag.java deleted file mode 100644 index 72b549743..000000000 --- a/backend/src/main/java/com/funeat/review/domain/ReviewTag.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.funeat.review.domain; - -import com.funeat.tag.domain.Tag; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; - -@Entity -public class ReviewTag { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "review_id") - private Review review; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "tag_id") - private Tag tag; - - protected ReviewTag() { - } - - private ReviewTag(final Review review, final Tag tag) { - this.review = review; - this.tag = tag; - } - - public static ReviewTag createReviewTag(final Review review, final Tag tag) { - final ReviewTag reviewTag = new ReviewTag(review, tag); - review.getReviewTags().add(reviewTag); - return reviewTag; - } - - public Long getId() { - return id; - } - - public Review getReview() { - return review; - } - - public Tag getTag() { - return tag; - } -} diff --git a/backend/src/main/java/com/funeat/review/dto/MostFavoriteReviewResponse.java b/backend/src/main/java/com/funeat/review/dto/MostFavoriteReviewResponse.java deleted file mode 100644 index 0d1352f80..000000000 --- a/backend/src/main/java/com/funeat/review/dto/MostFavoriteReviewResponse.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.funeat.review.dto; - -import com.funeat.review.domain.Review; -import com.funeat.review.domain.ReviewTag; -import com.funeat.tag.dto.TagDto; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -public class MostFavoriteReviewResponse { - - private final Long id; - private final String userName; - private final String profileImage; - private final String image; - private final Long rating; - private final List tags; - private final String content; - private final Boolean rebuy; - private final Long favoriteCount; - private final LocalDateTime createdAt; - - public MostFavoriteReviewResponse(final Long id, final String userName, final String profileImage, - final String image, final Long rating, final List tags, - final String content, final boolean rebuy, final Long favoriteCount, - final LocalDateTime createdAt) { - this.id = id; - this.userName = userName; - this.profileImage = profileImage; - this.image = image; - this.rating = rating; - this.tags = tags; - this.content = content; - this.rebuy = rebuy; - this.favoriteCount = favoriteCount; - this.createdAt = createdAt; - } - - public static Optional toResponse(final Optional nullableReview) { - if (nullableReview.isEmpty()) { - return Optional.empty(); - } - - final Review review = nullableReview.get(); - return Optional.of(new MostFavoriteReviewResponse( - review.getId(), - review.getMember().getNickname(), - review.getMember().getProfileImage(), - review.getImage(), - review.getRating(), - findTagDtos(review), - review.getContent(), - review.getReBuy(), - review.getFavoriteCount(), - review.getCreatedAt() - )); - } - - private static List findTagDtos(final Review review) { - return review.getReviewTags().stream() - .map(ReviewTag::getTag) - .map(TagDto::toDto) - .collect(Collectors.toList()); - } - - public Long getId() { - return id; - } - - public String getUserName() { - return userName; - } - - public String getProfileImage() { - return profileImage; - } - - public String getImage() { - return image; - } - - public Long getRating() { - return rating; - } - - public List getTags() { - return tags; - } - - public String getContent() { - return content; - } - - public Boolean isRebuy() { - return rebuy; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } -} diff --git a/backend/src/main/java/com/funeat/review/dto/RankingReviewDto.java b/backend/src/main/java/com/funeat/review/dto/RankingReviewDto.java deleted file mode 100644 index 5ac70bb59..000000000 --- a/backend/src/main/java/com/funeat/review/dto/RankingReviewDto.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.funeat.review.dto; - -import com.funeat.review.domain.Review; -import java.time.LocalDateTime; - -public class RankingReviewDto { - - private final Long reviewId; - private final Long productId; - private final String categoryType; - private final String productName; - private final String content; - private final Long rating; - private final Long favoriteCount; - private final LocalDateTime createdAt; - - private RankingReviewDto(final Long reviewId, final Long productId, final String categoryType, - final String productName, final String content, - final Long rating, final Long favoriteCount, final LocalDateTime createdAt) { - this.reviewId = reviewId; - this.productId = productId; - this.categoryType = categoryType; - this.productName = productName; - this.content = content; - this.rating = rating; - this.favoriteCount = favoriteCount; - this.createdAt = createdAt; - } - - public static RankingReviewDto toDto(final Review review) { - return new RankingReviewDto( - review.getId(), - review.getProduct().getId(), - review.getProduct().getCategory().getType().getName(), - review.getProduct().getName(), - review.getContent(), - review.getRating(), - review.getFavoriteCount(), - review.getCreatedAt()); - } - - public Long getReviewId() { - return reviewId; - } - - public Long getProductId() { - return productId; - } - - public String getProductName() { - return productName; - } - - public String getContent() { - return content; - } - - public Long getRating() { - return rating; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public String getCategoryType() { - return categoryType; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } -} diff --git a/backend/src/main/java/com/funeat/review/dto/RankingReviewsResponse.java b/backend/src/main/java/com/funeat/review/dto/RankingReviewsResponse.java deleted file mode 100644 index fd2befb0a..000000000 --- a/backend/src/main/java/com/funeat/review/dto/RankingReviewsResponse.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.funeat.review.dto; - -import java.util.List; - -public class RankingReviewsResponse { - - private final List reviews; - - public RankingReviewsResponse(final List reviews) { - this.reviews = reviews; - } - - public static RankingReviewsResponse toResponse(final List reviews) { - return new RankingReviewsResponse(reviews); - } - - public List getReviews() { - return reviews; - } -} diff --git a/backend/src/main/java/com/funeat/review/dto/ReviewCreateRequest.java b/backend/src/main/java/com/funeat/review/dto/ReviewCreateRequest.java deleted file mode 100644 index ce1e9a717..000000000 --- a/backend/src/main/java/com/funeat/review/dto/ReviewCreateRequest.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.funeat.review.dto; - -import java.util.List; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -public class ReviewCreateRequest { - - @NotNull(message = "평점을 확인해 주세요") - private final Long rating; - - @NotNull(message = "태그 ID 목록을 확인해 주세요") - @Size(min = 1, message = "적어도 1개의 태그 ID가 필요합니다") - private final List tagIds; - - @NotBlank(message = "리뷰 내용을 확인해 주세요") - @Size(max = 200, message = "리뷰 내용은 최대 200자까지 입력 가능합니다") - private final String content; - - @NotNull(message = "재구매 여부를 입력해주세요") - private final Boolean rebuy; - - public ReviewCreateRequest(final Long rating, final List tagIds, final String content, final Boolean rebuy) { - this.rating = rating; - this.tagIds = tagIds; - this.content = content; - this.rebuy = rebuy; - } - - public Long getRating() { - return rating; - } - - public String getContent() { - return content; - } - - public Boolean getRebuy() { - return rebuy; - } - - public List getTagIds() { - return tagIds; - } -} diff --git a/backend/src/main/java/com/funeat/review/dto/ReviewDetailResponse.java b/backend/src/main/java/com/funeat/review/dto/ReviewDetailResponse.java deleted file mode 100644 index 7e0c9858b..000000000 --- a/backend/src/main/java/com/funeat/review/dto/ReviewDetailResponse.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.funeat.review.dto; - -import com.funeat.review.domain.Review; -import com.funeat.review.domain.ReviewTag; -import com.funeat.tag.dto.TagDto; -import java.time.LocalDateTime; -import java.util.List; -import java.util.stream.Collectors; - -public class ReviewDetailResponse { - - private final Long id; - private final String userName; - private final String profileImage; - private final String image; - private final Long rating; - private final List tags; - private final String content; - private final boolean rebuy; - private final Long favoriteCount; - private final LocalDateTime createdAt; - private final String categoryType; - private final Long productId; - private final String productName; - - public ReviewDetailResponse(final Long id, final String userName, final String profileImage, final String image, - final Long rating, final List tags, final String content, final boolean rebuy, - final Long favoriteCount, final LocalDateTime createdAt, final String categoryType, - final Long productId, final String productName) { - this.id = id; - this.userName = userName; - this.profileImage = profileImage; - this.image = image; - this.rating = rating; - this.tags = tags; - this.content = content; - this.rebuy = rebuy; - this.favoriteCount = favoriteCount; - this.createdAt = createdAt; - this.categoryType = categoryType; - this.productId = productId; - this.productName = productName; - } - - public static ReviewDetailResponse toResponse(final Review review) { - return new ReviewDetailResponse( - review.getId(), - review.getMember().getNickname(), - review.getMember().getProfileImage(), - review.getImage(), - review.getRating(), - findTagDtos(review), - review.getContent(), - review.getReBuy(), - review.getFavoriteCount(), - review.getCreatedAt(), - review.getProduct().getCategory().getType().getName(), - review.getProduct().getId(), - review.getProduct().getName() - ); - } - - private static List findTagDtos(final Review review) { - return review.getReviewTags().stream() - .map(ReviewTag::getTag) - .map(TagDto::toDto) - .collect(Collectors.toList()); - } - - public Long getId() { - return id; - } - - public String getUserName() { - return userName; - } - - public String getProfileImage() { - return profileImage; - } - - public String getImage() { - return image; - } - - public Long getRating() { - return rating; - } - - public List getTags() { - return tags; - } - - public String getContent() { - return content; - } - - public boolean isRebuy() { - return rebuy; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public String getCategoryType() { - return categoryType; - } - - public Long getProductId() { - return productId; - } - - public String getProductName() { - return productName; - } -} diff --git a/backend/src/main/java/com/funeat/review/dto/ReviewFavoriteRequest.java b/backend/src/main/java/com/funeat/review/dto/ReviewFavoriteRequest.java deleted file mode 100644 index ea3017304..000000000 --- a/backend/src/main/java/com/funeat/review/dto/ReviewFavoriteRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.funeat.review.dto; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import javax.validation.constraints.NotNull; - -public class ReviewFavoriteRequest { - - @NotNull(message = "좋아요를 확인해주세요") - private final Boolean favorite; - - @JsonCreator - public ReviewFavoriteRequest(@JsonProperty("favorite") final Boolean favorite) { - this.favorite = favorite; - } - - public Boolean getFavorite() { - return favorite; - } -} diff --git a/backend/src/main/java/com/funeat/review/dto/SortingReviewDto.java b/backend/src/main/java/com/funeat/review/dto/SortingReviewDto.java deleted file mode 100644 index 1231d0058..000000000 --- a/backend/src/main/java/com/funeat/review/dto/SortingReviewDto.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.funeat.review.dto; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.funeat.member.domain.Member; -import com.funeat.member.domain.favorite.ReviewFavorite; -import com.funeat.review.domain.Review; -import com.funeat.review.domain.ReviewTag; -import com.funeat.tag.domain.Tag; -import com.funeat.tag.dto.TagDto; -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -public class SortingReviewDto { - - private final Long id; - private final String userName; - private final String profileImage; - private final String image; - private final Long rating; - private final List tags; - private final String content; - private final boolean rebuy; - private final Long favoriteCount; - private final boolean favorite; - private final LocalDateTime createdAt; - - @JsonCreator - public SortingReviewDto(final Long id, final String userName, final String profileImage, final String image, - final Long rating, final List tags, - final String content, final boolean rebuy, final Long favoriteCount, final boolean favorite, - final LocalDateTime createdAt) { - this.id = id; - this.userName = userName; - this.profileImage = profileImage; - this.image = image; - this.rating = rating; - this.tags = tags; - this.content = content; - this.rebuy = rebuy; - this.favoriteCount = favoriteCount; - this.favorite = favorite; - this.createdAt = createdAt; - } - - public static SortingReviewDto toDto(final SortingReviewDtoWithoutTag sortingReviewDto, final List tags) { - final List tagDtos = tags.stream() - .map(TagDto::toDto) - .collect(Collectors.toList()); - - return new SortingReviewDto( - sortingReviewDto.getId(), - sortingReviewDto.getUserName(), - sortingReviewDto.getProfileImage(), - sortingReviewDto.getImage(), - sortingReviewDto.getRating(), - tagDtos, - sortingReviewDto.getContent(), - sortingReviewDto.getRebuy(), - sortingReviewDto.getFavoriteCount(), - sortingReviewDto.getFavorite(), - sortingReviewDto.getCreatedAt()); - } - - public Long getId() { - return id; - } - - public String getUserName() { - return userName; - } - - public String getProfileImage() { - return profileImage; - } - - public String getImage() { - return image; - } - - public Long getRating() { - return rating; - } - - public List getTags() { - return tags; - } - - public String getContent() { - return content; - } - - public boolean getRebuy() { - return rebuy; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public boolean getFavorite() { - return favorite; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } -} diff --git a/backend/src/main/java/com/funeat/review/dto/SortingReviewDtoWithoutTag.java b/backend/src/main/java/com/funeat/review/dto/SortingReviewDtoWithoutTag.java deleted file mode 100644 index b58ac7165..000000000 --- a/backend/src/main/java/com/funeat/review/dto/SortingReviewDtoWithoutTag.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.funeat.review.dto; - -import java.time.LocalDateTime; -import java.util.Objects; - -public class SortingReviewDtoWithoutTag { - - private final Long id; - private final String userName; - private final String profileImage; - private final String image; - private final Long rating; - private final String content; - private final boolean rebuy; - private final Long favoriteCount; - private final boolean favorite; - private final LocalDateTime createdAt; - - public SortingReviewDtoWithoutTag(final Long id, final String userName, final String profileImage, - final String image, final Long rating, - final String content, final boolean rebuy, final Long favoriteCount, - final Boolean favorite, - final LocalDateTime createdAt) { - final Boolean isFavorite = checkingFavorite(favorite); - - this.id = id; - this.userName = userName; - this.profileImage = profileImage; - this.image = image; - this.rating = rating; - this.content = content; - this.rebuy = rebuy; - this.favoriteCount = favoriteCount; - this.favorite = isFavorite; - this.createdAt = createdAt; - } - - private static Boolean checkingFavorite(final Boolean favorite) { - if (Objects.isNull(favorite)) { - return Boolean.FALSE; - } - return favorite; - } - - public Long getId() { - return id; - } - - public String getUserName() { - return userName; - } - - public String getProfileImage() { - return profileImage; - } - - public String getImage() { - return image; - } - - public Long getRating() { - return rating; - } - - public String getContent() { - return content; - } - - public boolean getRebuy() { - return rebuy; - } - - public Long getFavoriteCount() { - return favoriteCount; - } - - public boolean getFavorite() { - return favorite; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } -} diff --git a/backend/src/main/java/com/funeat/review/dto/SortingReviewRequest.java b/backend/src/main/java/com/funeat/review/dto/SortingReviewRequest.java deleted file mode 100644 index b6bdeb1eb..000000000 --- a/backend/src/main/java/com/funeat/review/dto/SortingReviewRequest.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.funeat.review.dto; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.PositiveOrZero; - -public class SortingReviewRequest { - - @NotNull(message = "정렬 조건을 확인해주세요") - private String sort; - - @NotNull(message = "마지막으로 조회한 리뷰 ID를 확인해주세요") - @PositiveOrZero(message = "마지막으로 조회한 ID는 0 이상이어야 합니다. (처음 조회하면 0)") - private Long lastReviewId; - - public SortingReviewRequest(final String sort, final Long lastReviewId) { - this.sort = sort; - this.lastReviewId = lastReviewId; - } - - public String getSort() { - return sort; - } - - public Long getLastReviewId() { - return lastReviewId; - } -} diff --git a/backend/src/main/java/com/funeat/review/dto/SortingReviewsResponse.java b/backend/src/main/java/com/funeat/review/dto/SortingReviewsResponse.java deleted file mode 100644 index 1dc082fe0..000000000 --- a/backend/src/main/java/com/funeat/review/dto/SortingReviewsResponse.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.funeat.review.dto; - -import java.util.List; - -public class SortingReviewsResponse { - - private final List reviews; - private final Boolean hasNext; - - public SortingReviewsResponse(final List reviews, final Boolean hasNext) { - this.reviews = reviews; - this.hasNext = hasNext; - } - - public static SortingReviewsResponse toResponse(final List reviews, final Boolean hasNextReview) { - return new SortingReviewsResponse(reviews, hasNextReview); - } - - public List getReviews() { - return reviews; - } - - public Boolean getHasNext() { - return hasNext; - } -} diff --git a/backend/src/main/java/com/funeat/review/exception/ReviewErrorCode.java b/backend/src/main/java/com/funeat/review/exception/ReviewErrorCode.java deleted file mode 100644 index 2f5fb5c64..000000000 --- a/backend/src/main/java/com/funeat/review/exception/ReviewErrorCode.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.funeat.review.exception; - -import org.springframework.http.HttpStatus; - -public enum ReviewErrorCode { - - REVIEW_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 리뷰입니다. 리뷰 id를 확인하세요.", "3001"), - NOT_SUPPORTED_REVIEW_SORTING_CONDITION(HttpStatus.BAD_REQUEST, "존재하지 않는 정렬 옵션입니다. 정렬 옵션을 확인하세요.", "3002"), - NOT_AUTHOR_OF_REVIEW(HttpStatus.BAD_REQUEST, "해당 리뷰를 작성한 회원이 아닙니다", "3003") - ; - - private final HttpStatus status; - private final String message; - private final String code; - - ReviewErrorCode(final HttpStatus status, final String message, final String code) { - this.status = status; - this.message = message; - this.code = code; - } - - public HttpStatus getStatus() { - return status; - } - - public String getMessage() { - return message; - } - - public String getCode() { - return code; - } -} diff --git a/backend/src/main/java/com/funeat/review/exception/ReviewException.java b/backend/src/main/java/com/funeat/review/exception/ReviewException.java deleted file mode 100644 index 85fd3f666..000000000 --- a/backend/src/main/java/com/funeat/review/exception/ReviewException.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.funeat.review.exception; - -import com.funeat.exception.ErrorCode; -import com.funeat.exception.GlobalException; -import org.springframework.http.HttpStatus; - -public class ReviewException extends GlobalException { - - public ReviewException(final HttpStatus status, final ErrorCode errorCode) { - super(status, errorCode); - } - - public static class ReviewNotFoundException extends ReviewException { - public ReviewNotFoundException(final ReviewErrorCode errorCode, final Long reviewId) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), reviewId)); - } - } - - public static class NotSupportedReviewSortingConditionException extends ReviewException { - public NotSupportedReviewSortingConditionException(final ReviewErrorCode errorCode, final String sortFieldName) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), sortFieldName)); - } - } - - public static class NotAuthorOfReviewException extends ReviewException { - public NotAuthorOfReviewException(final ReviewErrorCode errorCode, final Long memberId) { - super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), memberId)); - } - } -} diff --git a/backend/src/main/java/com/funeat/review/persistence/ReviewCustomRepository.java b/backend/src/main/java/com/funeat/review/persistence/ReviewCustomRepository.java deleted file mode 100644 index e2dd79992..000000000 --- a/backend/src/main/java/com/funeat/review/persistence/ReviewCustomRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.funeat.review.persistence; - -import com.funeat.member.domain.Member; -import com.funeat.review.domain.Review; -import com.funeat.review.dto.SortingReviewDtoWithoutTag; -import java.util.List; -import org.springframework.data.jpa.domain.Specification; - -public interface ReviewCustomRepository { - - List getSortingReview(final Member loginMember, - final Specification specification, - final String sortField); -} diff --git a/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java b/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java deleted file mode 100644 index 69d35018d..000000000 --- a/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.funeat.review.persistence; - -import static javax.persistence.LockModeType.PESSIMISTIC_WRITE; - -import com.funeat.member.domain.Member; -import com.funeat.product.domain.Product; -import com.funeat.review.domain.Review; -import java.util.List; -import java.util.Optional; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Lock; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -public interface ReviewRepository extends JpaRepository, ReviewCustomRepository { - - Long countByProduct(final Product product); - - Page findReviewsByMember(final Member findMember, final Pageable pageable); - - @Lock(PESSIMISTIC_WRITE) - @Query("SELECT r FROM Review r WHERE r.id=:id") - Optional findByIdForUpdate(final Long id); - - @Query("SELECT r " - + "FROM Review r " - + "LEFT JOIN r.product p " - + "WHERE p.id = :id AND r.image != '' " - + "ORDER BY r.favoriteCount DESC, r.id DESC") - List findPopularReviewWithImage(@Param("id") final Long productId, final Pageable pageable); - - Optional findTopByProductOrderByFavoriteCountDescIdDesc(final Product product); - - List findReviewsByFavoriteCountGreaterThanEqual(final Long favoriteCount); -} diff --git a/backend/src/main/java/com/funeat/review/persistence/ReviewRepositoryImpl.java b/backend/src/main/java/com/funeat/review/persistence/ReviewRepositoryImpl.java deleted file mode 100644 index ae47f7127..000000000 --- a/backend/src/main/java/com/funeat/review/persistence/ReviewRepositoryImpl.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.funeat.review.persistence; - -import com.funeat.member.domain.Member; -import com.funeat.member.domain.favorite.ReviewFavorite; -import com.funeat.review.domain.Review; -import com.funeat.review.dto.SortingReviewDtoWithoutTag; -import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CompoundSelection; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Repository; - -@Repository -public class ReviewRepositoryImpl implements ReviewCustomRepository { - - @PersistenceContext - private EntityManager em; - - @Override - public List getSortingReview(final Member loginMember, - final Specification specification, - final String sortOption) { - final CriteriaBuilder cb = em.getCriteriaBuilder(); - final CriteriaQuery cq = cb.createQuery(SortingReviewDtoWithoutTag.class); - final Root root = cq.from(Review.class); - - // sortField, sortOrder - final String[] sortOptionSplit = sortOption.split(","); - final String sortField = sortOptionSplit[0]; - final String sortOrder = sortOptionSplit[1]; - - // join - final Join joinMember = root.join("member", JoinType.INNER); - - // left join - final Join leftJoinReviewFavorite = root.join("reviewFavorites", JoinType.LEFT); - final Predicate condition = cb.equal(leftJoinReviewFavorite.get("member"), loginMember); - leftJoinReviewFavorite.on(condition); - - // select - from - where - order by - cq.select(getConstruct(root, cb, joinMember, leftJoinReviewFavorite)) - .where(specification.toPredicate(root, cq, cb)) - .orderBy(getOrderBy(root, cb, sortField, sortOrder)); - - // limit - final TypedQuery query = em.createQuery(cq); - query.setMaxResults(11); - - // result - return query.getResultList(); - } - - private CompoundSelection getConstruct(final Root root, - final CriteriaBuilder cb, - final Join joinMember, - final Join leftJoinReviewFavorite) { - - return cb.construct(SortingReviewDtoWithoutTag.class, - root.get("id"), - joinMember.get("nickname"), - joinMember.get("profileImage"), - root.get("image"), - root.get("rating"), - root.get("content"), - root.get("reBuy"), - root.get("favoriteCount"), - leftJoinReviewFavorite.get("favorite"), - root.get("createdAt")); - } - - private List getOrderBy(final Root root, - final CriteriaBuilder cb, - final String fieldName, - final String sortOption) { - if ("ASC".equalsIgnoreCase(sortOption)) { - final Order order = cb.asc(root.get(fieldName)); - return List.of(order, cb.desc(root.get("id"))); - } - final Order order = cb.desc(root.get(fieldName)); - return List.of(order, cb.desc(root.get("id"))); - } -} diff --git a/backend/src/main/java/com/funeat/review/persistence/ReviewTagRepository.java b/backend/src/main/java/com/funeat/review/persistence/ReviewTagRepository.java deleted file mode 100644 index cbdf3c3bf..000000000 --- a/backend/src/main/java/com/funeat/review/persistence/ReviewTagRepository.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.funeat.review.persistence; - -import com.funeat.review.domain.Review; -import com.funeat.review.domain.ReviewTag; -import com.funeat.tag.domain.Tag; -import java.util.List; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; - -public interface ReviewTagRepository extends JpaRepository { - - @Query("SELECT rt.tag, COUNT(rt.tag) AS cnt " - + "FROM ReviewTag rt " - + "JOIN Review r ON rt.review.id = r.id " - + "WHERE r.product.id = :productId " - + "GROUP BY rt.tag " - + "ORDER BY cnt DESC") - List findTop3TagsByReviewIn(final Long productId, final Pageable pageable); - - void deleteByReview(final Review review); - - List findByReview(final Review review); -} diff --git a/backend/src/main/java/com/funeat/review/presentation/ReviewApiController.java b/backend/src/main/java/com/funeat/review/presentation/ReviewApiController.java deleted file mode 100644 index ff82ff499..000000000 --- a/backend/src/main/java/com/funeat/review/presentation/ReviewApiController.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.funeat.review.presentation; - -import com.funeat.auth.dto.LoginInfo; -import com.funeat.auth.util.AuthenticationPrincipal; -import com.funeat.common.logging.Logging; -import com.funeat.review.application.ReviewService; -import com.funeat.review.dto.MostFavoriteReviewResponse; -import com.funeat.review.dto.RankingReviewsResponse; -import com.funeat.review.dto.ReviewCreateRequest; -import com.funeat.review.dto.ReviewDetailResponse; -import com.funeat.review.dto.ReviewFavoriteRequest; -import com.funeat.review.dto.SortingReviewRequest; -import com.funeat.review.dto.SortingReviewsResponse; -import java.net.URI; -import java.util.Optional; -import javax.validation.Valid; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -@RestController -public class ReviewApiController implements ReviewController { - - private final ReviewService reviewService; - - public ReviewApiController(final ReviewService reviewService) { - this.reviewService = reviewService; - } - - @Logging - @PostMapping(value = "/api/products/{productId}/reviews", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, - MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity writeReview(@PathVariable final Long productId, - @AuthenticationPrincipal final LoginInfo loginInfo, - @RequestPart(required = false) final MultipartFile image, - @RequestPart @Valid final ReviewCreateRequest reviewRequest) { - reviewService.create(productId, loginInfo.getId(), image, reviewRequest); - - return ResponseEntity.created(URI.create("/api/products/" + productId)).build(); - } - - @Logging - @PatchMapping("/api/products/{productId}/reviews/{reviewId}") - public ResponseEntity toggleLikeReview(@PathVariable final Long productId, - @PathVariable final Long reviewId, - @AuthenticationPrincipal final LoginInfo loginInfo, - @RequestBody @Valid final ReviewFavoriteRequest request) { - reviewService.likeReview(reviewId, loginInfo.getId(), request); - reviewService.updateProductImage(productId); - - return ResponseEntity.noContent().build(); - } - - @GetMapping("/api/products/{productId}/reviews") - public ResponseEntity getSortingReviews(@AuthenticationPrincipal final LoginInfo loginInfo, - @PathVariable final Long productId, - @ModelAttribute final SortingReviewRequest request) { - final SortingReviewsResponse response = reviewService.sortingReviews(productId, loginInfo.getId(), request); - - return ResponseEntity.ok(response); - } - - @GetMapping("/api/ranks/reviews") - public ResponseEntity getRankingReviews() { - final RankingReviewsResponse response = reviewService.getTopReviews(); - - return ResponseEntity.ok(response); - } - - @GetMapping("/api/ranks/products/{productId}/reviews") - public ResponseEntity> getMostFavoriteReview(@PathVariable final Long productId) { - final Optional response = reviewService.getMostFavoriteReview(productId); - - if (response.isEmpty()) { - return ResponseEntity.noContent().build(); - } - return ResponseEntity.ok(response); - } - - @GetMapping("/api/reviews/{reviewId}") - public ResponseEntity getReviewDetail(@PathVariable final Long reviewId) { - final ReviewDetailResponse response = reviewService.getReviewDetail(reviewId); - - return ResponseEntity.ok(response); - } -} diff --git a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java deleted file mode 100644 index 13fb85e94..000000000 --- a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.funeat.review.presentation; - -import com.funeat.auth.dto.LoginInfo; -import com.funeat.auth.util.AuthenticationPrincipal; -import com.funeat.review.dto.MostFavoriteReviewResponse; -import com.funeat.review.dto.RankingReviewsResponse; -import com.funeat.review.dto.ReviewCreateRequest; -import com.funeat.review.dto.ReviewDetailResponse; -import com.funeat.review.dto.ReviewFavoriteRequest; -import com.funeat.review.dto.SortingReviewRequest; -import com.funeat.review.dto.SortingReviewsResponse; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.Optional; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.multipart.MultipartFile; - -@Tag(name = "03.Review", description = "리뷰관련 API 입니다.") -public interface ReviewController { - - @Operation(summary = "리뷰 추가", description = "리뷰를 작성한다.") - @ApiResponse( - responseCode = "201", - description = "리뷰 작성 성공." - ) - @PostMapping - ResponseEntity writeReview(@PathVariable final Long productId, - @AuthenticationPrincipal final LoginInfo loginInfo, - @RequestPart final MultipartFile image, - @RequestPart final ReviewCreateRequest reviewRequest); - - @Operation(summary = "리뷰 좋아요", description = "리뷰에 좋아요 또는 취소를 한다.") - @ApiResponse( - responseCode = "204", - description = "리뷰 좋아요(취소) 성공." - ) - @PatchMapping - ResponseEntity toggleLikeReview(@PathVariable final Long productId, - @PathVariable final Long reviewId, - @AuthenticationPrincipal final LoginInfo loginInfo, - @RequestBody final ReviewFavoriteRequest request); - - @Operation(summary = "리뷰를 정렬후 조회", description = "리뷰를 정렬후 조회한다.") - @ApiResponse( - responseCode = "200", - description = "리뷰 정렬후 조회 성공." - ) - @GetMapping - ResponseEntity getSortingReviews(@AuthenticationPrincipal final LoginInfo loginInfo, - @PathVariable final Long productId, - @ModelAttribute final SortingReviewRequest request); - - @Operation(summary = "리뷰 랭킹 Top3 조회", description = "리뷰 랭킹 Top3 조회한다.") - @ApiResponse( - responseCode = "200", - description = "리뷰 랭킹 Top3 조회 성공." - ) - @GetMapping - ResponseEntity getRankingReviews(); - - @Operation(summary = "좋아요를 제일 많은 받은 리뷰 조회", description = "특정 상품에 대해 좋아요를 제일 많이 받은 리뷰를 조회한다.") - @ApiResponse( - responseCode = "200", - description = "좋아요를 제일 많이 받은 리뷰 조회 성공." - ) - @GetMapping - ResponseEntity> getMostFavoriteReview(@PathVariable final Long productId); - - @Operation(summary = "리뷰 상세 조회", description = "리뷰의 상세 정보를 조회한다.") - @ApiResponse( - responseCode = "200", - description = "리뷰 상세 조회 성공." - ) - @GetMapping - ResponseEntity getReviewDetail(@PathVariable final Long reviewId); -} diff --git a/backend/src/main/java/com/funeat/review/specification/LongTypeReviewSortSpec.java b/backend/src/main/java/com/funeat/review/specification/LongTypeReviewSortSpec.java deleted file mode 100644 index 23914e003..000000000 --- a/backend/src/main/java/com/funeat/review/specification/LongTypeReviewSortSpec.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.funeat.review.specification; - -import com.funeat.review.domain.Review; -import java.util.Arrays; -import java.util.function.Function; - -public enum LongTypeReviewSortSpec { - - FAVORITE_COUNT("favoriteCount", Review::getFavoriteCount), - RATING("rating", Review::getRating); - - private final String fieldName; - private final Function function; - - LongTypeReviewSortSpec(final String fieldName, final Function function) { - this.fieldName = fieldName; - this.function = function; - } - - public static Long find(final String fieldName, final Review lastReview) { - return Arrays.stream(LongTypeReviewSortSpec.values()) - .filter(reviewSortSpec -> reviewSortSpec.fieldName.equals(fieldName)) - .findFirst() - .orElseThrow(IllegalArgumentException::new) - .function.apply(lastReview); - } -} diff --git a/backend/src/main/java/com/funeat/review/specification/SortingReviewSpecification.java b/backend/src/main/java/com/funeat/review/specification/SortingReviewSpecification.java deleted file mode 100644 index 781032017..000000000 --- a/backend/src/main/java/com/funeat/review/specification/SortingReviewSpecification.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.funeat.review.specification; - -import static com.funeat.review.exception.ReviewErrorCode.NOT_SUPPORTED_REVIEW_SORTING_CONDITION; - -import com.funeat.product.domain.Product; -import com.funeat.review.domain.Review; -import com.funeat.review.exception.ReviewException.NotSupportedReviewSortingConditionException; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Objects; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import org.springframework.data.jpa.domain.Specification; - -public final class SortingReviewSpecification { - - private static final List LOCALDATETIME_TYPE_INCLUDE = List.of("createdAt"); - private static final List LONG_TYPE_INCLUDE = List.of("favoriteCount", "rating"); - private static final String DELIMITER = ","; - private static final String PRODUCT = "product"; - private static final String ID = "id"; - private static final String ASC = "ASC"; - - private SortingReviewSpecification() { - } - - public static Specification sortingFirstPageBy(final Product product) { - return (root, query, criteriaBuilder) -> Specification - .where(equalsProduct(product)) - .toPredicate(root, query, criteriaBuilder); - } - - public static Specification sortingBy(final Product product, final String sortOption, - final Review lastReview) { - return (root, query, criteriaBuilder) -> { - final String[] sortFieldSplit = sortOption.split(DELIMITER); - final String field = sortFieldSplit[0]; - final String sort = sortFieldSplit[1]; - - return Specification - .where((equalsProduct(product).and(equals(field, lastReview)).and(lessThanLastReviewId(lastReview))) - .or(equalsProduct(product).and(lessOrGreaterThan(field, sort, lastReview)))) - .toPredicate(root, query, criteriaBuilder); - }; - } - - private static Specification equalsProduct(final Product product) { - return (root, query, criteriaBuilder) -> { - if (Objects.isNull(product)) { - return null; - } - - final Path productPath = root.get(PRODUCT); - - return criteriaBuilder.equal(productPath, product); - }; - } - - private static Specification lessThanLastReviewId(final Review lastReview) { - return (root, query, criteriaBuilder) -> { - if (Objects.isNull(lastReview)) { - return null; - } - - final Path reviewPath = root.get(ID); - - return criteriaBuilder.lessThan(reviewPath, lastReview.getId()); - }; - } - - private static Specification equals(final String fieldName, final Review lastReview) { - return (root, query, criteriaBuilder) -> { - if (validateNull(fieldName, lastReview)) { - return null; - } - - return checkEquals(fieldName, lastReview, root, criteriaBuilder); - }; - } - - private static Predicate checkEquals(final String fieldName, - final Review lastReview, - final Root root, - final CriteriaBuilder criteriaBuilder) { - if (LOCALDATETIME_TYPE_INCLUDE.contains(fieldName)) { - final Path createdAtPath = root.get(fieldName); - final LocalDateTime lastReviewCreatedAt = lastReview.getCreatedAt(); - return criteriaBuilder.equal(createdAtPath, lastReviewCreatedAt); - } - if (LONG_TYPE_INCLUDE.contains(fieldName)) { - final Path reviewPath = root.get(fieldName); - final Long lastReviewField = LongTypeReviewSortSpec.find(fieldName, lastReview); - return criteriaBuilder.equal(reviewPath, lastReviewField); - } - throw new NotSupportedReviewSortingConditionException(NOT_SUPPORTED_REVIEW_SORTING_CONDITION, fieldName); - } - - private static Specification lessOrGreaterThan(final String field, final String sort, - final Review lastReview) { - if (ASC.equalsIgnoreCase(sort)) { - return greaterThan(field, lastReview); - } - return lessThan(field, lastReview); - } - - private static Specification greaterThan(final String fieldName, final Review lastReview) { - return (root, query, criteriaBuilder) -> { - if (validateNull(fieldName, lastReview)) { - return null; - } - - return checkGreaterThan(fieldName, lastReview, root, criteriaBuilder); - }; - } - - private static Predicate checkGreaterThan(final String fieldName, final Review lastReview, final Root root, - final CriteriaBuilder criteriaBuilder) { - if (LOCALDATETIME_TYPE_INCLUDE.contains(fieldName)) { - final Path createdAtPath = root.get(fieldName); - final LocalDateTime lastReviewCreatedAt = lastReview.getCreatedAt(); - return criteriaBuilder.greaterThan(createdAtPath, lastReviewCreatedAt); - } - if (LONG_TYPE_INCLUDE.contains(fieldName)) { - final Path reviewPath = root.get(fieldName); - final Long lastReviewField = LongTypeReviewSortSpec.find(fieldName, lastReview); - return criteriaBuilder.greaterThan(reviewPath, lastReviewField); - } - throw new NotSupportedReviewSortingConditionException(NOT_SUPPORTED_REVIEW_SORTING_CONDITION, fieldName); - } - - private static Specification lessThan(final String fieldName, final Review lastReview) { - return (root, query, criteriaBuilder) -> { - if (validateNull(fieldName, lastReview)) { - return null; - } - - return checkLessThan(fieldName, lastReview, root, criteriaBuilder); - }; - } - - private static boolean validateNull(final String fieldName, final Review lastReview) { - return Objects.isNull(fieldName) || Objects.isNull(lastReview); - } - - private static Predicate checkLessThan(final String fieldName, final Review lastReview, final Root root, - final CriteriaBuilder criteriaBuilder) { - if (LOCALDATETIME_TYPE_INCLUDE.contains(fieldName)) { - final Path createdAtPath = root.get(fieldName); - final LocalDateTime lastReviewCreatedAt = lastReview.getCreatedAt(); - return criteriaBuilder.lessThan(createdAtPath, lastReviewCreatedAt); - } - if (LONG_TYPE_INCLUDE.contains(fieldName)) { - final Path reviewPath = root.get(fieldName); - final Long lastReviewField = LongTypeReviewSortSpec.find(fieldName, lastReview); - return criteriaBuilder.lessThan(reviewPath, lastReviewField); - } - throw new NotSupportedReviewSortingConditionException(NOT_SUPPORTED_REVIEW_SORTING_CONDITION, fieldName); - } -} diff --git a/backend/src/main/java/com/funeat/tag/application/TagService.java b/backend/src/main/java/com/funeat/tag/application/TagService.java deleted file mode 100644 index efb15849d..000000000 --- a/backend/src/main/java/com/funeat/tag/application/TagService.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.funeat.tag.application; - -import com.funeat.tag.domain.TagType; -import com.funeat.tag.dto.TagDto; -import com.funeat.tag.dto.TagsResponse; -import com.funeat.tag.persistence.TagRepository; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@Transactional(readOnly = true) -public class TagService { - - private final TagRepository tagRepository; - - public TagService(final TagRepository tagRepository) { - this.tagRepository = tagRepository; - } - - public List getAllTags() { - final List responses = new ArrayList<>(); - for (final TagType tagType : TagType.values()) { - responses.add(getTagsByTagType(tagType)); - } - return responses; - } - - private TagsResponse getTagsByTagType(final TagType tagType) { - final List tags = tagRepository.findTagsByTagType(tagType).stream() - .map(TagDto::toDto) - .collect(Collectors.toList()); - return TagsResponse.toResponse(tagType.name(), tags); - } -} diff --git a/backend/src/main/java/com/funeat/tag/domain/Tag.java b/backend/src/main/java/com/funeat/tag/domain/Tag.java deleted file mode 100644 index 554b8db6f..000000000 --- a/backend/src/main/java/com/funeat/tag/domain/Tag.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.funeat.tag.domain; - -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; - -@Entity -public class Tag { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String name; - - @Enumerated(EnumType.STRING) - private TagType tagType; - - protected Tag() { - } - - public Tag(final String name, final TagType tagType) { - this.name = name; - this.tagType = tagType; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public TagType getTagType() { - return tagType; - } -} diff --git a/backend/src/main/java/com/funeat/tag/domain/TagType.java b/backend/src/main/java/com/funeat/tag/domain/TagType.java deleted file mode 100644 index 859526546..000000000 --- a/backend/src/main/java/com/funeat/tag/domain/TagType.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.funeat.tag.domain; - -public enum TagType { - - TASTE, QUANTITY, ETC -} diff --git a/backend/src/main/java/com/funeat/tag/dto/TagDto.java b/backend/src/main/java/com/funeat/tag/dto/TagDto.java deleted file mode 100644 index 45a09c5f1..000000000 --- a/backend/src/main/java/com/funeat/tag/dto/TagDto.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.funeat.tag.dto; - -import com.funeat.tag.domain.Tag; -import com.funeat.tag.domain.TagType; - -public class TagDto { - - private final Long id; - private final String name; - private final TagType tagType; - - public TagDto(final Long id, final String name, final TagType tagType) { - this.id = id; - this.name = name; - this.tagType = tagType; - } - - public static TagDto toDto(final Tag tag) { - return new TagDto(tag.getId(), tag.getName(), tag.getTagType()); - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public TagType getTagType() { - return tagType; - } -} diff --git a/backend/src/main/java/com/funeat/tag/dto/TagsResponse.java b/backend/src/main/java/com/funeat/tag/dto/TagsResponse.java deleted file mode 100644 index e81f29e10..000000000 --- a/backend/src/main/java/com/funeat/tag/dto/TagsResponse.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.funeat.tag.dto; - -import java.util.List; - -public class TagsResponse { - - private final String tagType; - private final List tags; - - public TagsResponse(final String tagType, final List tags) { - this.tagType = tagType; - this.tags = tags; - } - - public static TagsResponse toResponse(final String tagType, final List tags) { - return new TagsResponse(tagType, tags); - } - - public String getTagType() { - return tagType; - } - - public List getTags() { - return tags; - } -} diff --git a/backend/src/main/java/com/funeat/tag/persistence/TagRepository.java b/backend/src/main/java/com/funeat/tag/persistence/TagRepository.java deleted file mode 100644 index 9ad319f7a..000000000 --- a/backend/src/main/java/com/funeat/tag/persistence/TagRepository.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.funeat.tag.persistence; - -import com.funeat.tag.domain.Tag; -import com.funeat.tag.domain.TagType; -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -public interface TagRepository extends JpaRepository { - - List findTagsByIdIn(final List tagIds); - - List findTagsByTagType(final TagType tagType); - - @Query("SELECT t " - + "FROM ReviewTag rt " - + "JOIN rt.tag t " - + "WHERE rt.review.id = :reviewId") - List findTagsByReviewId(@Param("reviewId") final Long reviewId); -} diff --git a/backend/src/main/java/com/funeat/tag/presentation/TagApiController.java b/backend/src/main/java/com/funeat/tag/presentation/TagApiController.java deleted file mode 100644 index b8e5c6b69..000000000 --- a/backend/src/main/java/com/funeat/tag/presentation/TagApiController.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.funeat.tag.presentation; - -import com.funeat.tag.application.TagService; -import com.funeat.tag.dto.TagsResponse; -import java.util.List; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class TagApiController implements TagController { - - private final TagService tagService; - - public TagApiController(final TagService tagService) { - this.tagService = tagService; - } - - @GetMapping("/api/tags") - public ResponseEntity> getAllTags() { - final List responses = tagService.getAllTags(); - return ResponseEntity.ok(responses); - } -} diff --git a/backend/src/main/java/com/funeat/tag/presentation/TagController.java b/backend/src/main/java/com/funeat/tag/presentation/TagController.java deleted file mode 100644 index a0e2377c8..000000000 --- a/backend/src/main/java/com/funeat/tag/presentation/TagController.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.funeat.tag.presentation; - -import com.funeat.tag.dto.TagsResponse; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.List; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; - -@Tag(name = "04.Tag", description = "태그 기능") -public interface TagController { - - @Operation(summary = "전체 태그 목록 조회", description = "전체 태그 목록을 태그 타입 별로 조회한다.") - @ApiResponse( - responseCode = "200", - description = "전체 태그 목록 조회 성공." - ) - @GetMapping - ResponseEntity> getAllTags(); -} diff --git a/backend/src/main/resources/application-dev.yml b/backend/src/main/resources/application-dev.yml deleted file mode 100644 index 0c1a72220..000000000 --- a/backend/src/main/resources/application-dev.yml +++ /dev/null @@ -1,46 +0,0 @@ -spring: - datasource: - driver-class-name: com.mysql.cj.jdbc.Driver - url: { DEV_DB_URL } - username: { DEV_DB_USERNAME } - password: { DEV_DB_PASSWORD } - - jpa: - hibernate: - ddl-auto: update - properties: - hibernate: - format_sql: true - show_sql: true - -kakao: - rest-api-key: { DEV_REST_API_KEY } - redirect-uri: { DEV_REDIRECT_URI } - admin-key: { DEV_ADMIN_KEY } - -management: - endpoints: - enabled-by-default: false - web: - exposure: - include: health, metrics, prometheus - base-path: { ACTUATOR_BASE_PATH } - jmx: - exposure: - exclude: "*" - endpoint: - health: - enabled: true - metrics: - enabled: true - prometheus: - enabled: true - -cloud: - aws: - region: - static: { S3_REGION } - s3: - bucket: { S3_BUCKET } - folder: { S3_DEV_FOLDER } - cloudfrontPath: { S3_DEV_CLOUDFRONT_PATH } diff --git a/backend/src/main/resources/application-local.yml b/backend/src/main/resources/application-local.yml deleted file mode 100644 index 27ba9bccb..000000000 --- a/backend/src/main/resources/application-local.yml +++ /dev/null @@ -1,31 +0,0 @@ -spring: - datasource: - driver-class-name: com.mysql.cj.jdbc.Driver - url: - username: - password: - - jpa: - hibernate: - ddl-auto: create - properties: - hibernate: - format_sql: true - show_sql: true -logging: - level: - org.hibernate.type.descriptor.sql: trace - -kakao: - rest-api-key: { LOCAL_REST_API_KEY } - redirect-uri: { LOCAL_REDIRECT_URI } - admin-key: { LOCAL_ADMIN_KEY } - -cloud: - aws: - region: - static: { S3_REGION } - s3: - bucket: { S3_BUCKET } - folder: { S3_LOCAL_FOLDER } - cloudfrontPath: { S3_LOCAL_CLOUDFRONT_PATH } diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml deleted file mode 100644 index 1943e2bf8..000000000 --- a/backend/src/main/resources/application-prod.yml +++ /dev/null @@ -1,45 +0,0 @@ -spring: - datasource: - driver-class-name: com.mysql.cj.jdbc.Driver - url: { PROD_DB_URL } - username: { PROD_DB_USERNAME } - password: { PROD_DB_PASSWORD } - - jpa: - hibernate: - ddl-auto: none - properties: - hibernate: - show_sql: true - -kakao: - rest-api-key: { PROD_REST_API_KEY } - redirect-uri: { PROD_REDIRECT_URI } - admin-key: { PROD_ADMIN_KEY } - -management: - endpoints: - enabled-by-default: false - web: - exposure: - include: health, metrics, prometheus - base-path: { ACTUATOR_BASE_PATH } - jmx: - exposure: - exclude: "*" - endpoint: - health: - enabled: true - metrics: - enabled: true - prometheus: - enabled: true - -cloud: - aws: - region: - static: { S3_REGION } - s3: - bucket: { S3_BUCKET } - folder: { S3_PROD_FOLDER } - cloudfrontPath: { S3_PROD_CLOUDFRONT_PATH } diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml deleted file mode 100644 index 941adb9cf..000000000 --- a/backend/src/main/resources/application.yml +++ /dev/null @@ -1,42 +0,0 @@ -spring: - profiles: - active: { DEPLOY_ACTIVE } - servlet: - multipart: - enabled: true - maxFileSize: 10MB - maxRequestSize: 15MB - task: - execution: - pool: - core-size: { THREAD_CORE_SIZE } - max-size: { THREAD_MAX_SIZE } - session: - store-type: jdbc - jdbc: - initialize-schema: never - datasource: - hikari: - connection-timeout: { CONNECTION_TIMEOUT } - maximum-pool-size: { MAXIMUM_POOL_SIZE } - -springdoc: - swagger-ui: - path: /funeat-api - enabled: true - tags-sorter: alpha - -logging: - file: - path: { LOG_DIR } - -server: - tomcat: - threads: - max: { MAX_THREADS } - max-connections: { MAX_CONNECTIONS } - accept-count: { ACCEPT_COUNT } - -back-office: - id: { BACK_OFFICE_ID } - key: { BACK_OFFICE_KEY } diff --git a/backend/src/main/resources/logback-spring-dev.xml b/backend/src/main/resources/logback-spring-dev.xml deleted file mode 100644 index b86839da1..000000000 --- a/backend/src/main/resources/logback-spring-dev.xml +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - ${dev_slack_webhook_uri} - - ${log_pattern} - - open-macbook - :face_with_symbols_on_mouth: - true - - - - - - WARN - - - - - - INFO - ACCEPT - DENY - - ${log_dir}/info.log - - - ${log_dir}/info/info.%d{yyyy-MM-dd}_%i.log - - ${dev_file_size} - ${dev_file_max_history} - - - - ${log_pattern} - - true - - - - - - WARN - ACCEPT - DENY - - ${log_dir}/warn.log - - - ${log_dir}/warn/%d{yyyy-MM-dd}_%i.log - - ${dev_file_size} - ${dev_file_max_history} - - - - ${log_pattern} - - true - - - - - - ERROR - ACCEPT - DENY - - ${log_dir}/error.log - - - ${log_dir}/error/%d{yyyy-MM-dd}_%i.log - - ${dev_file_size} - ${dev_file_max_history} - - - - ${log_pattern} - - true - - - - - ${log_dir}/query_log.log - - - ${log_dir}/query/%d{yyyy-MM-dd}_%i.log - - 10kb - 1 - - - - ${log_pattern} - - true - - - - - - - - - - - - - - - - - - diff --git a/backend/src/main/resources/logback-spring-prod.xml b/backend/src/main/resources/logback-spring-prod.xml deleted file mode 100644 index 101ba8d7f..000000000 --- a/backend/src/main/resources/logback-spring-prod.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - ${prod_slack_webhook_uri} - - ${log_pattern} - - open-macbook - :face_with_symbols_on_mouth: - true - - - - - - ERROR - - - - - - INFO - ACCEPT - DENY - - ${log_dir}/info.log - - - ${log_dir}/info/info.%d{yyyy-MM-dd}_%i.log - - ${prod_file_size} - ${prod_file_max_history} - - - - ${log_pattern} - - true - - - - - - WARN - ACCEPT - DENY - - ${log_dir}/warn.log - - - ${log_dir}/warn/%d{yyyy-MM-dd}_%i.log - - ${prod_file_size} - ${prod_file_max_history} - - - - ${log_pattern} - - true - - - - - - ERROR - ACCEPT - DENY - - ${log_dir}/error.log - - - ${log_dir}/error/%d{yyyy-MM-dd}_%i.log - - ${prod_file_size} - ${prod_file_max_history} - - - - ${log_pattern} - - true - - - - - - - - - - - diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml deleted file mode 100644 index ccea7008c..000000000 --- a/backend/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/backend/src/main/resources/logback-variables.yml b/backend/src/main/resources/logback-variables.yml deleted file mode 100644 index 823342938..000000000 --- a/backend/src/main/resources/logback-variables.yml +++ /dev/null @@ -1,7 +0,0 @@ -log_pattern: { LOG_PATTERN } -dev_slack_webhook_uri: { DEV_SLACK_WEBHOOK_URI } -dev_file_size: { DEV_FILE_SIZE } -dev_file_max_history: { DEV_FILE_MAX_HISTORY } -prod_slack_webhook_uri: { PROD_SLACK_WEBHOOK_URI } -prod_file_size: { PROD_FILE_SIZE } -prod_file_max_history: { PROD_FILE_MAX_HISTORY } diff --git a/backend/src/test/java/com/funeat/FuneatApplicationTests.java b/backend/src/test/java/com/funeat/FuneatApplicationTests.java deleted file mode 100644 index 6648db315..000000000 --- a/backend/src/test/java/com/funeat/FuneatApplicationTests.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.funeat; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class FuneatApplicationTests { - - @Test - void contextLoads() { - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/auth/AuthAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/auth/AuthAcceptanceTest.java deleted file mode 100644 index 77270c589..000000000 --- a/backend/src/test/java/com/funeat/acceptance/auth/AuthAcceptanceTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.funeat.acceptance.auth; - -import static com.funeat.acceptance.auth.LoginSteps.로그아웃_요청; -import static com.funeat.acceptance.auth.LoginSteps.로그인_시도_요청; -import static com.funeat.acceptance.auth.LoginSteps.로그인_쿠키_획득; -import static com.funeat.acceptance.auth.LoginSteps.카카오_로그인_버튼_클릭; -import static com.funeat.acceptance.common.CommonSteps.LOCATION_헤더에서_리다이렉트_주소_추출; -import static com.funeat.acceptance.common.CommonSteps.REDIRECT_URL을_검증한다; -import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; -import static com.funeat.acceptance.common.CommonSteps.리다이렉션_영구_이동; -import static com.funeat.acceptance.common.CommonSteps.인증되지_않음; -import static com.funeat.acceptance.common.CommonSteps.정상_처리; -import static com.funeat.fixture.MemberFixture.멤버1; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.acceptance.common.AcceptanceTest; -import com.funeat.auth.application.AuthService; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullAndEmptySource; -import org.springframework.beans.factory.annotation.Autowired; - -@SuppressWarnings("NonAsciiCharacters") -public class AuthAcceptanceTest extends AcceptanceTest { - - private static final String 마이페이지 = "/members"; - private static final String 메인페이지 = "/"; - - @Autowired - private AuthService authService; - - @Nested - class login_성공_테스트 { - - @Test - void 멤버가_로그인_버튼을_누르면_OAUTH_로그인_페이지로_리다이렉트할_수_있다() { - // given - final var OAUTH_로그인_페이지 = authService.getLoginRedirectUri(); - - // when - final var 응답 = 카카오_로그인_버튼_클릭(); - - // then - STATUS_CODE를_검증한다(응답, 리다이렉션_영구_이동); - REDIRECT_URL을_검증한다(응답, OAUTH_로그인_페이지); - } - } - - @Nested - class loginAuthorizeUser_성공_테스트 { - - @Test - void 신규_유저라면_마이페이지_경로를_헤더에_담아_응답을_보낸다() { - // given & when - final var 응답 = 로그인_시도_요청(멤버1); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 헤더에_리다이렉트가_존재하는지_검증한다(응답, 마이페이지); - } - - @Test - void 기존_유저라면_메인페이지_경로를_헤더에_담아_응답을_보낸다() { - // given - 로그인_쿠키_획득(멤버1); - - // when - final var 응답 = 로그인_시도_요청(멤버1); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 헤더에_리다이렉트가_존재하는지_검증한다(응답, 메인페이지); - } - } - - @Nested - class logout_성공_테스트 { - - @Test - void 로그아웃을_하다() { - // given & when - final var 응답 = 로그아웃_요청(로그인_쿠키_획득(멤버1)); - - // then - STATUS_CODE를_검증한다(응답, 리다이렉션_영구_이동); - REDIRECT_URL을_검증한다(응답, 메인페이지); - } - } - - @Nested - class logout_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 쿠키가_존재하지_않을_때_로그아웃을_하면_예외가_발생한다(final String cookie) { - // given & when - final var 응답 = 로그아웃_요청(cookie); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - } - } - - private void 헤더에_리다이렉트가_존재하는지_검증한다(final ExtractableResponse response, final String expected) { - final var actual = LOCATION_헤더에서_리다이렉트_주소_추출(response); - - assertThat(actual).isEqualTo(expected); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/auth/LoginSteps.java b/backend/src/test/java/com/funeat/acceptance/auth/LoginSteps.java deleted file mode 100644 index 92d2f48c7..000000000 --- a/backend/src/test/java/com/funeat/acceptance/auth/LoginSteps.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.funeat.acceptance.auth; - -import static io.restassured.RestAssured.given; - -import io.restassured.RestAssured; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; - -@SuppressWarnings("NonAsciiCharacters") -public class LoginSteps { - - public static ExtractableResponse 카카오_로그인_버튼_클릭() { - return given() - .redirects().follow(false) - .when() - .get("/api/auth/kakao") - .then() - .extract(); - } - - public static ExtractableResponse 로그인_시도_요청(final Long memberId) { - return given() - .param("code", String.valueOf(memberId)) - .when() - .get("/api/login/oauth2/code/kakao") - .then() - .extract(); - } - - public static ExtractableResponse 로그아웃_요청(final String loginCookie) { - return given() - .cookie("SESSION", loginCookie) - .when() - .post("/api/logout") - .then() - .extract(); - } - - public static String 로그인_쿠키_획득(final Long memberId) { - return RestAssured.given() - .queryParam("code", String.valueOf(memberId)) - .when() - .get("/api/login/oauth2/code/kakao") - .then() - .extract() - .response() - .getCookie("SESSION"); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/banner/BannerAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/banner/BannerAcceptanceTest.java deleted file mode 100644 index e5b3a39fd..000000000 --- a/backend/src/test/java/com/funeat/acceptance/banner/BannerAcceptanceTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.funeat.acceptance.banner; - -import static com.funeat.acceptance.banner.BannerSteps.배너_목록_조회_요청; -import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; -import static com.funeat.acceptance.common.CommonSteps.정상_처리; -import static com.funeat.fixture.BannerFixture.배너1_생성; -import static com.funeat.fixture.BannerFixture.배너2_생성; -import static com.funeat.fixture.BannerFixture.배너3_생성; -import static com.funeat.fixture.BannerFixture.배너4_생성; -import static com.funeat.fixture.BannerFixture.배너5_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.acceptance.common.AcceptanceTest; -import com.funeat.banner.domain.Banner; -import com.funeat.banner.dto.BannerResponse; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -public class BannerAcceptanceTest extends AcceptanceTest { - - @Nested - class getBanners_성공_테스트 { - - @Test - void 배너를_아이디_내림차순으로_전체_조회한다() { - // given - final var 배너1 = 배너1_생성(); - final var 배너2 = 배너2_생성(); - final var 배너3 = 배너3_생성(); - final var 배너4 = 배너4_생성(); - final var 배너5 = 배너5_생성(); - final var 생성할_배너_리스트 = Arrays.asList(배너1, 배너2, 배너3, 배너4, 배너5); - bannerRepository.saveAll(생성할_배너_리스트); - - // when - final var 응답 = 배너_목록_조회_요청(); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 배너_조회_결과를_검증한다(응답, 생성할_배너_리스트); - } - } - - private void 배너_조회_결과를_검증한다(final ExtractableResponse response, - final List expected) { - List expectedResponse = new ArrayList<>(); - for (int i = expected.size() - 1; i >= 0; i--) { - expectedResponse.add(BannerResponse.toResponse(expected.get(i))); - } - - final List result = response.jsonPath().getList("$", BannerResponse.class); - assertThat(result).usingRecursiveComparison() - .ignoringFields("id") - .isEqualTo(expectedResponse); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/banner/BannerSteps.java b/backend/src/test/java/com/funeat/acceptance/banner/BannerSteps.java deleted file mode 100644 index 6fbc155f9..000000000 --- a/backend/src/test/java/com/funeat/acceptance/banner/BannerSteps.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.funeat.acceptance.banner; - -import io.restassured.RestAssured; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; - -@SuppressWarnings("NonAsciiCharacters") -public class BannerSteps { - - public static ExtractableResponse 배너_목록_조회_요청() { - return RestAssured.given() - .when() - .get("/api/banners") - .then() - .extract(); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java deleted file mode 100644 index 2e5be6b6d..000000000 --- a/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.funeat.acceptance.common; - -import com.funeat.banner.persistence.BannerRepository; -import com.funeat.comment.persistence.CommentRepository; -import com.funeat.common.DataClearExtension; -import com.funeat.member.domain.Member; -import com.funeat.member.persistence.MemberRepository; -import com.funeat.member.persistence.RecipeFavoriteRepository; -import com.funeat.member.persistence.ReviewFavoriteRepository; -import com.funeat.product.domain.Category; -import com.funeat.product.domain.Product; -import com.funeat.product.persistence.CategoryRepository; -import com.funeat.product.persistence.ProductRecipeRepository; -import com.funeat.product.persistence.ProductRepository; -import com.funeat.recipe.persistence.RecipeImageRepository; -import com.funeat.recipe.persistence.RecipeRepository; -import com.funeat.review.persistence.ReviewRepository; -import com.funeat.review.persistence.ReviewTagRepository; -import com.funeat.tag.domain.Tag; -import com.funeat.tag.persistence.TagRepository; -import io.restassured.RestAssured; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.server.LocalServerPort; - -@ExtendWith(DataClearExtension.class) -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(ReplaceUnderscores.class) -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -public abstract class AcceptanceTest { - - @LocalServerPort - private int port; - - @Autowired - protected ProductRepository productRepository; - - @Autowired - protected CategoryRepository categoryRepository; - - @Autowired - protected MemberRepository memberRepository; - - @Autowired - protected ReviewRepository reviewRepository; - - @Autowired - protected TagRepository tagRepository; - - @Autowired - protected ReviewTagRepository reviewTagRepository; - - @Autowired - protected ReviewFavoriteRepository reviewFavoriteRepository; - - @Autowired - protected RecipeRepository recipeRepository; - - @Autowired - protected RecipeImageRepository recipeImageRepository; - - @Autowired - protected ProductRecipeRepository productRecipeRepository; - - @Autowired - protected RecipeFavoriteRepository recipeFavoriteRepository; - - @Autowired - protected BannerRepository bannerRepository; - - @Autowired - protected CommentRepository commentRepository; - - @BeforeEach - void setUp() { - RestAssured.port = port; - } - - protected Long 단일_상품_저장(final Product product) { - return productRepository.save(product).getId(); - } - - protected Long 단일_카테고리_저장(final Category category) { - return categoryRepository.save(category).getId(); - } - - protected Long 단일_멤버_저장(final Member member) { - return memberRepository.save(member).getId(); - } - - protected Long 단일_태그_저장(final Tag tag) { - return tagRepository.save(tag).getId(); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java b/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java deleted file mode 100644 index af33aa14f..000000000 --- a/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.funeat.acceptance.common; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.common.dto.PageDto; -import io.restassured.builder.MultiPartSpecBuilder; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import io.restassured.specification.MultiPartSpecification; -import java.util.ArrayList; -import java.util.List; -import org.springframework.http.HttpStatus; - -@SuppressWarnings("NonAsciiCharacters") -public class CommonSteps { - - private static final String LOCATION = "Location"; - public static final HttpStatus 정상_처리 = HttpStatus.OK; - public static final HttpStatus 정상_생성 = HttpStatus.CREATED; - public static final HttpStatus 정상_처리_NO_CONTENT = HttpStatus.NO_CONTENT; - public static final HttpStatus 리다이렉션_영구_이동 = HttpStatus.FOUND; - public static final HttpStatus 인증되지_않음 = HttpStatus.UNAUTHORIZED; - public static final HttpStatus 잘못된_요청 = HttpStatus.BAD_REQUEST; - public static final HttpStatus 찾을수_없음 = HttpStatus.NOT_FOUND; - - public static Long LOCATION_헤더에서_ID_추출(final ExtractableResponse response) { - return Long.parseLong(response.header(LOCATION).split("/")[2]); - } - - public static String LOCATION_헤더에서_리다이렉트_주소_추출(final ExtractableResponse response) { - return response.header(LOCATION); - } - - public static void LOCATION_헤더를_검증한다(final ExtractableResponse response) { - assertThat(response.header("Location")).isNotBlank(); - } - - public static void STATUS_CODE를_검증한다(final ExtractableResponse response, final HttpStatus httpStatus) { - assertThat(response.statusCode()).isEqualTo(httpStatus.value()); - } - - public static void REDIRECT_URL을_검증한다(final ExtractableResponse response, final String expected) { - final var actual = LOCATION_헤더에서_리다이렉트_주소_추출(response); - - assertThat(actual).isEqualTo(expected); - } - - public static MultiPartSpecification 사진_명세_요청(final String name) { - return new MultiPartSpecBuilder("image".getBytes()) - .fileName(String.format("%s.png", name)) - .controlName("image") - .mimeType("image/png") - .build(); - } - - public static List 여러개_사진_명세_요청(final String... names) { - final var images = new ArrayList(); - - for (final String name : names) { - images.add(new MultiPartSpecBuilder("image".getBytes()) - .fileName(String.format("%s.png", name)) - .controlName("image") - .mimeType("image/png") - .build() - ); - } - - return images; - } - - public static void 페이지를_검증한다(final ExtractableResponse response, final PageDto expected) { - final var actual = response.jsonPath().getObject("page", PageDto.class); - - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - public static void 다음_데이터가_있는지_검증한다(final ExtractableResponse response, final boolean expected) { - final var actual = response.jsonPath().getBoolean("hasNext"); - - assertThat(actual).isEqualTo(expected); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/common/TestPlatformUserProvider.java b/backend/src/test/java/com/funeat/acceptance/common/TestPlatformUserProvider.java deleted file mode 100644 index 269e27d55..000000000 --- a/backend/src/test/java/com/funeat/acceptance/common/TestPlatformUserProvider.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.funeat.acceptance.common; - -import com.funeat.auth.dto.UserInfoDto; -import com.funeat.auth.util.PlatformUserProvider; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Component; - -@Component -@Profile("test") -public class TestPlatformUserProvider implements PlatformUserProvider { - - @Override - public UserInfoDto getPlatformUser(final String code) { - return new UserInfoDto(Long.valueOf(code), String.format("member%s", code), - String.format("www.member%s.com", code)); - } - - @Override - public String getRedirectURI() { - return "www.test.com"; - } - - @Override - public void logout(final String platformId) { - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/member/MemberAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/member/MemberAcceptanceTest.java deleted file mode 100644 index b2a94b2c0..000000000 --- a/backend/src/test/java/com/funeat/acceptance/member/MemberAcceptanceTest.java +++ /dev/null @@ -1,431 +0,0 @@ -package com.funeat.acceptance.member; - -import static com.funeat.acceptance.auth.LoginSteps.로그인_쿠키_획득; -import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; -import static com.funeat.acceptance.common.CommonSteps.사진_명세_요청; -import static com.funeat.acceptance.common.CommonSteps.여러개_사진_명세_요청; -import static com.funeat.acceptance.common.CommonSteps.인증되지_않음; -import static com.funeat.acceptance.common.CommonSteps.잘못된_요청; -import static com.funeat.acceptance.common.CommonSteps.정상_처리; -import static com.funeat.acceptance.common.CommonSteps.정상_처리_NO_CONTENT; -import static com.funeat.acceptance.common.CommonSteps.찾을수_없음; -import static com.funeat.acceptance.common.CommonSteps.페이지를_검증한다; -import static com.funeat.acceptance.member.MemberSteps.리뷰_삭제_요청; -import static com.funeat.acceptance.member.MemberSteps.사용자_꿀조합_조회_요청; -import static com.funeat.acceptance.member.MemberSteps.사용자_리뷰_조회_요청; -import static com.funeat.acceptance.member.MemberSteps.사용자_정보_수정_요청; -import static com.funeat.acceptance.member.MemberSteps.사용자_정보_조회_요청; -import static com.funeat.acceptance.recipe.RecipeSteps.레시피_작성_요청; -import static com.funeat.acceptance.review.ReviewSteps.리뷰_작성_요청; -import static com.funeat.auth.exception.AuthErrorCode.LOGIN_MEMBER_NOT_FOUND; -import static com.funeat.exception.CommonErrorCode.REQUEST_VALID_ERROR_CODE; -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static com.funeat.fixture.ImageFixture.이미지1; -import static com.funeat.fixture.ImageFixture.이미지2; -import static com.funeat.fixture.ImageFixture.이미지3; -import static com.funeat.fixture.MemberFixture.멤버1; -import static com.funeat.fixture.MemberFixture.멤버2; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.MemberFixture.유저닉네임수정요청_생성; -import static com.funeat.fixture.PageFixture.FIRST_PAGE; -import static com.funeat.fixture.PageFixture.PAGE_SIZE; -import static com.funeat.fixture.PageFixture.마지막페이지O; -import static com.funeat.fixture.PageFixture.응답_페이지_생성; -import static com.funeat.fixture.PageFixture.첫페이지O; -import static com.funeat.fixture.PageFixture.총_데이터_개수; -import static com.funeat.fixture.PageFixture.총_페이지; -import static com.funeat.fixture.PageFixture.최신순; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점3점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점5점_생성; -import static com.funeat.fixture.RecipeFixture.레시피; -import static com.funeat.fixture.RecipeFixture.레시피1; -import static com.funeat.fixture.RecipeFixture.레시피2; -import static com.funeat.fixture.RecipeFixture.레시피추가요청_생성; -import static com.funeat.fixture.ReviewFixture.리뷰1; -import static com.funeat.fixture.ReviewFixture.리뷰2; -import static com.funeat.fixture.ReviewFixture.리뷰추가요청_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰추가요청_재구매X_생성; -import static com.funeat.fixture.ScoreFixture.점수_1점; -import static com.funeat.fixture.ScoreFixture.점수_2점; -import static com.funeat.fixture.ScoreFixture.점수_3점; -import static com.funeat.fixture.ScoreFixture.점수_4점; -import static com.funeat.fixture.TagFixture.태그_맛있어요_TASTE_생성; -import static com.funeat.review.exception.ReviewErrorCode.REVIEW_NOT_FOUND; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import com.funeat.acceptance.common.AcceptanceTest; -import com.funeat.member.domain.Member; -import com.funeat.member.dto.MemberProfileResponse; -import com.funeat.member.dto.MemberRecipeDto; -import com.funeat.member.dto.MemberReviewDto; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullAndEmptySource; - -@SuppressWarnings("NonAsciiCharacters") -public class MemberAcceptanceTest extends AcceptanceTest { - - @Nested - class getMemberProfile_성공_테스트 { - - @Test - void 사용자_정보를_확인하다() { - // given & when - final var 응답 = 사용자_정보_조회_요청(로그인_쿠키_획득(멤버1)); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 사용자_정보_조회를_검증하다(응답, 멤버_멤버1_생성()); - } - } - - @Nested - class getMemberProfile_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 로그인_하지않은_사용자가_사용자_정보를_확인시_예외가_발생한다(final String cookie) { - // given & when - final var 응답 = 사용자_정보_조회_요청(cookie); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), - LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - } - - @Nested - class putMemberProfile_성공_테스트 { - - @Test - void 사용자_정보를_수정하다() { - // given & when - final var 응답 = 사용자_정보_수정_요청(로그인_쿠키_획득(멤버1), 사진_명세_요청(이미지1), 유저닉네임수정요청_생성("after")); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리_NO_CONTENT); - } - - @Test - void 사용자_닉네임을_수정하다() { - // given & when - final var 응답 = 사용자_정보_수정_요청(로그인_쿠키_획득(멤버1), 사진_명세_요청(이미지1), 유저닉네임수정요청_생성("member1")); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리_NO_CONTENT); - } - - @Test - void 사용자_이미지를_수정하다() { - // given & when - final var 응답 = 사용자_정보_수정_요청(로그인_쿠키_획득(멤버1), 사진_명세_요청(이미지2), 유저닉네임수정요청_생성("after")); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리_NO_CONTENT); - } - } - - @Nested - class putMemberProfile_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 로그인_하지않은_사용자가_사용자_정보_수정시_예외가_발생한다(final String cookie) { - // given & when - final var 응답 = 사용자_정보_수정_요청(cookie, 사진_명세_요청(이미지1), 유저닉네임수정요청_생성("after")); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), - LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - - @ParameterizedTest - @NullAndEmptySource - void 사용자가_사용자_정보_수정할때_닉네임_미기입시_예외가_발생한다(final String nickname) { - // given & when - final var 응답 = 사용자_정보_수정_요청(로그인_쿠키_획득(멤버1), 사진_명세_요청(이미지1), 유저닉네임수정요청_생성(nickname)); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "닉네임을 확인해주세요. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - } - - @Nested - class getMemberReviews_성공_테스트 { - - @Test - void 사용자가_작성한_리뷰를_조회하다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매X_생성(점수_2점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품, 사진_명세_요청(이미지2), 리뷰추가요청_재구매O_생성(점수_1점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지3), 리뷰추가요청_재구매X_생성(점수_3점, List.of(태그))); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(2L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 사용자_리뷰_조회_요청(로그인_쿠키_획득(멤버1), 최신순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 사용자_리뷰_조회_결과를_검증한다(응답, 2); - } - - @Test - void 사용자가_작성한_리뷰가_없을때_리뷰는_빈상태로_조회된다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매X_생성(점수_2점, List.of(태그))); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(0L), 총_페이지(0L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 사용자_리뷰_조회_요청(로그인_쿠키_획득(멤버1), 최신순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 사용자_리뷰_조회_결과를_검증한다(응답, 0); - } - } - - @Nested - class getMemberReviews_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 로그인하지_않은_사용자가_작성한_리뷰를_조회할때_예외가_발생한다(final String cookie) { - // given & when - final var 응답 = 사용자_리뷰_조회_요청(cookie, 최신순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), - LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - } - - @Nested - class getMemberRecipes_성공_테스트 { - - @Test - void 사용자가_작성한_꿀조합을_조회하다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지2), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버2), 여러개_사진_명세_요청(이미지3), 레시피추가요청_생성(상품)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(2L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 사용자_꿀조합_조회_요청(로그인_쿠키_획득(멤버1), 최신순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 사용자_꿀조합_조회_결과를_검증한다(응답, List.of(레시피2, 레시피1)); - } - - @Test - void 사용자가_작성한_꿀조합이_없을때_꿀조합은_빈상태로_조회된다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버2), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(0L), 총_페이지(0L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 사용자_꿀조합_조회_요청(로그인_쿠키_획득(멤버1), 최신순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 사용자_꿀조합_조회_결과를_검증한다(응답, Collections.emptyList()); - } - - @Test - void 사용자가_작성한_꿀조합에_이미지가_없을때_꿀조합은_이미지없이_조회된다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), null, 레시피추가요청_생성(상품)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(1L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 사용자_꿀조합_조회_요청(로그인_쿠키_획득(멤버1), 최신순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 사용자_꿀조합_조회_결과를_검증한다(응답, List.of(레시피)); - 조회한_꿀조합의_이미지가_없는지_확인한다(응답); - } - } - - @Nested - class getMemberRecipes_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 로그인하지_않은_사용자가_작성한_꿀조합을_조회할때_예외가_발생한다(final String cookie) { - // given & when - final var 응답 = 사용자_꿀조합_조회_요청(cookie, 최신순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), - LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - } - - @Nested - class deleteReview_성공_테스트 { - - @Test - void 자신이_작성한_리뷰를_삭제할_수_있다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - - // when - final var 응답 = 리뷰_삭제_요청(로그인_쿠키_획득(멤버1), 리뷰1); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리_NO_CONTENT); - } - } - - @Nested - class deleteReview_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 로그인하지_않는_사용자가_리뷰_삭제시_예외가_발생한다(final String cookie) { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - - // when - final var 응답 = 리뷰_삭제_요청(cookie, 리뷰1); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - - @Test - void 존재하지_않는_리뷰를_삭제할_수_없다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - - // when - final var 응답 = 리뷰_삭제_요청(로그인_쿠키_획득(멤버1), 리뷰2); - - // then - STATUS_CODE를_검증한다(응답, 찾을수_없음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REVIEW_NOT_FOUND.getCode(), REVIEW_NOT_FOUND.getMessage()); - } - - @Test - void 자신이_작성하지_않은_리뷰는_삭제할_수_없다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - - // when - final var 응답 = 리뷰_삭제_요청(로그인_쿠키_획득(멤버2), 리뷰1); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - } - } - - private void 사용자_리뷰_조회_결과를_검증한다(final ExtractableResponse response, final int expectedReviewSize) { - final var actual = response.jsonPath().getList("reviews", MemberReviewDto.class); - - assertThat(actual.size()).isEqualTo(expectedReviewSize); - } - - private void 사용자_꿀조합_조회_결과를_검증한다(final ExtractableResponse response, final List recipeIds) { - final var actual = response.jsonPath() - .getList("recipes", MemberRecipeDto.class); - - assertThat(actual).extracting(MemberRecipeDto::getId) - .containsExactlyElementsOf(recipeIds); - } - - private void RESPONSE_CODE와_MESSAGE를_검증한다(final ExtractableResponse response, final String expectedCode, - final String expectedMessage) { - assertSoftly(soft -> { - soft.assertThat(response.jsonPath().getString("code")) - .isEqualTo(expectedCode); - soft.assertThat(response.jsonPath().getString("message")) - .isEqualTo(expectedMessage); - }); - } - - private void 사용자_정보_조회를_검증하다(final ExtractableResponse response, final Member member) { - final var expected = MemberProfileResponse.toResponse(member); - final var expectedNickname = expected.getNickname(); - final var expectedProfileImage = expected.getProfileImage(); - - final var actualNickname = response.jsonPath().getString("nickname"); - final var actualProfileImage = response.jsonPath().getString("profileImage"); - - assertSoftly(soft -> { - soft.assertThat(actualNickname) - .isEqualTo(expectedNickname); - soft.assertThat(actualProfileImage) - .isEqualTo(expectedProfileImage); - }); - } - - private void 조회한_꿀조합의_이미지가_없는지_확인한다(final ExtractableResponse response) { - final var actual = response.jsonPath() - .getString("image"); - - assertThat(actual).isNull(); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/member/MemberSteps.java b/backend/src/test/java/com/funeat/acceptance/member/MemberSteps.java deleted file mode 100644 index 98cf4d6fa..000000000 --- a/backend/src/test/java/com/funeat/acceptance/member/MemberSteps.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.funeat.acceptance.member; - -import static io.restassured.RestAssured.given; - -import com.funeat.member.dto.MemberRequest; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import io.restassured.specification.MultiPartSpecification; -import java.util.Objects; - -@SuppressWarnings("NonAsciiCharacters") -public class MemberSteps { - - public static ExtractableResponse 사용자_정보_조회_요청(final String loginCookie) { - return given() - .cookie("SESSION", loginCookie) - .when() - .get("/api/members") - .then() - .extract(); - } - - public static ExtractableResponse 사용자_정보_수정_요청(final String loginCookie, - final MultiPartSpecification image, - final MemberRequest request) { - final var requestSpec = given() - .cookie("SESSION", loginCookie); - - if (Objects.nonNull(image)) { - requestSpec.multiPart(image); - } - - return requestSpec - .multiPart("memberRequest", request, "application/json") - .body(request) - .when() - .put("/api/members") - .then() - .extract(); - } - - public static ExtractableResponse 사용자_리뷰_조회_요청(final String loginCookie, final String sort, - final Long page) { - return given() - .when() - .cookie("SESSION", loginCookie) - .queryParam("sort", sort) - .queryParam("page", page) - .get("/api/members/reviews") - .then() - .extract(); - } - - public static ExtractableResponse 사용자_꿀조합_조회_요청(final String loginCookie, final String sort, - final Long page) { - return given() - .when() - .cookie("SESSION", loginCookie) - .queryParam("sort", sort) - .queryParam("page", page) - .get("/api/members/recipes") - .then() - .extract(); - } - - public static ExtractableResponse 리뷰_삭제_요청(final String loginCookie, final Long reviewId) { - return given() - .cookie("SESSION", loginCookie) - .when() - .delete("/api/members/reviews/{reviewId}", reviewId) - .then() - .extract(); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/product/CategoryAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/product/CategoryAcceptanceTest.java deleted file mode 100644 index 3ee19b4f9..000000000 --- a/backend/src/test/java/com/funeat/acceptance/product/CategoryAcceptanceTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.funeat.acceptance.product; - -import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; -import static com.funeat.acceptance.common.CommonSteps.잘못된_요청; -import static com.funeat.acceptance.common.CommonSteps.정상_처리; -import static com.funeat.acceptance.product.CategorySteps.카테고리_목록_조회_요청; -import static com.funeat.exception.CommonErrorCode.REQUEST_VALID_ERROR_CODE; -import static com.funeat.fixture.CategoryFixture.음식; -import static com.funeat.fixture.CategoryFixture.카테고리_CU_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_과자류_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import com.funeat.acceptance.common.AcceptanceTest; -import com.funeat.product.dto.CategoryResponse; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import java.util.List; -import org.assertj.core.api.SoftAssertions; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullAndEmptySource; -import org.junit.jupiter.params.provider.ValueSource; - -@SuppressWarnings("NonAsciiCharacters") -public class CategoryAcceptanceTest extends AcceptanceTest { - - - @Nested - class getAllCategoriesByType_성공_테스트 { - - @Test - void 공통_상품_카테고리의_목록을_조회한다() { - // given - final var 간편식사 = 단일_카테고리_저장(카테고리_간편식사_생성()); - final var 즉석조리 = 단일_카테고리_저장(카테고리_즉석조리_생성()); - final var 과자류 = 단일_카테고리_저장(카테고리_과자류_생성()); - final var CU = 단일_카테고리_저장(카테고리_CU_생성()); - - // when - final var 응답 = 카테고리_목록_조회_요청(음식); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 공통_상품_카테고리_목록_조회_결과를_검증한다(응답, List.of(간편식사, 즉석조리, 과자류), List.of(CU)); - } - } - - @Nested - class getAllCategoriesByType_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - @ValueSource(strings = {"a", "foo"}) - void 존재하지_않는_카테고리의_목록을_조회할때_예외가_발생한다(final String type) { - // given & when - final var 응답 = 카테고리_목록_조회_요청(type); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - REQUEST_VALID_ERROR_CODE.getMessage()); - } - } - - private void 공통_상품_카테고리_목록_조회_결과를_검증한다(final ExtractableResponse response, - final List includeCategoryIds, final List excludeCategoryIds) { - final var actual = response.jsonPath() - .getList("", CategoryResponse.class); - - assertThat(actual).extracting(CategoryResponse::getId) - .containsExactlyElementsOf(includeCategoryIds) - .doesNotContainAnyElementsOf(excludeCategoryIds); - } - - private void RESPONSE_CODE와_MESSAGE를_검증한다(final ExtractableResponse response, final String expectedCode, - final String expectedMessage) { - assertSoftly(soft -> { - soft.assertThat(response.jsonPath().getString("code")) - .isEqualTo(expectedCode); - soft.assertThat(response.jsonPath().getString("message")) - .isEqualTo(expectedMessage); - }); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/product/CategorySteps.java b/backend/src/test/java/com/funeat/acceptance/product/CategorySteps.java deleted file mode 100644 index 342beb2fc..000000000 --- a/backend/src/test/java/com/funeat/acceptance/product/CategorySteps.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.funeat.acceptance.product; - -import static io.restassured.RestAssured.given; - -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; - -@SuppressWarnings("NonAsciiCharacters") -public class CategorySteps { - - public static ExtractableResponse 카테고리_목록_조회_요청(final String type) { - return given() - .queryParam("type", type) - .when() - .get("/api/categories") - .then() - .extract(); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java deleted file mode 100644 index 5a73e6554..000000000 --- a/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java +++ /dev/null @@ -1,734 +0,0 @@ -package com.funeat.acceptance.product; - -import static com.funeat.acceptance.auth.LoginSteps.로그인_쿠키_획득; -import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; -import static com.funeat.acceptance.common.CommonSteps.다음_데이터가_있는지_검증한다; -import static com.funeat.acceptance.common.CommonSteps.사진_명세_요청; -import static com.funeat.acceptance.common.CommonSteps.여러개_사진_명세_요청; -import static com.funeat.acceptance.common.CommonSteps.정상_처리; -import static com.funeat.acceptance.common.CommonSteps.찾을수_없음; -import static com.funeat.acceptance.common.CommonSteps.페이지를_검증한다; -import static com.funeat.acceptance.product.ProductSteps.상품_검색_결과_조회_요청; -import static com.funeat.acceptance.product.ProductSteps.상품_랭킹_조회_요청; -import static com.funeat.acceptance.product.ProductSteps.상품_레시피_목록_요청; -import static com.funeat.acceptance.product.ProductSteps.상품_상세_조회_요청; -import static com.funeat.acceptance.product.ProductSteps.상품_자동_완성_검색_요청; -import static com.funeat.acceptance.product.ProductSteps.카테고리별_상품_목록_조회_요청; -import static com.funeat.acceptance.recipe.RecipeSteps.레시피_작성_요청; -import static com.funeat.acceptance.recipe.RecipeSteps.여러명이_레시피_좋아요_요청; -import static com.funeat.acceptance.review.ReviewSteps.리뷰_작성_요청; -import static com.funeat.fixture.CategoryFixture.존재하지_않는_카테고리; -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.ImageFixture.이미지1; -import static com.funeat.fixture.ImageFixture.이미지2; -import static com.funeat.fixture.ImageFixture.이미지3; -import static com.funeat.fixture.ImageFixture.이미지4; -import static com.funeat.fixture.ImageFixture.이미지5; -import static com.funeat.fixture.ImageFixture.이미지6; -import static com.funeat.fixture.ImageFixture.이미지7; -import static com.funeat.fixture.MemberFixture.멤버1; -import static com.funeat.fixture.MemberFixture.멤버2; -import static com.funeat.fixture.MemberFixture.멤버3; -import static com.funeat.fixture.PageFixture.FIRST_PAGE; -import static com.funeat.fixture.PageFixture.PAGE_SIZE; -import static com.funeat.fixture.PageFixture.SECOND_PAGE; -import static com.funeat.fixture.PageFixture.가격_내림차순; -import static com.funeat.fixture.PageFixture.가격_오름차순; -import static com.funeat.fixture.PageFixture.과거순; -import static com.funeat.fixture.PageFixture.리뷰수_내림차순; -import static com.funeat.fixture.PageFixture.마지막페이지O; -import static com.funeat.fixture.PageFixture.마지막페이지X; -import static com.funeat.fixture.PageFixture.응답_페이지_생성; -import static com.funeat.fixture.PageFixture.좋아요수_내림차순; -import static com.funeat.fixture.PageFixture.첫페이지O; -import static com.funeat.fixture.PageFixture.첫페이지X; -import static com.funeat.fixture.PageFixture.총_데이터_개수; -import static com.funeat.fixture.PageFixture.총_페이지; -import static com.funeat.fixture.PageFixture.최신순; -import static com.funeat.fixture.PageFixture.평균_평점_내림차순; -import static com.funeat.fixture.PageFixture.평균_평점_오름차순; -import static com.funeat.fixture.ProductFixture.상품1; -import static com.funeat.fixture.ProductFixture.상품10; -import static com.funeat.fixture.ProductFixture.상품11; -import static com.funeat.fixture.ProductFixture.상품2; -import static com.funeat.fixture.ProductFixture.상품3; -import static com.funeat.fixture.ProductFixture.상품4; -import static com.funeat.fixture.ProductFixture.상품5; -import static com.funeat.fixture.ProductFixture.상품6; -import static com.funeat.fixture.ProductFixture.상품7; -import static com.funeat.fixture.ProductFixture.상품8; -import static com.funeat.fixture.ProductFixture.상품9; -import static com.funeat.fixture.ProductFixture.상품_망고빙수_가격5000원_평점4점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점3점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점4점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점5점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점3점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점5점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격3000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격3000원_평점5점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격4000원_평점4점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격5000원_평점3점_생성; -import static com.funeat.fixture.ProductFixture.상품_애플망고_가격3000원_평점5점_생성; -import static com.funeat.fixture.ProductFixture.존재하지_않는_상품; -import static com.funeat.fixture.RecipeFixture.레시피1; -import static com.funeat.fixture.RecipeFixture.레시피2; -import static com.funeat.fixture.RecipeFixture.레시피3; -import static com.funeat.fixture.RecipeFixture.레시피추가요청_생성; -import static com.funeat.fixture.RecipeFixture.좋아요O; -import static com.funeat.fixture.ReviewFixture.리뷰추가요청_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰추가요청_재구매X_생성; -import static com.funeat.fixture.ScoreFixture.점수_1점; -import static com.funeat.fixture.ScoreFixture.점수_2점; -import static com.funeat.fixture.ScoreFixture.점수_3점; -import static com.funeat.fixture.ScoreFixture.점수_4점; -import static com.funeat.fixture.ScoreFixture.점수_5점; -import static com.funeat.fixture.TagFixture.태그_단짠단짠_TASTE_생성; -import static com.funeat.fixture.TagFixture.태그_맛있어요_TASTE_생성; -import static com.funeat.product.exception.CategoryErrorCode.CATEGORY_NOT_FOUND; -import static com.funeat.product.exception.ProductErrorCode.PRODUCT_NOT_FOUND; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import com.funeat.acceptance.common.AcceptanceTest; -import com.funeat.product.domain.Category; -import com.funeat.product.dto.ProductInCategoryDto; -import com.funeat.product.dto.ProductResponse; -import com.funeat.product.dto.RankingProductDto; -import com.funeat.product.dto.SearchProductDto; -import com.funeat.product.dto.SearchProductResultDto; -import com.funeat.recipe.dto.RecipeDto; -import com.funeat.tag.dto.TagDto; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -class ProductAcceptanceTest extends AcceptanceTest { - - @Nested - class getAllProductsInCategory_성공_테스트 { - - @Nested - class 가격_기준_내림차순으로_카테고리별_상품_목록_조회 { - - @Test - void 상품_가격이_서로_다르면_가격_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품1 = 단일_상품_저장(상품_삼각김밥_가격3000원_평점5점_생성(카테고리)); - final var 상품2 = 단일_상품_저장(상품_삼각김밥_가격2000원_평점3점_생성(카테고리)); - final var 상품3 = 단일_상품_저장(상품_삼각김밥_가격4000원_평점4점_생성(카테고리)); - - // when - final var 응답 = 카테고리별_상품_목록_조회_요청(1L, 가격_내림차순, 0L); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 마지막페이지X); - 카테고리별_상품_목록_조회_결과를_검증한다(응답, List.of(상품3, 상품1, 상품2)); - } - - @Test - void 상품_가격이_서로_같으면_ID_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - final var 카테고리_아이디 = 단일_카테고리_저장(카테고리); - final var 상품1 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 상품2 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 상품3 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - // when - final var 응답 = 카테고리별_상품_목록_조회_요청(카테고리_아이디, 가격_내림차순, 0L); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 마지막페이지X); - 카테고리별_상품_목록_조회_결과를_검증한다(응답, List.of(상품3, 상품2, 상품1)); - } - } - - @Nested - class 가격_기준_오름차순으로_카테고리별_상품_목록_조회 { - - @Test - void 상품_가격이_서로_다르면_가격_기준_오름차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - final var 카테고리_아이디 = 단일_카테고리_저장(카테고리); - final var 상품1 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 상품2 = 단일_상품_저장(상품_삼각김밥_가격4000원_평점4점_생성(카테고리)); - final var 상품3 = 단일_상품_저장(상품_삼각김밥_가격2000원_평점3점_생성(카테고리)); - - // when - final var 응답 = 카테고리별_상품_목록_조회_요청(카테고리_아이디, 가격_오름차순, 0L); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 마지막페이지X); - 카테고리별_상품_목록_조회_결과를_검증한다(응답, List.of(상품1, 상품3, 상품2)); - } - - @Test - void 상품_가격이_서로_같으면_ID_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - // when - final var 응답 = 카테고리별_상품_목록_조회_요청(1L, 가격_오름차순, 0L); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 마지막페이지X); - 카테고리별_상품_목록_조회_결과를_검증한다(응답, List.of(3L, 2L, 1L)); - } - } - - @Nested - class 평점_기준_내림차순으로_카테고리별_상품_목록_조회 { - - @Test - void 상품_평점이_서로_다르면_평점_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - 단일_상품_저장(상품_삼각김밥_가격2000원_평점5점_생성(카테고리)); - 단일_상품_저장(상품_삼각김밥_가격2000원_평점1점_생성(카테고리)); - - // when - final var 응답 = 카테고리별_상품_목록_조회_요청(1L, 평균_평점_내림차순, 0L); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 마지막페이지X); - 카테고리별_상품_목록_조회_결과를_검증한다(응답, List.of(2L, 1L, 3L)); - } - - @Test - void 상품_평점이_서로_같으면_ID_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - 단일_상품_저장(상품_삼각김밥_가격2000원_평점1점_생성(카테고리)); - 단일_상품_저장(상품_삼각김밥_가격2000원_평점1점_생성(카테고리)); - - // when - final var 응답 = 카테고리별_상품_목록_조회_요청(1L, 평균_평점_내림차순, 0L); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 마지막페이지X); - 카테고리별_상품_목록_조회_결과를_검증한다(응답, List.of(3L, 2L, 1L)); - } - } - - @Nested - class 평점_기준_오름차순으로_카테고리별_상품_목록_조회 { - - @Test - void 상품_평점이_서로_다르면_평점_기준_오름차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - 단일_상품_저장(상품_삼각김밥_가격2000원_평점1점_생성(카테고리)); - 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - 단일_상품_저장(상품_삼각김밥_가격2000원_평점3점_생성(카테고리)); - - // when - final var 응답 = 카테고리별_상품_목록_조회_요청(1L, 평균_평점_오름차순, 0L); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 마지막페이지X); - 카테고리별_상품_목록_조회_결과를_검증한다(응답, List.of(1L, 3L, 2L)); - } - - @Test - void 상품_평점이_서로_같으면_ID_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - 단일_상품_저장(상품_삼각김밥_가격2000원_평점1점_생성(카테고리)); - 단일_상품_저장(상품_삼각김밥_가격2000원_평점1점_생성(카테고리)); - - // when - final var 응답 = 카테고리별_상품_목록_조회_요청(1L, 평균_평점_오름차순, 0L); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 마지막페이지X); - 카테고리별_상품_목록_조회_결과를_검증한다(응답, List.of(3L, 2L, 1L)); - } - } - - @Nested - class 리뷰수_기준_내림차순으로_카테고리별_상품_목록_조회 { - - @Test - void 리뷰수가_서로_다르면_리뷰수_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - final var 카테고리_아이디 = 단일_카테고리_저장(카테고리); - final var 상품1 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 상품2 = 단일_상품_저장(상품_삼각김밥_가격2000원_평점3점_생성(카테고리)); - final var 상품3 = 단일_상품_저장(상품_삼각김밥_가격2000원_평점1점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품1, 사진_명세_요청(이미지1), 리뷰추가요청_재구매X_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품2, 사진_명세_요청(이미지2), 리뷰추가요청_재구매X_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품2, 사진_명세_요청(이미지3), 리뷰추가요청_재구매O_생성(점수_2점, List.of(태그))); - - // when - final var 응답 = 카테고리별_상품_목록_조회_요청(카테고리_아이디, 리뷰수_내림차순, 0L); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 마지막페이지X); - 카테고리별_상품_목록_조회_결과를_검증한다(응답, List.of(상품2, 상품1, 상품3)); - } - - @Test - void 리뷰수가_서로_같으면_ID_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - final var 카테고리_아이디 = 단일_카테고리_저장(카테고리); - final var 상품1 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 상품2 = 단일_상품_저장(상품_삼각김밥_가격5000원_평점3점_생성(카테고리)); - final var 상품3 = 단일_상품_저장(상품_삼각김밥_가격3000원_평점1점_생성(카테고리)); - - // when - final var 응답 = 카테고리별_상품_목록_조회_요청(카테고리_아이디, 리뷰수_내림차순, 0L); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 마지막페이지X); - 카테고리별_상품_목록_조회_결과를_검증한다(응답, List.of(상품3, 상품2, 상품1)); - } - } - } - - @Nested - class getAllProductsInCategory_실패_테스트 { - - @Test - void 상품을_정렬할때_카테고리가_존재하지_않으면_예외가_발생한다() { - // given & when - final var 응답 = 카테고리별_상품_목록_조회_요청(존재하지_않는_카테고리, 가격_내림차순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 찾을수_없음); - ERROR_CODE와_MESSAGE를_검증한다(응답, CATEGORY_NOT_FOUND.getCode(), CATEGORY_NOT_FOUND.getMessage()); - } - } - - @Nested - class getProductDetail_성공_테스트 { - - @Test - void 상품_상세_정보를_조회한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그1 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - final var 태그2 = 단일_태그_저장(태그_단짠단짠_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매X_생성(점수_4점, List.of(태그1, 태그2))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지2), 리뷰추가요청_재구매X_생성(점수_4점, List.of(태그2))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지3), 리뷰추가요청_재구매X_생성(점수_1점, List.of(태그2))); - - // when - final var 응답 = 상품_상세_조회_요청(상품); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 상품_상세_정보_조회_결과를_검증한다(응답); - } - } - - @Nested - class getProductDetail_실패_테스트 { - - @Test - void 존재하지_않는_상품_상세_정보를_조회할때_예외가_발생한다() { - // given & when - final var 응답 = 상품_상세_조회_요청(존재하지_않는_상품); - - // then - STATUS_CODE를_검증한다(응답, 찾을수_없음); - ERROR_CODE와_MESSAGE를_검증한다(응답, PRODUCT_NOT_FOUND.getCode(), PRODUCT_NOT_FOUND.getMessage()); - } - } - - @Nested - class getRankingProducts_성공_테스트 { - - @Test - void 전체_상품들_중에서_랭킹_TOP3를_조회할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품1 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점4점_생성(카테고리)); - final var 상품2 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 상품3 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점4점_생성(카테고리)); - final var 상품4 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품1, 사진_명세_요청(이미지1), 리뷰추가요청_재구매X_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품1, 사진_명세_요청(이미지2), 리뷰추가요청_재구매X_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버3), 상품1, 사진_명세_요청(이미지3), 리뷰추가요청_재구매X_생성(점수_4점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품2, 사진_명세_요청(이미지4), 리뷰추가요청_재구매X_생성(점수_5점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품2, 사진_명세_요청(이미지5), 리뷰추가요청_재구매X_생성(점수_5점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품3, 사진_명세_요청(이미지6), 리뷰추가요청_재구매X_생성(점수_4점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품3, 사진_명세_요청(이미지7), 리뷰추가요청_재구매X_생성(점수_5점, List.of(태그))); - - // when - final var 응답 = 상품_랭킹_조회_요청(); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 상품_랭킹_조회_결과를_검증한다(응답, List.of(상품2, 상품3, 상품1)); - } - } - - @Nested - class searchProducts_성공_테스트 { - - @Test - void 검색어가_포함된_상품들을_검색한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품1 = 단일_상품_저장(상품_애플망고_가격3000원_평점5점_생성(카테고리)); - final var 상품2 = 단일_상품_저장(상품_망고빙수_가격5000원_평점4점_생성(카테고리)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(2L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 상품_자동_완성_검색_요청("망고", FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 상품_자동_완성_검색_결과를_검증한다(응답, List.of(상품2, 상품1)); - } - - @Test - void 검색어가_포함된_상품이_없으면_빈_리스트를_반환한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - 반복_애플망고_상품_저장(2, 카테고리); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(0L), 총_페이지(0L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 상품_자동_완성_검색_요청("김밥", FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 상품_자동_완성_검색_결과를_검증한다(응답, Collections.emptyList()); - } - - @Test - void 페이지가_넘어가도_중복없이_결과를_조회한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - 단일_상품_저장(상품_망고빙수_가격5000원_평점4점_생성(카테고리)); - 반복_애플망고_상품_저장(10, 카테고리); - - final var 예상_응답_페이지1 = 응답_페이지_생성(총_데이터_개수(11L), 총_페이지(2L), 첫페이지O, 마지막페이지X, FIRST_PAGE, PAGE_SIZE); - final var 예상_응답_페이지2 = 응답_페이지_생성(총_데이터_개수(11L), 총_페이지(2L), 첫페이지X, 마지막페이지O, SECOND_PAGE, PAGE_SIZE); - - // when - final var 응답1 = 상품_자동_완성_검색_요청("망고", FIRST_PAGE); - final var 응답2 = 상품_자동_완성_검색_요청("망고", SECOND_PAGE); - - // then - STATUS_CODE를_검증한다(응답1, 정상_처리); - 페이지를_검증한다(응답1, 예상_응답_페이지1); - - STATUS_CODE를_검증한다(응답2, 정상_처리); - 페이지를_검증한다(응답2, 예상_응답_페이지2); - - 결과값이_이전_요청_결과값에_중복되는지_검증(응답1, 응답2); - } - - @Test - void 페이지가_넘어가도_시작되는_단어_우선_조회한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - 단일_상품_저장(상품_망고빙수_가격5000원_평점4점_생성(카테고리)); - 반복_애플망고_상품_저장(9, 카테고리); - 단일_상품_저장(상품_망고빙수_가격5000원_평점4점_생성(카테고리)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(11L), 총_페이지(2L), 첫페이지O, 마지막페이지X, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 상품_자동_완성_검색_요청("망고", FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 상품_자동_완성_검색_결과를_검증한다(응답, List.of(상품11, 상품1, 상품10, 상품9, 상품8, 상품7, 상품6, 상품5, 상품4, 상품3)); - } - } - - @Nested - class getSearchResults_성공_테스트 { - - @Test - void 상품_검색_결과들을_조회한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - 단일_상품_저장(상품_애플망고_가격3000원_평점5점_생성(카테고리)); - 단일_상품_저장(상품_망고빙수_가격5000원_평점4점_생성(카테고리)); - 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(2L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var response = 상품_검색_결과_조회_요청("망고", FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(response, 정상_처리); - 페이지를_검증한다(response, 예상_응답_페이지); - 상품_검색_결과를_검증한다(response, List.of(상품2, 상품1)); - } - - @Test - void 검색_결과에_상품이_없으면_빈_리스트를_반환한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - 단일_상품_저장(상품_애플망고_가격3000원_평점5점_생성(카테고리)); - 단일_상품_저장(상품_망고빙수_가격5000원_평점4점_생성(카테고리)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(0L), 총_페이지(0L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 상품_검색_결과_조회_요청("김밥", FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 상품_검색_결과를_검증한다(응답, Collections.emptyList()); - } - - @Test - void 페이지가_넘어가도_중복없이_상품_결과를_조회한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - 단일_상품_저장(상품_망고빙수_가격5000원_평점4점_생성(카테고리)); - 반복_애플망고_상품_저장(10, 카테고리); - - final var 예상_응답_페이지1 = 응답_페이지_생성(총_데이터_개수(11L), 총_페이지(2L), 첫페이지O, 마지막페이지X, FIRST_PAGE, PAGE_SIZE); - final var 예상_응답_페이지2 = 응답_페이지_생성(총_데이터_개수(11L), 총_페이지(2L), 첫페이지X, 마지막페이지O, SECOND_PAGE, PAGE_SIZE); - - // when - final var 응답1 = 상품_검색_결과_조회_요청("망고", FIRST_PAGE); - final var 응답2 = 상품_검색_결과_조회_요청("망고", SECOND_PAGE); - - // then - STATUS_CODE를_검증한다(응답1, 정상_처리); - 페이지를_검증한다(응답1, 예상_응답_페이지1); - - STATUS_CODE를_검증한다(응답2, 정상_처리); - 페이지를_검증한다(응답2, 예상_응답_페이지2); - - 결과값이_이전_요청_결과값에_중복되는지_검증(응답1, 응답2); - } - - @Test - void 페이지가_넘어가도_시작되는_단어_우선_조회한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - 단일_상품_저장(상품_망고빙수_가격5000원_평점4점_생성(카테고리)); - 반복_애플망고_상품_저장(9, 카테고리); - 단일_상품_저장(상품_망고빙수_가격5000원_평점4점_생성(카테고리)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(11L), 총_페이지(2L), 첫페이지O, 마지막페이지X, FIRST_PAGE, PAGE_SIZE); - - // when - final var response = 상품_검색_결과_조회_요청("망고", FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(response, 정상_처리); - 페이지를_검증한다(response, 예상_응답_페이지); - 상품_검색_결과를_검증한다(response, List.of(상품11, 상품1, 상품10, 상품9, 상품8, 상품7, 상품6, 상품5, 상품4, 상품3)); - } - } - - @Nested - class getProductRecipes_성공_테스트 { - - @Test - void 해당_상품의_꿀조합_목록을_좋아요가_많은_순으로_조회할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지2), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지3), 레시피추가요청_생성(상품)); - 여러명이_레시피_좋아요_요청(List.of(멤버1), 레시피1, 좋아요O); - 여러명이_레시피_좋아요_요청(List.of(멤버2, 멤버3), 레시피2, 좋아요O); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(3L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var response = 상품_레시피_목록_요청(상품, 좋아요수_내림차순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(response, 정상_처리); - 페이지를_검증한다(response, 예상_응답_페이지); - 상품_레시피_목록_조회_결과를_검증한다(response, List.of(레시피2, 레시피1, 레시피3)); - } - - @Test - void 해당_상품의_꿀조합_목록을_최신순으로_조회할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지2), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지3), 레시피추가요청_생성(상품)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(3L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var response = 상품_레시피_목록_요청(상품, 최신순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(response, 정상_처리); - 페이지를_검증한다(response, 예상_응답_페이지); - 상품_레시피_목록_조회_결과를_검증한다(response, List.of(레시피3, 레시피2, 레시피1)); - } - - @Test - void 해당_상품의_꿀조합_목록을_오래된순으로_조회할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지2), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지3), 레시피추가요청_생성(상품)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(3L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 상품_레시피_목록_요청(상품, 과거순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 상품_레시피_목록_조회_결과를_검증한다(응답, List.of(레시피1, 레시피2, 레시피3)); - } - } - - private void 카테고리별_상품_목록_조회_결과를_검증한다(final ExtractableResponse response, final List productIds) { - final var actual = response.jsonPath() - .getList("products", ProductInCategoryDto.class); - - assertThat(actual).extracting(ProductInCategoryDto::getId) - .containsExactlyElementsOf(productIds); - } - - private void ERROR_CODE와_MESSAGE를_검증한다(final ExtractableResponse response, final String expectedCode, - final String expectedMessage) { - assertSoftly(soft -> { - soft.assertThat(response.jsonPath().getString("code")) - .isEqualTo(expectedCode); - soft.assertThat(response.jsonPath().getString("message")) - .isEqualTo(expectedMessage); - }); - } - - private void 상품_상세_정보_조회_결과를_검증한다(final ExtractableResponse response) { - final var actual = response.as(ProductResponse.class); - final var actualTags = response.jsonPath() - .getList("tags", TagDto.class); - - assertSoftly(soft -> { - soft.assertThat(actual.getId()).isEqualTo(1L); - soft.assertThat(actual.getName()).isEqualTo("삼각김밥"); - soft.assertThat(actual.getPrice()).isEqualTo(1000L); - soft.assertThat(actual.getImage()).isEqualTo("image.png"); - soft.assertThat(actual.getContent()).isEqualTo("맛있는 삼각김밥"); - soft.assertThat(actual.getAverageRating()).isEqualTo(3.0); - soft.assertThat(actual.getReviewCount()).isEqualTo(3L); - soft.assertThat(actualTags).extracting("id").containsExactly(2L, 1L); - }); - } - - private void 상품_랭킹_조회_결과를_검증한다(final ExtractableResponse response, final List productIds) { - final var actual = response.jsonPath() - .getList("products", RankingProductDto.class); - - assertThat(actual).extracting(RankingProductDto::getId) - .isEqualTo(productIds); - } - - private void 상품_자동_완성_검색_결과를_검증한다(final ExtractableResponse response, final List productIds) { - final var actual = response.jsonPath() - .getList("products", SearchProductDto.class); - - assertThat(actual).extracting(SearchProductDto::getId) - .isEqualTo(productIds); - } - - private void 결과값이_이전_요청_결과값에_중복되는지_검증(final ExtractableResponse response1, - final ExtractableResponse response2) { - final var lastResponses = response1.jsonPath() - .getList("products", SearchProductResultDto.class); - final var currentResponse = response2.jsonPath() - .getList("products", SearchProductResultDto.class).get(0); - - assertThat(lastResponses).usingRecursiveFieldByFieldElementComparator() - .doesNotContain(currentResponse); - } - - private void 상품_검색_결과를_검증한다(final ExtractableResponse response, final List productIds) { - final var actual = response.jsonPath() - .getList("products", SearchProductResultDto.class); - - assertThat(actual).extracting(SearchProductResultDto::getId) - .containsExactlyElementsOf(productIds); - } - - private void 상품_레시피_목록_조회_결과를_검증한다(final ExtractableResponse response, final List recipeIds) { - final var actual = response.jsonPath().getList("recipes", RecipeDto.class); - - assertThat(actual).extracting(RecipeDto::getId) - .containsExactlyElementsOf(recipeIds); - } - - private void 반복_애플망고_상품_저장(final int repeat, final Category category) { - for (int i = 0; i < repeat; i++) { - 단일_상품_저장(상품_애플망고_가격3000원_평점5점_생성(category)); - } - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java b/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java deleted file mode 100644 index d41bd5be1..000000000 --- a/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.funeat.acceptance.product; - -import static io.restassured.RestAssured.given; - - -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; - -@SuppressWarnings("NonAsciiCharacters") -public class ProductSteps { - - public static ExtractableResponse 카테고리별_상품_목록_조회_요청(final Long categoryId, final String sort, - final Long lastProductId) { - return given() - .queryParam("sort", sort) - .queryParam("lastProductId", lastProductId) - .when() - .get("/api/categories/{category_id}/products", categoryId) - .then() - .extract(); - } - - public static ExtractableResponse 상품_상세_조회_요청(final Long productId) { - return given() - .when() - .get("/api/products/{product_id}", productId) - .then() - .extract(); - } - - public static ExtractableResponse 상품_랭킹_조회_요청() { - return given() - .when() - .get("/api/ranks/products") - .then() - .extract(); - } - - public static ExtractableResponse 상품_자동_완성_검색_요청(final String query, final Long page) { - return given() - .queryParam("query", query) - .queryParam("page", page) - .when() - .get("/api/search/products") - .then() - .extract(); - } - - public static ExtractableResponse 상품_검색_결과_조회_요청(final String query, final Long page) { - return given() - .queryParam("query", query) - .queryParam("page", page) - .when() - .get("/api/search/products/results") - .then() - .extract(); - } - - public static ExtractableResponse 상품_레시피_목록_요청(final Long productId, final String sort, - final Long page) { - return given() - .queryParam("sort", sort) - .queryParam("page", page) - .when() - .get("/api/products/{productId}/recipes", productId) - .then() - .extract(); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/recipe/RecipeAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/recipe/RecipeAcceptanceTest.java deleted file mode 100644 index bb388ea78..000000000 --- a/backend/src/test/java/com/funeat/acceptance/recipe/RecipeAcceptanceTest.java +++ /dev/null @@ -1,806 +0,0 @@ -package com.funeat.acceptance.recipe; - -import static com.funeat.acceptance.auth.LoginSteps.로그인_쿠키_획득; -import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; -import static com.funeat.acceptance.common.CommonSteps.여러개_사진_명세_요청; -import static com.funeat.acceptance.common.CommonSteps.인증되지_않음; -import static com.funeat.acceptance.common.CommonSteps.잘못된_요청; -import static com.funeat.acceptance.common.CommonSteps.정상_생성; -import static com.funeat.acceptance.common.CommonSteps.정상_처리; -import static com.funeat.acceptance.common.CommonSteps.정상_처리_NO_CONTENT; -import static com.funeat.acceptance.common.CommonSteps.찾을수_없음; -import static com.funeat.acceptance.common.CommonSteps.페이지를_검증한다; -import static com.funeat.acceptance.recipe.RecipeSteps.레시피_검색_결과_조회_요청; -import static com.funeat.acceptance.recipe.RecipeSteps.레시피_댓글_작성_요청; -import static com.funeat.acceptance.recipe.RecipeSteps.레시피_댓글_조회_요청; -import static com.funeat.acceptance.recipe.RecipeSteps.레시피_랭킹_조회_요청; -import static com.funeat.acceptance.recipe.RecipeSteps.레시피_목록_요청; -import static com.funeat.acceptance.recipe.RecipeSteps.레시피_상세_정보_요청; -import static com.funeat.acceptance.recipe.RecipeSteps.레시피_작성_요청; -import static com.funeat.acceptance.recipe.RecipeSteps.레시피_좋아요_요청; -import static com.funeat.acceptance.recipe.RecipeSteps.여러명이_레시피_좋아요_요청; -import static com.funeat.auth.exception.AuthErrorCode.LOGIN_MEMBER_NOT_FOUND; -import static com.funeat.exception.CommonErrorCode.REQUEST_VALID_ERROR_CODE; -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.ImageFixture.이미지1; -import static com.funeat.fixture.ImageFixture.이미지2; -import static com.funeat.fixture.ImageFixture.이미지3; -import static com.funeat.fixture.ImageFixture.이미지4; -import static com.funeat.fixture.MemberFixture.멤버1; -import static com.funeat.fixture.MemberFixture.멤버2; -import static com.funeat.fixture.MemberFixture.멤버3; -import static com.funeat.fixture.PageFixture.FIRST_PAGE; -import static com.funeat.fixture.PageFixture.PAGE_SIZE; -import static com.funeat.fixture.PageFixture.과거순; -import static com.funeat.fixture.PageFixture.마지막페이지O; -import static com.funeat.fixture.PageFixture.응답_페이지_생성; -import static com.funeat.fixture.PageFixture.좋아요수_내림차순; -import static com.funeat.fixture.PageFixture.첫페이지O; -import static com.funeat.fixture.PageFixture.총_데이터_개수; -import static com.funeat.fixture.PageFixture.총_페이지; -import static com.funeat.fixture.PageFixture.최신순; -import static com.funeat.fixture.ProductFixture.상품_망고빙수_가격5000원_평점4점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점5점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격3000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_애플망고_가격3000원_평점5점_생성; -import static com.funeat.fixture.RecipeFixture.레시피; -import static com.funeat.fixture.RecipeFixture.레시피1; -import static com.funeat.fixture.RecipeFixture.레시피2; -import static com.funeat.fixture.RecipeFixture.레시피3; -import static com.funeat.fixture.RecipeFixture.레시피4; -import static com.funeat.fixture.RecipeFixture.레시피_본문; -import static com.funeat.fixture.RecipeFixture.레시피_제목; -import static com.funeat.fixture.RecipeFixture.레시피좋아요요청_생성; -import static com.funeat.fixture.RecipeFixture.레시피추가요청_생성; -import static com.funeat.fixture.RecipeFixture.존재하지_않는_레시피; -import static com.funeat.fixture.RecipeFixture.좋아요O; -import static com.funeat.fixture.RecipeFixture.좋아요X; -import static com.funeat.recipe.exception.RecipeErrorCode.RECIPE_NOT_FOUND; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import com.funeat.acceptance.common.AcceptanceTest; -import com.funeat.member.domain.Member; -import com.funeat.recipe.domain.Recipe; -import com.funeat.recipe.dto.ProductRecipeDto; -import com.funeat.recipe.dto.RankingRecipeDto; -import com.funeat.recipe.dto.RecipeAuthorDto; -import com.funeat.recipe.dto.RecipeCommentCondition; -import com.funeat.recipe.dto.RecipeCommentCreateRequest; -import com.funeat.recipe.dto.RecipeCommentResponse; -import com.funeat.recipe.dto.RecipeCreateRequest; -import com.funeat.recipe.dto.RecipeDetailResponse; -import com.funeat.recipe.dto.RecipeDto; -import com.funeat.recipe.dto.SearchRecipeResultDto; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullAndEmptySource; -import org.junit.jupiter.params.provider.NullSource; - -@SuppressWarnings("NonAsciiCharacters") -public class RecipeAcceptanceTest extends AcceptanceTest { - - @Nested - class writeRecipe_성공_테스트 { - - @Test - void 레시피를_작성한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - // when - final var 응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - // then - STATUS_CODE를_검증한다(응답, 정상_생성); - } - - @Test - void 이미지가_없어도_레시피를_작성할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - // when - final var 응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), null, 레시피추가요청_생성(상품)); - - // then - STATUS_CODE를_검증한다(응답, 정상_생성); - } - } - - @Nested - class writeRecipe_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 로그인_하지않은_사용자가_레시피_작성시_예외가_발생한다(final String cookie) { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - // when - final var 응답 = 레시피_작성_요청(cookie, 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), - LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - - @ParameterizedTest - @NullAndEmptySource - void 사용자가_레시피_작성할때_레시피이름_미기입시_예외가_발생한다(final String title) { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - final var 요청 = 레시피추가요청_생성(title, List.of(상품), 레시피_본문); - - // when - final var 응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 요청); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "꿀조합 이름을 확인해 주세요. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @NullSource - @ParameterizedTest - void 사용자가_레시피_작성할때_상품들이_NULL일시_예외가_발생한다(final List productIds) { - // given - final var 요청 = 레시피추가요청_생성(레시피_제목, productIds, 레시피_본문); - - // when - final var 응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 요청); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "상품 ID 목록을 확인해 주세요. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @Test - void 사용자가_레시피_작성할때_상품들이_비어있을시_예외가_발생한다() { - // given - final var 요청 = new RecipeCreateRequest(레시피_제목, Collections.emptyList(), 레시피_본문); - - // when - final var 응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 요청); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "적어도 1개의 상품 ID가 필요합니다. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @ParameterizedTest - @NullAndEmptySource - void 사용자가_레시피_작성할때_내용이_비어있을시_예외가_발생한다(final String content) { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - final var 요청 = 레시피추가요청_생성(레시피_제목, List.of(상품), content); - - // when - final var 응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 요청); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "꿀조합 내용을 확인해 주세요. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @Test - void 사용자가_레시피_작성할때_레시피내용이_500자_초과시_예외가_발생한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - final var 레시피내용_501자 = ".".repeat(500) + "a"; - final var 요청 = 레시피추가요청_생성("title", List.of(상품), 레시피내용_501자); - - // when - final var 응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 요청); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "꿀조합 내용은 최대 500자까지 입력 가능합니다. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - } - - @Nested - class getRecipeDetail_성공_테스트 { - - @Test - void 레시피의_상세_정보를_조회한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품1 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - final var 상품2 = 단일_상품_저장(상품_삼각김밥_가격3000원_평점1점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품1, 상품2)); - - // when - final var 응답 = 레시피_상세_정보_요청(로그인_쿠키_획득(멤버1), 레시피); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 레시피_상세_정보_조회_결과를_검증한다(응답); - } - } - - @Nested - class getRecipeDetail_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 로그인_하지않은_사용자가_레시피_상세_조회시_예외가_발생한다(final String cookie) { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - // when - final var 응답 = 레시피_상세_정보_요청(cookie, 레시피); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), - LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - - @Test - void 존재하지_않는_레시피_사용자가_레시피_상세_조회시_예외가_발생한다() { - // given & when - final var 응답 = 레시피_상세_정보_요청(로그인_쿠키_획득(멤버1), 존재하지_않는_레시피); - - // then - STATUS_CODE를_검증한다(응답, 찾을수_없음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, RECIPE_NOT_FOUND.getCode(), RECIPE_NOT_FOUND.getMessage()); - } - } - - @Nested - class likeRecipe_성공_테스트 { - - @Test - void 레시피에_좋아요를_할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - // when - final var 응답 = 레시피_좋아요_요청(로그인_쿠키_획득(멤버1), 레시피, 레시피좋아요요청_생성(좋아요O)); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리_NO_CONTENT); - } - - @Test - void 레시피에_좋아요를_취소할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - 레시피_좋아요_요청(로그인_쿠키_획득(멤버1), 레시피, 레시피좋아요요청_생성(좋아요O)); - - // when - final var 응답 = 레시피_좋아요_요청(로그인_쿠키_획득(멤버1), 레시피, 레시피좋아요요청_생성(좋아요X)); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리_NO_CONTENT); - } - } - - @Nested - class likeRecipe_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 로그인_하지않은_사용자가_레시피에_좋아요를_할때_예외가_발생한다(final String cookie) { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - // when - final var 응답 = 레시피_좋아요_요청(cookie, 레시피, 레시피좋아요요청_생성(좋아요O)); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), - LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - - @Test - void 사용자가_레시피에_좋아요를_할때_좋아요_미기입시_예외가_발생한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - // when - final var 응답 = 레시피_좋아요_요청(로그인_쿠키_획득(멤버1), 레시피, 레시피좋아요요청_생성(null)); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "좋아요를 확인해주세요. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @Test - void 존재하지_않는_레시피에_사용자가_좋아요를_할때_예외가_발생한다() { - // given & when - final var 응답 = 레시피_좋아요_요청(로그인_쿠키_획득(멤버1), 존재하지_않는_레시피, 레시피좋아요요청_생성(좋아요O)); - - // then - STATUS_CODE를_검증한다(응답, 찾을수_없음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, RECIPE_NOT_FOUND.getCode(), RECIPE_NOT_FOUND.getMessage()); - } - } - - @Nested - class getSearchResults_성공_테스트 { - - @Test - void 검색어에_해당하는_상품이_포함된_레시피가_2개면_레시피_2개를_반환한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품1 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - final var 상품2 = 단일_상품_저장(상품_망고빙수_가격5000원_평점4점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품1)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지2), 레시피추가요청_생성(상품1, 상품2)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지3), 레시피추가요청_생성(상품2)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(2L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 레시피_검색_결과_조회_요청("망고", FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 레시피_검색_결과를_검증한다(응답, List.of(레시피2, 레시피3)); - } - - @Test - void 검색어에_해당하는_상품이_2개고_상품이_포함된_레시피가_1개면_레시피_1개를_반환한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품1 = 단일_상품_저장(상품_망고빙수_가격5000원_평점4점_생성(카테고리)); - final var 상품2 = 단일_상품_저장(상품_애플망고_가격3000원_평점5점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품1, 상품2)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(1L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 레시피_검색_결과_조회_요청("망고", FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 레시피_검색_결과를_검증한다(응답, List.of(레시피)); - } - - @Test - void 검색_결과에_레시피가_없으면_빈_리스트를_반환한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점1점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(0L), 총_페이지(0L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 레시피_검색_결과_조회_요청("참치", FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 레시피_검색_결과를_검증한다(응답, Collections.emptyList()); - } - } - - @Nested - class getSortingRecipes_성공_테스트 { - - @Test - void 꿀조합을_좋아요가_많은_순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지2), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지3), 레시피추가요청_생성(상품)); - 여러명이_레시피_좋아요_요청(List.of(멤버1), 레시피1, 좋아요O); - 여러명이_레시피_좋아요_요청(List.of(멤버1, 멤버2), 레시피2, 좋아요O); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(3L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 레시피_목록_요청(좋아요수_내림차순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 레시피_목록_조회_결과를_검증한다(응답, List.of(레시피2, 레시피1, 레시피3)); - } - - @Test - void 꿀조합을_최신순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지2), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지3), 레시피추가요청_생성(상품)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(3L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var 응답 = 레시피_목록_요청(최신순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 페이지를_검증한다(응답, 예상_응답_페이지); - 레시피_목록_조회_결과를_검증한다(응답, List.of(레시피3, 레시피2, 레시피1)); - } - - @Test - void 꿀조합을_오래된순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지2), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지3), 레시피추가요청_생성(상품)); - - final var 예상_응답_페이지 = 응답_페이지_생성(총_데이터_개수(3L), 총_페이지(1L), 첫페이지O, 마지막페이지O, FIRST_PAGE, PAGE_SIZE); - - // when - final var response = 레시피_목록_요청(과거순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(response, 정상_처리); - 페이지를_검증한다(response, 예상_응답_페이지); - 레시피_목록_조회_결과를_검증한다(response, List.of(레시피1, 레시피2, 레시피3)); - } - } - - @Nested - class getRankingRecipes_성공_테스트 { - - @Test - void 전체_꿀조합들_중에서_랭킹_TOP3를_조회할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지2), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지3), 레시피추가요청_생성(상품)); - 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지4), 레시피추가요청_생성(상품)); - 여러명이_레시피_좋아요_요청(List.of(멤버1, 멤버2), 레시피2, 좋아요O); - 여러명이_레시피_좋아요_요청(List.of(멤버1), 레시피3, 좋아요O); - 여러명이_레시피_좋아요_요청(List.of(멤버1, 멤버2, 멤버3), 레시피4, 좋아요O); - - // when - final var 응답 = 레시피_랭킹_조회_요청(); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 레시피_랭킹_조회_결과를_검증한다(응답, List.of(레시피4, 레시피2, 레시피3)); - } - } - - @Nested - class writeRecipeComment_성공_테스트 { - - @Test - void 꿀조합에_댓글을_작성할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 꿀조합_작성_응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - // when - final var 작성된_꿀조합_아이디 = 작성된_꿀조합_아이디_추출(꿀조합_작성_응답); - final var 댓글작성자_로그인_쿠키_획득 = 로그인_쿠키_획득(멤버2); - final var 꿀조합_댓글 = new RecipeCommentCreateRequest("테스트 코멘트 1"); - - final var 응답 = 레시피_댓글_작성_요청(댓글작성자_로그인_쿠키_획득, 작성된_꿀조합_아이디, 꿀조합_댓글); - - // then - STATUS_CODE를_검증한다(응답, 정상_생성); - 꿀조합_댓글_작성_결과를_검증한다(응답, 멤버2, 꿀조합_댓글); - } - } - - @Nested - class writeRecipeComment_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 꿀조합에_댓글을_작성할때_댓글이_비어있을시_예외가_발생한다(final String comment) { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 꿀조합_작성_응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - // when - final var 작성된_꿀조합_아이디 = 작성된_꿀조합_아이디_추출(꿀조합_작성_응답); - final var 댓글작성자_로그인_쿠키_획득 = 로그인_쿠키_획득(멤버2); - final var 꿀조합_댓글 = new RecipeCommentCreateRequest(comment); - - final var 레시피_댓글_작성_요청 = 레시피_댓글_작성_요청(댓글작성자_로그인_쿠키_획득, 작성된_꿀조합_아이디, 꿀조합_댓글); - - // then - STATUS_CODE를_검증한다(레시피_댓글_작성_요청, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(레시피_댓글_작성_요청, REQUEST_VALID_ERROR_CODE.getCode(), - "꿀조합 댓글을 확인해 주세요. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @Test - void 꿀조합에_댓글을_작성할때_댓글이_200자_초과시_예외가_발생한다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 꿀조합_작성_응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - // when - final var 작성된_꿀조합_아이디 = 작성된_꿀조합_아이디_추출(꿀조합_작성_응답); - final var 댓글작성자_로그인_쿠키_획득 = 로그인_쿠키_획득(멤버2); - final var 꿀조합_댓글 = new RecipeCommentCreateRequest("1" + "댓글입니다".repeat(40)); - - final var 응답 = 레시피_댓글_작성_요청(댓글작성자_로그인_쿠키_획득, 작성된_꿀조합_아이디, 꿀조합_댓글); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "꿀조합 댓글은 최대 200자까지 입력 가능합니다. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @ParameterizedTest - @NullAndEmptySource - void 로그인_하지않은_사용자가_꿀조합_댓글_작성시_예외가_발생한다(final String cookie) { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 꿀조합_작성_응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - // when - final var 작성된_꿀조합_아이디 = 작성된_꿀조합_아이디_추출(꿀조합_작성_응답); - final var 꿀조합_댓글 = new RecipeCommentCreateRequest("테스트 코멘트 1"); - - final var 응답 = 레시피_댓글_작성_요청(cookie, 작성된_꿀조합_아이디, 꿀조합_댓글); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), - LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - } - - @Nested - class getRecipeComment_성공_테스트 { - - @Test - void 꿀조합에_댓글을_조회할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 꿀조합_작성_응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - final var 작성된_꿀조합_아이디 = 작성된_꿀조합_아이디_추출(꿀조합_작성_응답); - final var 댓글작성자_로그인_쿠키_획득 = 로그인_쿠키_획득(멤버2); - - for (int i = 1; i <= 15; i++) { - 레시피_댓글_작성_요청(댓글작성자_로그인_쿠키_획득, 작성된_꿀조합_아이디, - new RecipeCommentCreateRequest("테스트 코멘트" + i)); - } - - // when - final var 응답 = 레시피_댓글_조회_요청(로그인_쿠키_획득(멤버1), 작성된_꿀조합_아이디, - new RecipeCommentCondition(null, null)); - - // then - final var expectedSize = 10; - final var expectedHasNext = true; - - STATUS_CODE를_검증한다(응답, 정상_처리); - 레시피_댓글_조회_결과를_검증한다(응답, expectedSize, expectedHasNext); - } - - @Test - void 꿀조합에_댓글을_마지막_페이지를_조회할_수_있다() { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 꿀조합_작성_응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - final var 작성된_꿀조합_아이디 = 작성된_꿀조합_아이디_추출(꿀조합_작성_응답); - final var 댓글작성자_로그인_쿠키_획득 = 로그인_쿠키_획득(멤버2); - - final var totalElements = 15L; - final var lastId = 6L; - - for (int i = 1; i <= totalElements; i++) { - 레시피_댓글_작성_요청(댓글작성자_로그인_쿠키_획득, 작성된_꿀조합_아이디, - new RecipeCommentCreateRequest("테스트 코멘트" + i)); - } - - // when - final var 응답 = 레시피_댓글_조회_요청(로그인_쿠키_획득(멤버1), 작성된_꿀조합_아이디, new RecipeCommentCondition(lastId, totalElements)); - - // then - final var expectedSize = 5; - final var expectedHasNext = false; - - STATUS_CODE를_검증한다(응답, 정상_처리); - 레시피_댓글_조회_결과를_검증한다(응답, expectedSize, expectedHasNext); - } - } - - @Nested - class getRecipeComment_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 로그인_하지않은_사용자가_꿀조합_댓글_조회시_예외가_발생한다(final String cookie) { - // given - final var 카테고리 = 카테고리_간편식사_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점5점_생성(카테고리)); - final var 꿀조합_작성_응답 = 레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지1), 레시피추가요청_생성(상품)); - - final var 작성된_꿀조합_아이디 = 작성된_꿀조합_아이디_추출(꿀조합_작성_응답); - final var 댓글작성자_로그인_쿠키_획득 = 로그인_쿠키_획득(멤버2); - final var 꿀조합_댓글 = new RecipeCommentCreateRequest("테스트 코멘트"); - - 레시피_댓글_작성_요청(댓글작성자_로그인_쿠키_획득, 작성된_꿀조합_아이디, 꿀조합_댓글); - - // when - final var 응답 = 레시피_댓글_조회_요청(cookie, 작성된_꿀조합_아이디, - new RecipeCommentCondition(6L, 15L)); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), - LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - } - - private void 레시피_목록_조회_결과를_검증한다(final ExtractableResponse response, final List recipeIds) { - final var actual = response.jsonPath().getList("recipes", RecipeDto.class); - - assertThat(actual).extracting(RecipeDto::getId) - .containsExactlyElementsOf(recipeIds); - } - - private void 레시피_상세_정보_조회_결과를_검증한다(final ExtractableResponse response) { - final var actual = response.as(RecipeDetailResponse.class); - final var actualAuthor = response.jsonPath().getObject("author", RecipeAuthorDto.class); - final var actualProducts = response.jsonPath().getList("products", ProductRecipeDto.class); - - assertSoftly(soft -> { - soft.assertThat(actual.getId()).isEqualTo(1L); - soft.assertThat(actual.getImages()).hasSize(0); - soft.assertThat(actual.getTitle()).isEqualTo("The most delicious recipes"); - soft.assertThat(actual.getContent()).isEqualTo("More rice, more rice, more rice.. Done!!"); - soft.assertThat(actual.getTotalPrice()).isEqualTo(4000L); - soft.assertThat(actual.getFavoriteCount()).isEqualTo(0L); - soft.assertThat(actual.getFavorite()).isEqualTo(false); - soft.assertThat(actualAuthor.getNickname()).isEqualTo("member1"); - soft.assertThat(actualAuthor.getProfileImage()).isEqualTo("www.member1.com"); - soft.assertThat(actualProducts).extracting(ProductRecipeDto::getId) - .containsExactlyElementsOf(List.of(1L, 2L)); - }); - } - - private void RESPONSE_CODE와_MESSAGE를_검증한다(final ExtractableResponse response, final String expectedCode, - final String expectedMessage) { - assertSoftly(soft -> { - soft.assertThat(response.jsonPath().getString("code")) - .isEqualTo(expectedCode); - soft.assertThat(response.jsonPath().getString("message")) - .isEqualTo(expectedMessage); - }); - } - - private List 예상_레시피_랭킹_변환(final List recipes, final Member member) { - return recipes.stream() - .map(it -> RankingRecipeDto.toDto(it, Collections.emptyList(), RecipeAuthorDto.toDto(member))) - .collect(Collectors.toList()); - } - - private void 레시피_랭킹_조회_결과를_검증한다(final ExtractableResponse response, final List recipeIds) { - final var actual = response.jsonPath() - .getList("recipes", RankingRecipeDto.class); - - assertThat(actual).extracting(RankingRecipeDto::getId) - .isEqualTo(recipeIds); - } - - private void 레시피_검색_결과를_검증한다(final ExtractableResponse response, final List recipeIds) { - final var actual = response.jsonPath() - .getList("recipes", SearchRecipeResultDto.class); - - assertThat(actual).extracting(SearchRecipeResultDto::getId) - .containsExactlyElementsOf(recipeIds); - } - - private Long 작성된_꿀조합_아이디_추출(final ExtractableResponse response) { - return Long.parseLong(response.header("Location").split("/")[3]); - } - - private void 꿀조합_댓글_작성_결과를_검증한다(final ExtractableResponse response, final Long memberId, - final RecipeCommentCreateRequest request) { - final var savedCommentId = Long.parseLong(response.header("Location").split("/")[4]); - - final var findComments = commentRepository.findAll(); - - assertSoftly(soft -> { - soft.assertThat(savedCommentId).isEqualTo(findComments.get(0).getId()); - soft.assertThat(memberId).isEqualTo(findComments.get(0).getMember().getId()); - soft.assertThat(request.getComment()).isEqualTo(findComments.get(0).getComment()); - }); - } - - private void 레시피_댓글_조회_결과를_검증한다(final ExtractableResponse response, final int expectedSize, - final boolean expectedHasNext) { - final var actualComments = response.jsonPath().getList("comments", RecipeCommentResponse.class); - final var actualHasNext = response.jsonPath().getBoolean("hasNext"); - - assertThat(actualComments).hasSize(expectedSize); - assertThat(actualHasNext).isEqualTo(expectedHasNext); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/recipe/RecipeSteps.java b/backend/src/test/java/com/funeat/acceptance/recipe/RecipeSteps.java deleted file mode 100644 index 8d68fe7e9..000000000 --- a/backend/src/test/java/com/funeat/acceptance/recipe/RecipeSteps.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.funeat.acceptance.recipe; - -import static com.funeat.acceptance.auth.LoginSteps.로그인_쿠키_획득; -import static com.funeat.fixture.RecipeFixture.레시피좋아요요청_생성; -import static io.restassured.RestAssured.given; - -import com.funeat.recipe.dto.RecipeCommentCondition; -import com.funeat.recipe.dto.RecipeCommentCreateRequest; -import com.funeat.recipe.dto.RecipeCreateRequest; -import com.funeat.recipe.dto.RecipeFavoriteRequest; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import io.restassured.specification.MultiPartSpecification; -import java.util.List; -import java.util.Objects; - -@SuppressWarnings("NonAsciiCharacters") -public class RecipeSteps { - - public static ExtractableResponse 레시피_작성_요청(final String loginCookie, - final List images, - final RecipeCreateRequest recipeRequest) { - final var requestSpec = given() - .cookie("SESSION", loginCookie); - - if (Objects.nonNull(images) && !images.isEmpty()) { - images.forEach(requestSpec::multiPart); - } - - return requestSpec - .multiPart("recipeRequest", recipeRequest, "application/json") - .when() - .post("/api/recipes") - .then() - .extract(); - } - - public static ExtractableResponse 레시피_상세_정보_요청(final String loginCookie, final Long recipeId) { - return given() - .cookie("SESSION", loginCookie) - .when() - .get("/api/recipes/{recipeId}", recipeId) - .then() - .extract(); - } - - public static ExtractableResponse 레시피_목록_요청(final String sort, final Long page) { - return given() - .queryParam("sort", sort) - .queryParam("page", page) - .when() - .get("/api/recipes") - .then() - .extract(); - } - - public static ExtractableResponse 레시피_좋아요_요청(final String loginCookie, final Long recipeId, - final RecipeFavoriteRequest request) { - return given() - .cookie("SESSION", loginCookie) - .contentType("application/json") - .body(request) - .when() - .patch("/api/recipes/{recipeId}", recipeId) - .then() - .extract(); - } - - public static void 여러명이_레시피_좋아요_요청(final List memberIds, final Long recipeId, - final Boolean favorite) { - final var request = 레시피좋아요요청_생성(favorite); - - for (final var memberId : memberIds) { - 레시피_좋아요_요청(로그인_쿠키_획득(memberId), recipeId, request); - } - } - - public static ExtractableResponse 레시피_랭킹_조회_요청() { - return given() - .when() - .get("/api/ranks/recipes") - .then() - .extract(); - } - - public static ExtractableResponse 레시피_검색_결과_조회_요청(final String query, final Long page) { - return given() - .queryParam("query", query) - .queryParam("page", page) - .when() - .get("/api/search/recipes/results") - .then() - .extract(); - } - - public static ExtractableResponse 레시피_댓글_작성_요청(final String loginCookie, - final Long recipeId, - final RecipeCommentCreateRequest request) { - return given() - .cookie("SESSION", loginCookie) - .contentType("application/json") - .body(request) - .when() - .post("/api/recipes/" + recipeId + "/comments") - .then() - .extract(); - } - - public static ExtractableResponse 레시피_댓글_조회_요청(final String loginCookie, final Long recipeId, - final RecipeCommentCondition condition) { - return given() - .cookie("SESSION", loginCookie) - .contentType("application/json") - .param("lastId", condition.getLastId()) - .param("totalElements", condition.getTotalElements()) - .when() - .get("/api/recipes/" + recipeId + "/comments") - .then() - .extract(); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java deleted file mode 100644 index 233cc4ca5..000000000 --- a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java +++ /dev/null @@ -1,752 +0,0 @@ -package com.funeat.acceptance.review; - -import static com.funeat.acceptance.auth.LoginSteps.로그인_쿠키_획득; -import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; -import static com.funeat.acceptance.common.CommonSteps.다음_데이터가_있는지_검증한다; -import static com.funeat.acceptance.common.CommonSteps.사진_명세_요청; -import static com.funeat.acceptance.common.CommonSteps.인증되지_않음; -import static com.funeat.acceptance.common.CommonSteps.잘못된_요청; -import static com.funeat.acceptance.common.CommonSteps.정상_생성; -import static com.funeat.acceptance.common.CommonSteps.정상_처리; -import static com.funeat.acceptance.common.CommonSteps.정상_처리_NO_CONTENT; -import static com.funeat.acceptance.common.CommonSteps.찾을수_없음; -import static com.funeat.acceptance.product.ProductSteps.상품_상세_조회_요청; -import static com.funeat.acceptance.review.ReviewSteps.리뷰_랭킹_조회_요청; -import static com.funeat.acceptance.review.ReviewSteps.리뷰_상세_조회_요청; -import static com.funeat.acceptance.review.ReviewSteps.리뷰_작성_요청; -import static com.funeat.acceptance.review.ReviewSteps.리뷰_좋아요_요청; -import static com.funeat.acceptance.review.ReviewSteps.여러명이_리뷰_좋아요_요청; -import static com.funeat.acceptance.review.ReviewSteps.정렬된_리뷰_목록_조회_요청; -import static com.funeat.acceptance.review.ReviewSteps.좋아요를_제일_많이_받은_리뷰_조회_요청; -import static com.funeat.auth.exception.AuthErrorCode.LOGIN_MEMBER_NOT_FOUND; -import static com.funeat.exception.CommonErrorCode.REQUEST_VALID_ERROR_CODE; -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static com.funeat.fixture.ImageFixture.이미지1; -import static com.funeat.fixture.ImageFixture.이미지2; -import static com.funeat.fixture.ImageFixture.이미지3; -import static com.funeat.fixture.ImageFixture.이미지4; -import static com.funeat.fixture.ImageFixture.이미지5; -import static com.funeat.fixture.MemberFixture.멤버1; -import static com.funeat.fixture.MemberFixture.멤버2; -import static com.funeat.fixture.MemberFixture.멤버3; -import static com.funeat.fixture.PageFixture.FIRST_PAGE; -import static com.funeat.fixture.PageFixture.좋아요수_내림차순; -import static com.funeat.fixture.PageFixture.최신순; -import static com.funeat.fixture.PageFixture.평점_내림차순; -import static com.funeat.fixture.PageFixture.평점_오름차순; -import static com.funeat.fixture.ProductFixture.상품1; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점3점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점3점_생성; -import static com.funeat.fixture.ProductFixture.존재하지_않는_상품; -import static com.funeat.fixture.ReviewFixture.다음_데이터_존재X; -import static com.funeat.fixture.ReviewFixture.리뷰; -import static com.funeat.fixture.ReviewFixture.리뷰1; -import static com.funeat.fixture.ReviewFixture.리뷰2; -import static com.funeat.fixture.ReviewFixture.리뷰3; -import static com.funeat.fixture.ReviewFixture.리뷰4; -import static com.funeat.fixture.ReviewFixture.리뷰좋아요요청_생성; -import static com.funeat.fixture.ReviewFixture.리뷰추가요청_생성; -import static com.funeat.fixture.ReviewFixture.리뷰추가요청_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰추가요청_재구매X_생성; -import static com.funeat.fixture.ReviewFixture.재구매O; -import static com.funeat.fixture.ReviewFixture.존재하지_않는_리뷰; -import static com.funeat.fixture.ReviewFixture.좋아요O; -import static com.funeat.fixture.ReviewFixture.좋아요X; -import static com.funeat.fixture.ReviewFixture.첫_목록을_가져옴; -import static com.funeat.fixture.ScoreFixture.점수_1점; -import static com.funeat.fixture.ScoreFixture.점수_2점; -import static com.funeat.fixture.ScoreFixture.점수_3점; -import static com.funeat.fixture.ScoreFixture.점수_4점; -import static com.funeat.fixture.ScoreFixture.점수_5점; -import static com.funeat.fixture.TagFixture.태그_맛있어요_TASTE_생성; -import static com.funeat.product.exception.ProductErrorCode.PRODUCT_NOT_FOUND; -import static com.funeat.review.exception.ReviewErrorCode.REVIEW_NOT_FOUND; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import com.funeat.acceptance.common.AcceptanceTest; -import com.funeat.review.dto.RankingReviewDto; -import com.funeat.review.dto.ReviewCreateRequest; -import com.funeat.review.dto.ReviewDetailResponse; -import com.funeat.review.dto.SortingReviewDto; -import com.funeat.tag.dto.TagDto; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullAndEmptySource; - -@SuppressWarnings("NonAsciiCharacters") -class ReviewAcceptanceTest extends AcceptanceTest { - - @Nested - class writeReview_성공_테스트 { - - @Test - void 이미지를_포함하여_리뷰를_작성한다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - // when - final var 응답 = 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), - 리뷰추가요청_재구매O_생성(점수_1점, List.of(태그))); - - // then - STATUS_CODE를_검증한다(응답, 정상_생성); - } - - @Test - void 이미지가_없어도_리뷰를_작성할_수_있다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - // when - final var 응답 = 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, null, - 리뷰추가요청_재구매O_생성(점수_1점, List.of(태그))); - - // then - STATUS_CODE를_검증한다(응답, 정상_생성); - } - } - - @Nested - class writeReview_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 로그인_하지않은_사용자가_리뷰_작성시_예외가_발생한다(final String cookie) { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - // when - final var 응답 = 리뷰_작성_요청(cookie, 상품, 사진_명세_요청(이미지1), - 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), - LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - - @Test - void 사용자가_리뷰_작성할때_태그들이_NULL일시_예외가_발생한다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - - // when - final var 응답 = 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), - 리뷰추가요청_재구매O_생성(점수_4점, null)); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "태그 ID 목록을 확인해 주세요. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @Test - void 사용자가_리뷰_작성할때_태그들이_비어있을시_예외가_발생한다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - - // when - final var 응답 = 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), - 리뷰추가요청_재구매O_생성(점수_4점, Collections.emptyList())); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "적어도 1개의 태그 ID가 필요합니다. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @Test - void 사용자가_리뷰_작성할때_평점이_비어있을시_예외가_발생한다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - // when - final var 응답 = 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), - 리뷰추가요청_재구매O_생성(null, List.of(태그))); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "평점을 확인해 주세요. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @ParameterizedTest - @NullAndEmptySource - void 사용자가_리뷰_작성할때_리뷰내용이_비어있을시_예외가_발생한다(final String content) { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - final var 요청 = 리뷰추가요청_생성(점수_1점, List.of(태그), content, 재구매O); - - // when - final var 응답 = 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 요청); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "리뷰 내용을 확인해 주세요. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @Test - void 사용자가_리뷰_작성할때_재구매여부가_비어있을시_예외가_발생한다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - final var 요청 = 리뷰추가요청_생성(점수_1점, List.of(태그), "content", null); - - // when - final var 응답 = 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 요청); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "재구매 여부를 입력해주세요. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @Test - void 사용자가_리뷰_작성할때_리뷰내용이_200자_초과시_예외가_발생한다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - final var 리뷰내용_201자 = "test".repeat(50) + "a"; - final var 요청 = new ReviewCreateRequest(점수_1점, List.of(태그), 리뷰내용_201자, 재구매O); - - // when - final var 응답 = 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 요청); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "리뷰 내용은 최대 200자까지 입력 가능합니다. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - } - - @Nested - class toggleLikeReview_성공_테스트 { - - @Test - void 리뷰에_좋아요를_할_수_있다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - - // when - final var 응답 = 리뷰_좋아요_요청(로그인_쿠키_획득(멤버1), 상품, 리뷰, 리뷰좋아요요청_생성(좋아요O)); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리_NO_CONTENT); - } - - @Test - void 리뷰에_좋아요를_취소할_수_있다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - 리뷰_좋아요_요청(로그인_쿠키_획득(멤버1), 상품, 리뷰, 리뷰좋아요요청_생성(좋아요O)); - - // when - final var 응답 = 리뷰_좋아요_요청(로그인_쿠키_획득(멤버1), 상품, 리뷰, 리뷰좋아요요청_생성(좋아요X)); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리_NO_CONTENT); - } - - @Test - void 상품_이미지가_존재하는_좋아요를_가장_많이_받은_리뷰로_상품_이미지가_바뀐다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지2), 리뷰추가요청_재구매O_생성(점수_1점, List.of(태그))); - - 리뷰_좋아요_요청(로그인_쿠키_획득(멤버1), 상품, 리뷰2, 리뷰좋아요요청_생성(좋아요O)); - - // when - final var 응답 = 상품_상세_조회_요청(상품); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 상품_사진을_검증한다(응답, "2.png"); - } - } - - @Nested - class toggleLikeReview_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 로그인_하지않은_사용자가_리뷰에_좋아요를_할때_예외가_발생한다(final String cookie) { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - - // when - final var 응답 = 리뷰_좋아요_요청(cookie, 상품, 리뷰, 리뷰좋아요요청_생성(좋아요O)); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), - LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - - @Test - void 사용자가_리뷰에_좋아요를_할때_좋아요_미기입시_예외가_발생한다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - - // when - final var 응답 = 리뷰_좋아요_요청(로그인_쿠키_획득(멤버1), 상품, 리뷰, 리뷰좋아요요청_생성(null)); - - // then - STATUS_CODE를_검증한다(응답, 잘못된_요청); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REQUEST_VALID_ERROR_CODE.getCode(), - "좋아요를 확인해주세요. " + REQUEST_VALID_ERROR_CODE.getMessage()); - } - - @Test - void 존재하지_않는_리뷰에_사용자가_좋아요를_할때_예외가_발생한다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - - // when - final var 응답 = 리뷰_좋아요_요청(로그인_쿠키_획득(멤버1), 상품, 존재하지_않는_리뷰, 리뷰좋아요요청_생성(좋아요O)); - - // then - STATUS_CODE를_검증한다(응답, 찾을수_없음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REVIEW_NOT_FOUND.getCode(), REVIEW_NOT_FOUND.getMessage()); - } - } - - @Nested - class getSortingReviews_성공_테스트 { - - @Nested - class 좋아요_기준_내림차순으로_리뷰_목록_조회 { - - @Test - void 좋아요_수가_서로_다르면_좋아요_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품, 사진_명세_요청(이미지2), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버3), 상품, 사진_명세_요청(이미지3), 리뷰추가요청_재구매X_생성(점수_3점, List.of(태그))); - 여러명이_리뷰_좋아요_요청(List.of(멤버1), 상품, 리뷰3, 좋아요O); - 여러명이_리뷰_좋아요_요청(List.of(멤버2, 멤버3), 상품, 리뷰2, 좋아요O); - - // when - final var 응답 = 정렬된_리뷰_목록_조회_요청(로그인_쿠키_획득(멤버1), 상품, 첫_목록을_가져옴, 좋아요수_내림차순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 다음_데이터_존재X); - 정렬된_리뷰_목록_조회_결과를_검증한다(응답, List.of(리뷰2, 리뷰3, 리뷰1)); - } - - @Test - void 좋아요_수가_서로_같으면_ID_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품, 사진_명세_요청(이미지2), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버3), 상품, 사진_명세_요청(이미지3), 리뷰추가요청_재구매X_생성(점수_3점, List.of(태그))); - - // when - final var 응답 = 정렬된_리뷰_목록_조회_요청(로그인_쿠키_획득(멤버1), 상품, 첫_목록을_가져옴, 좋아요수_내림차순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 다음_데이터_존재X); - 정렬된_리뷰_목록_조회_결과를_검증한다(응답, List.of(리뷰3, 리뷰2, 리뷰1)); - } - } - - @Nested - class 평점_기준_오름차순으로_리뷰_목록을_조회 { - - @Test - void 평점이_서로_다르면_평점_기준_오름차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_2점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품, 사진_명세_요청(이미지2), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버3), 상품, 사진_명세_요청(이미지3), 리뷰추가요청_재구매X_생성(점수_3점, List.of(태그))); - - // when - final var 응답 = 정렬된_리뷰_목록_조회_요청(로그인_쿠키_획득(멤버1), 상품, 첫_목록을_가져옴, 평점_오름차순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 다음_데이터_존재X); - 정렬된_리뷰_목록_조회_결과를_검증한다(응답, List.of(리뷰1, 리뷰3, 리뷰2)); - } - - @Test - void 평점이_서로_같으면_ID_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품, 사진_명세_요청(이미지2), 리뷰추가요청_재구매O_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버3), 상품, 사진_명세_요청(이미지3), 리뷰추가요청_재구매X_생성(점수_3점, List.of(태그))); - - // when - final var 응답 = 정렬된_리뷰_목록_조회_요청(로그인_쿠키_획득(멤버1), 상품, 첫_목록을_가져옴, 평점_오름차순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 다음_데이터_존재X); - 정렬된_리뷰_목록_조회_결과를_검증한다(응답, List.of(리뷰3, 리뷰2, 리뷰1)); - } - } - - @Nested - class 평점_기준_내림차순으로_리뷰_목록_조회 { - - @Test - void 평점이_서로_다르면_평점_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_2점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품, 사진_명세_요청(이미지2), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버3), 상품, 사진_명세_요청(이미지3), 리뷰추가요청_재구매X_생성(점수_3점, List.of(태그))); - - // when - final var 응답 = 정렬된_리뷰_목록_조회_요청(로그인_쿠키_획득(멤버1), 상품, 첫_목록을_가져옴, 평점_내림차순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 다음_데이터_존재X); - 정렬된_리뷰_목록_조회_결과를_검증한다(응답, List.of(리뷰2, 리뷰3, 리뷰1)); - } - - @Test - void 평점이_서로_같으면_ID_기준_내림차순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품, 사진_명세_요청(이미지2), 리뷰추가요청_재구매O_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버3), 상품, 사진_명세_요청(이미지3), 리뷰추가요청_재구매X_생성(점수_3점, List.of(태그))); - - // when - final var 응답 = 정렬된_리뷰_목록_조회_요청(로그인_쿠키_획득(멤버1), 상품, 첫_목록을_가져옴, 평점_내림차순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 다음_데이터_존재X); - 정렬된_리뷰_목록_조회_결과를_검증한다(응답, List.of(리뷰3, 리뷰2, 리뷰1)); - } - } - - @Nested - class 최신순으로_리뷰_목록을_조회 { - - @Test - void 등록_시간이_서로_다르면_최신순으로_정렬할_수_있다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_2점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품, 사진_명세_요청(이미지2), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버3), 상품, 사진_명세_요청(이미지3), 리뷰추가요청_재구매X_생성(점수_3점, List.of(태그))); - - // when - final var 응답 = 정렬된_리뷰_목록_조회_요청(로그인_쿠키_획득(멤버1), 상품, 첫_목록을_가져옴, 최신순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 다음_데이터가_있는지_검증한다(응답, 다음_데이터_존재X); - 정렬된_리뷰_목록_조회_결과를_검증한다(응답, List.of(리뷰3, 리뷰2, 리뷰1)); - } - } - } - - @Nested - class getSortingReviews_실패_테스트 { - - @ParameterizedTest - @NullAndEmptySource - void 로그인_하지않은_사용자가_리뷰_목록을_조회시_예외가_발생한다(final String cookie) { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_2점, List.of(태그))); - - // when - final var 응답 = 정렬된_리뷰_목록_조회_요청(cookie, 상품, 첫_목록을_가져옴, 좋아요수_내림차순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 인증되지_않음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, LOGIN_MEMBER_NOT_FOUND.getCode(), - LOGIN_MEMBER_NOT_FOUND.getMessage()); - } - - @Test - void 존재하지_않는_상품의_리뷰_목록을_조회시_예외가_발생한다() { - // given && when - final var 응답 = 정렬된_리뷰_목록_조회_요청(로그인_쿠키_획득(멤버1), 존재하지_않는_상품, 첫_목록을_가져옴, 좋아요수_내림차순, FIRST_PAGE); - - // then - STATUS_CODE를_검증한다(응답, 찾을수_없음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, PRODUCT_NOT_FOUND.getCode(), PRODUCT_NOT_FOUND.getMessage()); - } - } - - @Nested - class getRankingReviews_성공_테스트 { - - @Test - void 리뷰_랭킹을_조회하다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품1 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 상품2 = 단일_상품_저장(상품_삼각김밥_가격2000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품1, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품1, 사진_명세_요청(이미지2), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버3), 상품1, 사진_명세_요청(이미지3), 리뷰추가요청_재구매O_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품2, 사진_명세_요청(이미지4), 리뷰추가요청_재구매O_생성(점수_5점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품2, 사진_명세_요청(이미지5), 리뷰추가요청_재구매O_생성(점수_1점, List.of(태그))); - 여러명이_리뷰_좋아요_요청(List.of(멤버1, 멤버2, 멤버3), 상품1, 리뷰2, 좋아요O); - 여러명이_리뷰_좋아요_요청(List.of(멤버1, 멤버2), 상품1, 리뷰3, 좋아요O); - 여러명이_리뷰_좋아요_요청(List.of(멤버1), 상품1, 리뷰4, 좋아요O); - - // when - final var 응답 = 리뷰_랭킹_조회_요청(); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 리뷰_랭킹_조회_결과를_검증한다(응답, List.of(리뷰2, 리뷰3, 리뷰4)); - } - } - - @Nested - class getMostFavoriteReview_성공_테스트 { - - @Test - void 특정_상품에서_좋아요를_가장_많이_받은_리뷰를_조회하다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 리뷰추가요청_재구매O_생성(점수_3점, List.of(태그))); - 리뷰_작성_요청(로그인_쿠키_획득(멤버2), 상품, 사진_명세_요청(이미지2), 리뷰추가요청_재구매O_생성(점수_4점, List.of(태그))); - 여러명이_리뷰_좋아요_요청(List.of(멤버1, 멤버2, 멤버3), 상품1, 리뷰2, 좋아요O); - - // when - final var 응답 = 좋아요를_제일_많이_받은_리뷰_조회_요청(상품); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 좋아요를_제일_많이_받은_리뷰_결과를_검증한다(응답, 리뷰2); - } - - @Test - void 특정_상품에서_리뷰가_없다면_빈_응답을_반환하다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - - // when - final var 응답 = 좋아요를_제일_많이_받은_리뷰_조회_요청(상품); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리_NO_CONTENT); - 좋아요를_제일_많이_받은_리뷰_결과가_빈_응답인지_검증한다(응답); - } - } - - @Nested - class getMostFavoriteReview_실패_테스트 { - - @Test - void 존재하지_않는_상품의_좋아요를_가장_많이_받은_리뷰_조회시_예외가_발생한다() { - // given & when - final var 응답 = 좋아요를_제일_많이_받은_리뷰_조회_요청(존재하지_않는_상품); - - // then - STATUS_CODE를_검증한다(응답, 찾을수_없음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, PRODUCT_NOT_FOUND.getCode(), PRODUCT_NOT_FOUND.getMessage()); - } - } - - @Nested - class getReviewDetail_성공_테스트 { - - @Test - void 리뷰_상세_정보를_조회한다() { - // given - final var 카테고리 = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(카테고리); - final var 상품 = 단일_상품_저장(상품_삼각김밥_가격1000원_평점3점_생성(카테고리)); - final var 태그 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - - final var 요청 = 리뷰추가요청_재구매O_생성(점수_3점, List.of(태그)); - 리뷰_작성_요청(로그인_쿠키_획득(멤버1), 상품, 사진_명세_요청(이미지1), 요청); - - // when - final var 응답 = 리뷰_상세_조회_요청(리뷰); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 리뷰_상세_정보_조회_결과를_검증한다(응답, 요청); - } - } - - @Nested - class getReviewDetail_실패_테스트 { - - @Test - void 존재하지_않는_리뷰_조회시_예외가_발생한다() { - // given & when - final var 응답 = 리뷰_상세_조회_요청(존재하지_않는_리뷰); - - // then - STATUS_CODE를_검증한다(응답, 찾을수_없음); - RESPONSE_CODE와_MESSAGE를_검증한다(응답, REVIEW_NOT_FOUND.getCode(), REVIEW_NOT_FOUND.getMessage()); - } - } - - private void RESPONSE_CODE와_MESSAGE를_검증한다(final ExtractableResponse response, final String expectedCode, - final String expectedMessage) { - assertSoftly(soft -> { - soft.assertThat(response.jsonPath().getString("code")) - .isEqualTo(expectedCode); - soft.assertThat(response.jsonPath().getString("message")) - .isEqualTo(expectedMessage); - }); - } - - private void 정렬된_리뷰_목록_조회_결과를_검증한다(final ExtractableResponse response, final List reviewIds) { - final var actual = response.jsonPath().getList("reviews", SortingReviewDto.class); - - assertThat(actual).extracting(SortingReviewDto::getId) - .containsExactlyElementsOf(reviewIds); - } - - private void 리뷰_랭킹_조회_결과를_검증한다(final ExtractableResponse response, final List reviewIds) { - final var actual = response.jsonPath() - .getList("reviews", RankingReviewDto.class); - - assertThat(actual).extracting(RankingReviewDto::getReviewId) - .containsExactlyElementsOf(reviewIds); - } - - private void 좋아요를_제일_많이_받은_리뷰_결과를_검증한다(final ExtractableResponse response, final Long reviewId) { - final var actual = response.jsonPath() - .getLong("id"); - - assertThat(actual).isEqualTo(reviewId); - } - - private void 좋아요를_제일_많이_받은_리뷰_결과가_빈_응답인지_검증한다(final ExtractableResponse response) { - final var actual = response.body() - .asString(); - - assertThat(actual).isEmpty(); - } - - private void 상품_사진을_검증한다(final ExtractableResponse response, final String expected) { - final var actual = response.jsonPath() - .getString("image"); - - assertThat(actual).isEqualTo(expected); - } - - private void 리뷰_상세_정보_조회_결과를_검증한다(final ExtractableResponse response, final ReviewCreateRequest request) { - final var actual = response.as(ReviewDetailResponse.class); - final var actualTags = response.jsonPath().getList("tags", TagDto.class); - - assertSoftly(soft -> { - soft.assertThat(actual.getId()).isEqualTo(리뷰); - soft.assertThat(actual.getImage()).isEqualTo("1.png"); - soft.assertThat(actual.getRating()).isEqualTo(request.getRating()); - soft.assertThat(actual.getContent()).isEqualTo(request.getContent()); - soft.assertThat(actual.isRebuy()).isEqualTo(request.getRebuy()); - soft.assertThat(actual.getFavoriteCount()).isEqualTo(0L); - soft.assertThat(actualTags).extracting(TagDto::getId) - .containsExactlyElementsOf(request.getTagIds()); - }); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/review/ReviewSteps.java b/backend/src/test/java/com/funeat/acceptance/review/ReviewSteps.java deleted file mode 100644 index d1b556d7d..000000000 --- a/backend/src/test/java/com/funeat/acceptance/review/ReviewSteps.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.funeat.acceptance.review; - -import static com.funeat.acceptance.auth.LoginSteps.로그인_쿠키_획득; -import static com.funeat.fixture.ReviewFixture.리뷰좋아요요청_생성; -import static io.restassured.RestAssured.given; - -import com.funeat.review.dto.ReviewCreateRequest; -import com.funeat.review.dto.ReviewFavoriteRequest; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import io.restassured.specification.MultiPartSpecification; -import java.util.List; -import java.util.Objects; - -@SuppressWarnings("NonAsciiCharacters") -public class ReviewSteps { - - public static ExtractableResponse 리뷰_작성_요청(final String loginCookie, final Long productId, - final MultiPartSpecification image, - final ReviewCreateRequest request) { - final var requestSpec = given() - .cookie("SESSION", loginCookie); - - if (Objects.nonNull(image)) { - requestSpec.multiPart(image); - } - - return requestSpec - .multiPart("reviewRequest", request, "application/json") - .when() - .post("/api/products/{productId}/reviews", productId) - .then() - .extract(); - } - - public static ExtractableResponse 리뷰_좋아요_요청(final String loginCookie, final Long productId, - final Long reviewId, final ReviewFavoriteRequest request) { - return given() - .cookie("SESSION", loginCookie) - .contentType("application/json") - .body(request) - .when() - .patch("/api/products/{productId}/reviews/{reviewId}", productId, reviewId) - .then() - .extract(); - } - - public static void 여러명이_리뷰_좋아요_요청(final List memberIds, final Long productId, final Long reviewId, - final Boolean favorite) { - final var request = 리뷰좋아요요청_생성(favorite); - - for (final var memberId : memberIds) { - 리뷰_좋아요_요청(로그인_쿠키_획득(memberId), productId, reviewId, request); - } - } - - public static ExtractableResponse 정렬된_리뷰_목록_조회_요청(final String loginCookie, final Long productId, - final Long lastReviewId, - final String sort, final Long page) { - return given() - .cookie("SESSION", loginCookie) - .queryParam("sort", sort) - .queryParam("page", page) - .queryParam("lastReviewId", lastReviewId).log().all() - .when() - .get("/api/products/{product_id}/reviews", productId) - .then().log().all() - .extract(); - } - - public static ExtractableResponse 리뷰_랭킹_조회_요청() { - return given() - .when() - .get("/api/ranks/reviews") - .then() - .extract(); - } - - public static ExtractableResponse 좋아요를_제일_많이_받은_리뷰_조회_요청(final Long productId) { - return given() - .when() - .get("/api/ranks/products/{product_id}/reviews", productId) - .then().log().all() - .extract(); - } - - public static ExtractableResponse 리뷰_상세_조회_요청(final Long reviewId) { - return given() - .when() - .get("/api/reviews/{reviewId}", reviewId) - .then() - .extract(); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/tag/TagAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/tag/TagAcceptanceTest.java deleted file mode 100644 index 426c7508b..000000000 --- a/backend/src/test/java/com/funeat/acceptance/tag/TagAcceptanceTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.funeat.acceptance.tag; - -import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; -import static com.funeat.acceptance.common.CommonSteps.정상_처리; -import static com.funeat.acceptance.tag.TagSteps.전체_태그_목록_조회_요청; -import static com.funeat.fixture.TagFixture.태그_간식_ETC_생성; -import static com.funeat.fixture.TagFixture.태그_갓성비_PRICE_생성; -import static com.funeat.fixture.TagFixture.태그_단짠단짠_TASTE_생성; -import static com.funeat.fixture.TagFixture.태그_맛있어요_TASTE_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.acceptance.common.AcceptanceTest; -import com.funeat.tag.dto.TagDto; -import com.funeat.tag.dto.TagsResponse; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import java.util.List; -import java.util.stream.Collectors; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -public class TagAcceptanceTest extends AcceptanceTest { - - @Nested - class getAllTags_성공_테스트 { - - @Test - void 전체_태그_목록을_조회할_수_있다() { - // given - final var 맛있어요 = 단일_태그_저장(태그_맛있어요_TASTE_생성()); - final var 단짠단짠 = 단일_태그_저장(태그_단짠단짠_TASTE_생성()); - final var 갓성비 = 단일_태그_저장(태그_갓성비_PRICE_생성()); - final var 간식 = 단일_태그_저장(태그_간식_ETC_생성()); - - // when - final var 응답 = 전체_태그_목록_조회_요청(); - - // then - STATUS_CODE를_검증한다(응답, 정상_처리); - 전체_태그_목록_조회_결과를_검증한다(응답, List.of(맛있어요, 단짠단짠, 갓성비, 간식)); - } - } - - private void 전체_태그_목록_조회_결과를_검증한다(final ExtractableResponse response, final List tagIds) { - final var actual = response.jsonPath() - .getList("", TagsResponse.class); - - final var actualTagIds = actual.stream() - .flatMap(tagsResponse -> tagsResponse.getTags().stream()) - .map(TagDto::getId) - .collect(Collectors.toList()); - - assertThat(actualTagIds).containsExactlyElementsOf(tagIds); - } -} diff --git a/backend/src/test/java/com/funeat/acceptance/tag/TagSteps.java b/backend/src/test/java/com/funeat/acceptance/tag/TagSteps.java deleted file mode 100644 index 53dae95a1..000000000 --- a/backend/src/test/java/com/funeat/acceptance/tag/TagSteps.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.funeat.acceptance.tag; - -import static io.restassured.RestAssured.given; - -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; - -@SuppressWarnings("NonAsciiCharacters") -public class TagSteps { - - public static ExtractableResponse 전체_태그_목록_조회_요청() { - return given() - .when() - .get("/api/tags") - .then() - .extract(); - } -} diff --git a/backend/src/test/java/com/funeat/auth/application/AuthServiceTest.java b/backend/src/test/java/com/funeat/auth/application/AuthServiceTest.java deleted file mode 100644 index 7f82ccb6a..000000000 --- a/backend/src/test/java/com/funeat/auth/application/AuthServiceTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.funeat.auth.application; - -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; - -import com.funeat.auth.dto.SignUserDto; -import com.funeat.common.ServiceTest; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -public class AuthServiceTest extends ServiceTest { - - @Nested - class loginWithKakao_성공_테스트 { - - @Test - void 카카오_로그인을_하여_멤버_정보를_가져온다() { - // given - final var code = "1"; - final var member = 멤버_멤버1_생성(); - final var expected = SignUserDto.of(true, member); - - // when - final var actual = authService.loginWithKakao(code); - - // then - assertThat(actual).usingRecursiveComparison() - .ignoringFields("member.id") - .isEqualTo(expected); - } - } - - @Nested - class logoutWithKakao_성공_테스트 { - - @Test - void 카카오_로그인한_멤버를_로그아웃한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var memberId = member.getId(); - - // when & then - assertDoesNotThrow(() -> authService.logoutWithKakao(memberId)); - } - } -} diff --git a/backend/src/test/java/com/funeat/banner/application/BannerServiceTest.java b/backend/src/test/java/com/funeat/banner/application/BannerServiceTest.java deleted file mode 100644 index dad7292f4..000000000 --- a/backend/src/test/java/com/funeat/banner/application/BannerServiceTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.funeat.banner.application; - -import static com.funeat.fixture.BannerFixture.배너1_생성; -import static com.funeat.fixture.BannerFixture.배너2_생성; -import static com.funeat.fixture.BannerFixture.배너3_생성; -import static com.funeat.fixture.BannerFixture.배너4_생성; -import static com.funeat.fixture.BannerFixture.배너5_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.banner.dto.BannerResponse; -import com.funeat.common.ServiceTest; -import java.util.List; -import java.util.stream.Collectors; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -public class BannerServiceTest extends ServiceTest { - - @Nested - class getBanners_성공_테스트 { - - @Test - void 배너를_아이디_내림차순으로_전체_조회한다() { - // given - final var 배너1 = 배너1_생성(); - final var 배너2 = 배너2_생성(); - final var 배너3 = 배너3_생성(); - final var 배너4 = 배너4_생성(); - final var 배너5 = 배너5_생성(); - 복수_배너_저장(배너1, 배너2, 배너3, 배너4, 배너5); - - // when - final var result = bannerService.getAllBanners(); - - // then - final var 배너들 = List.of(배너5, 배너4, 배너3, 배너2, 배너1); - final var bannerResponses = 배너들.stream() - .map(BannerResponse::toResponse) - .collect(Collectors.toList()); - - assertThat(result).usingRecursiveComparison() - .ignoringFields("id") - .isEqualTo(bannerResponses); - } - } -} diff --git a/backend/src/test/java/com/funeat/banner/persistence/BannerRepositoryTest.java b/backend/src/test/java/com/funeat/banner/persistence/BannerRepositoryTest.java deleted file mode 100644 index 34ef6e6b9..000000000 --- a/backend/src/test/java/com/funeat/banner/persistence/BannerRepositoryTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.funeat.banner.persistence; - -import static com.funeat.fixture.BannerFixture.배너1_생성; -import static com.funeat.fixture.BannerFixture.배너2_생성; -import static com.funeat.fixture.BannerFixture.배너3_생성; -import static com.funeat.fixture.BannerFixture.배너4_생성; -import static com.funeat.fixture.BannerFixture.배너5_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.common.RepositoryTest; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -public class BannerRepositoryTest extends RepositoryTest { - - @Nested - class findAllByOrderByIdDesc_성공_테스트 { - - @Test - void 배너는_아이디가_내림차순으로_조회된다() { - // given - final var 배너1 = 배너1_생성(); - final var 배너2 = 배너2_생성(); - final var 배너3 = 배너3_생성(); - final var 배너4 = 배너4_생성(); - final var 배너5 = 배너5_생성(); - 복수_배너_저장(배너1, 배너2, 배너3, 배너4, 배너5); - - // when - final var bannersOrderByIdDesc = bannerRepository.findAllByOrderByIdDesc(); - - // then - assertThat(bannersOrderByIdDesc).usingRecursiveComparison() - .isEqualTo(List.of(배너5, 배너4, 배너3, 배너2, 배너1)); - } - } -} diff --git a/backend/src/test/java/com/funeat/common/DataCleaner.java b/backend/src/test/java/com/funeat/common/DataCleaner.java deleted file mode 100644 index 9cefb2f36..000000000 --- a/backend/src/test/java/com/funeat/common/DataCleaner.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.funeat.common; - -import java.util.ArrayList; -import java.util.List; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.transaction.Transactional; -import org.springframework.stereotype.Component; - -@Component -public class DataCleaner { - - private static final String FOREIGN_KEY_CHECK_FORMAT = "SET REFERENTIAL_INTEGRITY %s"; - private static final String TRUNCATE_FORMAT = "TRUNCATE TABLE %s"; - private static final String AUTO_INCREMENT_FORMAT = "ALTER TABLE %s ALTER COLUMN id RESTART WITH 1"; - - private final List tableNames = new ArrayList<>(); - - @PersistenceContext - private EntityManager entityManager; - - @SuppressWarnings("unchecked") - @PostConstruct - public void findDatabaseTableNames() { - List tableInfos = entityManager.createNativeQuery("SHOW TABLES").getResultList(); - for (Object[] tableInfo : tableInfos) { - String tableName = (String) tableInfo[0]; - tableNames.add(tableName); - } - } - - @Transactional - public void clear() { - entityManager.clear(); - truncate(); - } - - private void truncate() { - entityManager.createNativeQuery(String.format(FOREIGN_KEY_CHECK_FORMAT, "FALSE")).executeUpdate(); - for (String tableName : tableNames) { - entityManager.createNativeQuery(String.format(TRUNCATE_FORMAT, tableName)).executeUpdate(); - entityManager.createNativeQuery(String.format(AUTO_INCREMENT_FORMAT, tableName)).executeUpdate(); - } - entityManager.createNativeQuery(String.format(FOREIGN_KEY_CHECK_FORMAT, "TRUE")).executeUpdate(); - } -} diff --git a/backend/src/test/java/com/funeat/common/DataClearExtension.java b/backend/src/test/java/com/funeat/common/DataClearExtension.java deleted file mode 100644 index 4c06f51f0..000000000 --- a/backend/src/test/java/com/funeat/common/DataClearExtension.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.funeat.common; - -import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -public class DataClearExtension implements BeforeEachCallback { - - @Override - public void beforeEach(final ExtensionContext context) { - final DataCleaner dataCleaner = getDataCleaner(context); - dataCleaner.clear(); - } - - private DataCleaner getDataCleaner(final ExtensionContext extensionContext) { - return SpringExtension.getApplicationContext(extensionContext) - .getBean(DataCleaner.class); - } -} diff --git a/backend/src/test/java/com/funeat/common/EventTest.java b/backend/src/test/java/com/funeat/common/EventTest.java deleted file mode 100644 index aeb72d418..000000000 --- a/backend/src/test/java/com/funeat/common/EventTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.funeat.common; - -import com.funeat.member.domain.Member; -import com.funeat.member.persistence.MemberRepository; -import com.funeat.product.domain.Category; -import com.funeat.product.domain.Product; -import com.funeat.product.persistence.CategoryRepository; -import com.funeat.product.persistence.ProductRepository; -import com.funeat.review.application.ReviewService; -import com.funeat.review.persistence.ReviewRepository; -import com.funeat.tag.persistence.TagRepository; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.event.ApplicationEvents; -import org.springframework.test.context.event.RecordApplicationEvents; - -@SpringBootTest -@RecordApplicationEvents -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(ReplaceUnderscores.class) -@ExtendWith({MockitoExtension.class, DataClearExtension.class}) -public class EventTest { - - @Autowired - protected ApplicationEvents events; - - @Autowired - protected ReviewService reviewService; - - @Autowired - protected ProductRepository productRepository; - - @Autowired - protected CategoryRepository categoryRepository; - - @Autowired - protected TagRepository tagRepository; - - @Autowired - protected MemberRepository memberRepository; - - @Autowired - protected ReviewRepository reviewRepository; - - @AfterEach - void tearDown() { - events.clear(); - } - - protected Long 단일_상품_저장(final Product product) { - return productRepository.save(product).getId(); - } - - protected Long 단일_카테고리_저장(final Category category) { - return categoryRepository.save(category).getId(); - } - - protected Long 단일_멤버_저장(final Member member) { - return memberRepository.save(member).getId(); - } -} diff --git a/backend/src/test/java/com/funeat/common/RepositoryTest.java b/backend/src/test/java/com/funeat/common/RepositoryTest.java deleted file mode 100644 index 1ae1fff62..000000000 --- a/backend/src/test/java/com/funeat/common/RepositoryTest.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.funeat.common; - -import com.funeat.banner.domain.Banner; -import com.funeat.banner.persistence.BannerRepository; -import com.funeat.member.domain.Member; -import com.funeat.member.domain.favorite.RecipeFavorite; -import com.funeat.member.domain.favorite.ReviewFavorite; -import com.funeat.member.persistence.MemberRepository; -import com.funeat.member.persistence.RecipeFavoriteRepository; -import com.funeat.member.persistence.ReviewFavoriteRepository; -import com.funeat.product.domain.Category; -import com.funeat.product.domain.Product; -import com.funeat.product.domain.ProductRecipe; -import com.funeat.product.persistence.CategoryRepository; -import com.funeat.product.persistence.ProductRecipeRepository; -import com.funeat.product.persistence.ProductRepository; -import com.funeat.recipe.domain.Recipe; -import com.funeat.recipe.domain.RecipeImage; -import com.funeat.recipe.persistence.RecipeImageRepository; -import com.funeat.recipe.persistence.RecipeRepository; -import com.funeat.review.domain.Review; -import com.funeat.review.domain.ReviewTag; -import com.funeat.review.persistence.ReviewRepository; -import com.funeat.review.persistence.ReviewTagRepository; -import com.funeat.tag.domain.Tag; -import com.funeat.tag.persistence.TagRepository; -import java.util.List; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.context.annotation.Import; - -@DataJpaTest -@Import(DataCleaner.class) -@ExtendWith(DataClearExtension.class) -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(ReplaceUnderscores.class) -public abstract class RepositoryTest { - - @Autowired - protected MemberRepository memberRepository; - - @Autowired - protected RecipeFavoriteRepository recipeFavoriteRepository; - - @Autowired - protected ReviewFavoriteRepository reviewFavoriteRepository; - - @Autowired - protected CategoryRepository categoryRepository; - - @Autowired - protected ProductRecipeRepository productRecipeRepository; - - @Autowired - protected ProductRepository productRepository; - - @Autowired - protected RecipeImageRepository recipeImageRepository; - - @Autowired - protected RecipeRepository recipeRepository; - - @Autowired - protected ReviewRepository reviewRepository; - - @Autowired - protected ReviewTagRepository reviewTagRepository; - - @Autowired - protected TagRepository tagRepository; - - @Autowired - protected BannerRepository bannerRepository; - - protected Long 단일_상품_저장(final Product product) { - return productRepository.save(product).getId(); - } - - protected void 복수_상품_저장(final Product... productsToSave) { - final var products = List.of(productsToSave); - - productRepository.saveAll(products); - } - - protected Long 단일_카테고리_저장(final Category category) { - return categoryRepository.save(category).getId(); - } - - protected void 복수_카테고리_저장(final Category... categoriesToSave) { - final var categories = List.of(categoriesToSave); - - categoryRepository.saveAll(categories); - } - - protected Long 단일_멤버_저장(final Member member) { - return memberRepository.save(member).getId(); - } - - protected void 복수_멤버_저장(final Member... membersToSave) { - final var members = List.of(membersToSave); - - memberRepository.saveAll(members); - } - - protected Long 단일_리뷰_저장(final Review review) { - return reviewRepository.save(review).getId(); - } - - protected void 복수_리뷰_저장(final Review... reviewsToSave) { - final var reviews = List.of(reviewsToSave); - - reviewRepository.saveAll(reviews); - } - - protected Long 단일_꿀조합_저장(final Recipe recipe) { - return recipeRepository.save(recipe).getId(); - } - - protected void 복수_꿀조합_저장(final Recipe... recipesToSave) { - final var recipes = List.of(recipesToSave); - - recipeRepository.saveAll(recipes); - } - - protected void 복수_꿀조합_이미지_저장(final RecipeImage... recipeImageToSave) { - final var recipeFavorites = List.of(recipeImageToSave); - - recipeImageRepository.saveAll(recipeFavorites); - } - - protected void 복수_꿀조합_상품_저장(final ProductRecipe... productRecipeImageToSave) { - final var productRecipes = List.of(productRecipeImageToSave); - - productRecipeRepository.saveAll(productRecipes); - } - - protected Long 단일_태그_저장(final Tag tag) { - return tagRepository.save(tag).getId(); - } - - protected void 복수_태그_저장(final Tag... tagsToSave) { - final var tags = List.of(tagsToSave); - - tagRepository.saveAll(tags); - } - - protected Long 단일_리뷰_태그_저장(final ReviewTag reviewTag) { - return reviewTagRepository.save(reviewTag).getId(); - } - - protected void 복수_리뷰_태그_저장(final ReviewTag... reviewTagsToSave) { - final var reviewTags = List.of(reviewTagsToSave); - - reviewTagRepository.saveAll(reviewTags); - } - - protected Long 단일_리뷰_좋아요_저장(final ReviewFavorite reviewFavorite) { - return reviewFavoriteRepository.save(reviewFavorite).getId(); - } - - protected void 복수_리뷰_좋아요_저장(final ReviewFavorite... reviewFavoritesToSave) { - final var reviewFavorites = List.of(reviewFavoritesToSave); - - reviewFavoriteRepository.saveAll(reviewFavorites); - } - - protected Long 단일_레시피_저장(final Recipe recipe) { - return recipeRepository.save(recipe).getId(); - } - - protected void 복수_레시피_저장(final Recipe... recipeToSave) { - final var recipes = List.of(recipeToSave); - - recipeRepository.saveAll(recipes); - } - - protected void 복수_레시피_이미지_저장(final RecipeImage... recipeImageToSave) { - final var images = List.of(recipeImageToSave); - - recipeImageRepository.saveAll(images); - } - - protected void 복수_레시피_상품_저장(final ProductRecipe... productRecipeToSave) { - final var productRecipes = List.of(productRecipeToSave); - - productRecipeRepository.saveAll(productRecipes); - } - - protected void 레시피_좋아요_저장(final RecipeFavorite recipeFavorite) { - recipeFavoriteRepository.save(recipeFavorite); - } - - protected void 복수_배너_저장(final Banner... bannerToSave) { - final List banners = List.of(bannerToSave); - - bannerRepository.saveAll(banners); - } -} diff --git a/backend/src/test/java/com/funeat/common/ServiceTest.java b/backend/src/test/java/com/funeat/common/ServiceTest.java deleted file mode 100644 index 23ee634f5..000000000 --- a/backend/src/test/java/com/funeat/common/ServiceTest.java +++ /dev/null @@ -1,214 +0,0 @@ -package com.funeat.common; - -import com.funeat.auth.application.AuthService; -import com.funeat.banner.application.BannerService; -import com.funeat.banner.domain.Banner; -import com.funeat.banner.persistence.BannerRepository; -import com.funeat.comment.persistence.CommentRepository; -import com.funeat.member.application.TestMemberService; -import com.funeat.member.domain.Member; -import com.funeat.member.domain.favorite.ReviewFavorite; -import com.funeat.member.persistence.MemberRepository; -import com.funeat.member.persistence.RecipeFavoriteRepository; -import com.funeat.member.persistence.ReviewFavoriteRepository; -import com.funeat.product.application.CategoryService; -import com.funeat.product.application.ProductService; -import com.funeat.product.domain.Category; -import com.funeat.product.domain.Product; -import com.funeat.product.domain.ProductRecipe; -import com.funeat.product.persistence.CategoryRepository; -import com.funeat.product.persistence.ProductRecipeRepository; -import com.funeat.product.persistence.ProductRepository; -import com.funeat.recipe.application.RecipeService; -import com.funeat.recipe.domain.Recipe; -import com.funeat.recipe.domain.RecipeImage; -import com.funeat.recipe.persistence.RecipeImageRepository; -import com.funeat.recipe.persistence.RecipeRepository; -import com.funeat.review.application.ReviewService; -import com.funeat.review.domain.Review; -import com.funeat.review.domain.ReviewTag; -import com.funeat.review.persistence.ReviewRepository; -import com.funeat.review.persistence.ReviewTagRepository; -import com.funeat.tag.application.TagService; -import com.funeat.tag.domain.Tag; -import com.funeat.tag.persistence.TagRepository; -import java.util.List; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.transaction.annotation.Transactional; - -@Transactional -@SpringBootTest -@ExtendWith(DataClearExtension.class) -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(ReplaceUnderscores.class) -public abstract class ServiceTest { - - @Autowired - protected MemberRepository memberRepository; - - @Autowired - protected RecipeFavoriteRepository recipeFavoriteRepository; - - @Autowired - protected ReviewFavoriteRepository reviewFavoriteRepository; - - @Autowired - protected CategoryRepository categoryRepository; - - @Autowired - protected ProductRecipeRepository productRecipeRepository; - - @Autowired - protected ProductRepository productRepository; - - @Autowired - protected RecipeImageRepository recipeImageRepository; - - @Autowired - protected RecipeRepository recipeRepository; - - @Autowired - protected ReviewRepository reviewRepository; - - @Autowired - protected ReviewTagRepository reviewTagRepository; - - @Autowired - protected TagRepository tagRepository; - - @Autowired - protected BannerRepository bannerRepository; - - @Autowired - protected CommentRepository commentRepository; - - @Autowired - protected AuthService authService; - - @Autowired - protected TestMemberService memberService; - - @Autowired - protected CategoryService categoryService; - - @Autowired - protected RecipeService recipeService; - - @Autowired - protected ProductService productService; - - @Autowired - protected ReviewService reviewService; - - @Autowired - protected TagService tagService; - - @Autowired - protected BannerService bannerService; - - protected Long 단일_상품_저장(final Product product) { - return productRepository.save(product).getId(); - } - - protected void 복수_상품_저장(final Product... productsToSave) { - final var products = List.of(productsToSave); - - productRepository.saveAll(products); - } - - protected Long 단일_카테고리_저장(final Category category) { - return categoryRepository.save(category).getId(); - } - - protected void 복수_카테고리_저장(final Category... categoriesToSave) { - final var categories = List.of(categoriesToSave); - - categoryRepository.saveAll(categories); - } - - protected Long 단일_멤버_저장(final Member member) { - return memberRepository.save(member).getId(); - } - - protected void 복수_멤버_저장(final Member... membersToSave) { - final var members = List.of(membersToSave); - - memberRepository.saveAll(members); - } - - protected Long 단일_리뷰_저장(final Review review) { - return reviewRepository.save(review).getId(); - } - - protected void 복수_리뷰_저장(final Review... reviewsToSave) { - final var reviews = List.of(reviewsToSave); - - reviewRepository.saveAll(reviews); - } - - protected Long 단일_태그_저장(final Tag tag) { - return tagRepository.save(tag).getId(); - } - - protected void 복수_태그_저장(final Tag... tagsToSave) { - final var tags = List.of(tagsToSave); - - tagRepository.saveAll(tags); - } - - protected Long 단일_리뷰_태그_저장(final ReviewTag reviewTag) { - return reviewTagRepository.save(reviewTag).getId(); - } - - protected void 복수_리뷰_태그_저장(final ReviewTag... reviewTagsToSave) { - final var reviewTags = List.of(reviewTagsToSave); - - reviewTagRepository.saveAll(reviewTags); - } - - protected Long 단일_리뷰_좋아요_저장(final ReviewFavorite reviewFavorite) { - return reviewFavoriteRepository.save(reviewFavorite).getId(); - } - - protected void 복수_리뷰_좋아요_저장(final ReviewFavorite... reviewFavoritesToSave) { - final var reviewFavorites = List.of(reviewFavoritesToSave); - - reviewFavoriteRepository.saveAll(reviewFavorites); - } - - protected void 단일_꿀조합_저장(final Recipe recipe) { - recipeRepository.save(recipe); - } - - protected void 복수_꿀조합_저장(final Recipe... recipesToSave) { - final var recipeFavorites = List.of(recipesToSave); - - recipeRepository.saveAll(recipeFavorites); - } - - protected void 단일_꿀조합_이미지_저장(final RecipeImage recipeImage) { - recipeImageRepository.save(recipeImage); - } - - protected void 복수_꿀조합_이미지_저장(final RecipeImage... recipeImageToSave) { - final var recipeFavorites = List.of(recipeImageToSave); - - recipeImageRepository.saveAll(recipeFavorites); - } - - protected void 복수_꿀조합_상품_저장(final ProductRecipe... productRecipeImageToSave) { - final var productRecipes = List.of(productRecipeImageToSave); - - productRecipeRepository.saveAll(productRecipes); - } - - protected void 복수_배너_저장(final Banner... bannerToSave) { - final List banners = List.of(bannerToSave); - - bannerRepository.saveAll(banners); - } -} diff --git a/backend/src/test/java/com/funeat/common/TestImageUploader.java b/backend/src/test/java/com/funeat/common/TestImageUploader.java deleted file mode 100644 index 642da2176..000000000 --- a/backend/src/test/java/com/funeat/common/TestImageUploader.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.funeat.common; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Comparator; -import java.util.stream.Stream; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; - -@Component -@Profile("test") -public class TestImageUploader implements ImageUploader { - - @Override - public String upload(final MultipartFile image) { - // 실제로 IO 작업을 수행하는 대신, 임시 디렉토리로 복사하도록 수정 - try { - final String temporaryPath = String.valueOf(System.currentTimeMillis()); - final Path tempDirectoryPath = Files.createTempDirectory(temporaryPath); - final Path filePath = tempDirectoryPath.resolve(image.getOriginalFilename()); - Files.copy(image.getInputStream(), filePath); - - deleteDirectory(tempDirectoryPath); - return image.getOriginalFilename(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public void delete(final String fileName) { - } - - private void deleteDirectory(Path directory) throws IOException { - // 디렉토리 내부 파일 및 디렉토리 삭제 - try (Stream pathStream = Files.walk(directory)) { - pathStream.sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); - } - Files.deleteIfExists(directory); - } -} diff --git a/backend/src/test/java/com/funeat/fixture/BannerFixture.java b/backend/src/test/java/com/funeat/fixture/BannerFixture.java deleted file mode 100644 index 9ab4443f5..000000000 --- a/backend/src/test/java/com/funeat/fixture/BannerFixture.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.funeat.fixture; - -import com.funeat.banner.domain.Banner; - -public class BannerFixture { - - public static Banner 배너1_생성() { - return new Banner("배너1링크", "배너1.jpeg"); - } - - public static Banner 배너2_생성() { - return new Banner("배너2링크", "배너2.jpeg"); - } - - public static Banner 배너3_생성() { - return new Banner("배너3링크", "배너3.jpeg"); - } - - public static Banner 배너4_생성() { - return new Banner("배너4링크", "배너4.jpeg"); - } - - public static Banner 배너5_생성() { - return new Banner("배너5링크", "배너5.jpeg"); - } -} diff --git a/backend/src/test/java/com/funeat/fixture/CategoryFixture.java b/backend/src/test/java/com/funeat/fixture/CategoryFixture.java deleted file mode 100644 index 95ee4b056..000000000 --- a/backend/src/test/java/com/funeat/fixture/CategoryFixture.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.funeat.fixture; - -import com.funeat.product.domain.Category; -import com.funeat.product.domain.CategoryType; - -@SuppressWarnings("NonAsciiCharacters") -public class CategoryFixture { - - public static final String 음식 = "food"; - - public static final Long 존재하지_않는_카테고리 = 99999L; - - public static Category 카테고리_간편식사_생성() { - return new Category("간편식사", CategoryType.FOOD, "siksa.jpeg"); - } - - public static Category 카테고리_즉석조리_생성() { - return new Category("즉석조리", CategoryType.FOOD, "direct.jpeg"); - } - - public static Category 카테고리_과자류_생성() { - return new Category("과자류", CategoryType.FOOD, "snack.jpeg"); - } - - public static Category 카테고리_아이스크림_생성() { - return new Category("아이스크림", CategoryType.FOOD, "ice.jpeg"); - } - - public static Category 카테고리_식품_생성() { - return new Category("식품", CategoryType.FOOD, "food.jpeg"); - } - - public static Category 카테고리_음료_생성() { - return new Category("음료", CategoryType.FOOD, "drink.jpeg"); - } - - public static Category 카테고리_CU_생성() { - return new Category("CU", CategoryType.STORE, "cu.jpeg"); - } - - public static Category 카테고리_GS25_생성() { - return new Category("GS25", CategoryType.STORE, "gs25.jpeg"); - } - - public static Category 카테고리_EMART24_생성() { - return new Category("EMART24", CategoryType.STORE, "emart.jpeg"); - } - - public static Category 카테고리_세븐일레븐_생성() { - return new Category("세븐일레븐", CategoryType.STORE, "seven.jpeg"); - } -} diff --git a/backend/src/test/java/com/funeat/fixture/ImageFixture.java b/backend/src/test/java/com/funeat/fixture/ImageFixture.java deleted file mode 100644 index 5e744727e..000000000 --- a/backend/src/test/java/com/funeat/fixture/ImageFixture.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.funeat.fixture; - -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.web.multipart.MultipartFile; - -@SuppressWarnings("NonAsciiCharacters") -public class ImageFixture { - - public static final String 이미지1 = "1"; - public static final String 이미지2 = "2"; - public static final String 이미지3 = "3"; - public static final String 이미지4 = "4"; - public static final String 이미지5 = "5"; - public static final String 이미지6 = "6"; - public static final String 이미지7 = "7"; - - public static MultipartFile 이미지_생성() { - return new MockMultipartFile("image", "image.jpg", "image/jpeg", new byte[]{1, 2, 3}); - } - - public static List 여러_이미지_생성(final int count) { - return IntStream.range(0, count) - .mapToObj(it -> 이미지_생성()) - .collect(Collectors.toList()); - } -} diff --git a/backend/src/test/java/com/funeat/fixture/MemberFixture.java b/backend/src/test/java/com/funeat/fixture/MemberFixture.java deleted file mode 100644 index b6d67798f..000000000 --- a/backend/src/test/java/com/funeat/fixture/MemberFixture.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.funeat.fixture; - -import com.funeat.member.domain.Member; -import com.funeat.member.dto.MemberRequest; - -@SuppressWarnings("NonAsciiCharacters") -public class MemberFixture { - - public static final Long 멤버1 = 1L; - public static final Long 멤버2 = 2L; - public static final Long 멤버3 = 3L; - - - public static Member 멤버_멤버1_생성() { - return new Member("member1", "www.member1.com", "1"); - } - - public static Member 멤버_멤버2_생성() { - return new Member("member2", "www.member2.com", "2"); - } - - public static Member 멤버_멤버3_생성() { - return new Member("member3", "www.member3.com", "3"); - } - - public static MemberRequest 유저닉네임수정요청_생성(final String modifyNickname) { - return new MemberRequest(modifyNickname); - } -} diff --git a/backend/src/test/java/com/funeat/fixture/PageFixture.java b/backend/src/test/java/com/funeat/fixture/PageFixture.java deleted file mode 100644 index 773658f48..000000000 --- a/backend/src/test/java/com/funeat/fixture/PageFixture.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.funeat.fixture; - -import com.funeat.common.dto.PageDto; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; - -@SuppressWarnings("NonAsciiCharacters") -public class PageFixture { - - public static final String 평균_평점_오름차순 = "averageRating,asc"; - public static final String 평균_평점_내림차순 = "averageRating,desc"; - public static final String 가격_오름차순 = "price,asc"; - public static final String 가격_내림차순 = "price,desc"; - public static final String 좋아요수_내림차순 = "favoriteCount,desc"; - public static final String 리뷰수_내림차순 = "reviewCount,desc"; - public static final String 평점_오름차순 = "rating,asc"; - public static final String 평점_내림차순 = "rating,desc"; - public static final String 과거순 = "createdAt,asc"; - public static final String 최신순 = "createdAt,desc"; - public static final String 아이디_내림차순 = "id,desc"; - - public static final Long PAGE_SIZE = 10L; - public static final Long FIRST_PAGE = 0L; - public static final Long SECOND_PAGE = 1L; - - public static final boolean 첫페이지O = true; - public static final boolean 첫페이지X = false; - public static final boolean 마지막페이지O = true; - public static final boolean 마지막페이지X = false; - - public static PageRequest 페이지요청_기본_생성(final int page, final int size) { - return PageRequest.of(page, size); - } - - public static PageRequest 페이지요청_생성(final int page, final int size, final String sort) { - final String[] splitSort = sort.split(","); - final String order = splitSort[0]; - final Direction direction = Direction.fromString(splitSort[1]); - - return PageRequest.of(page, size, Sort.by(direction, order)); - } - - public static PageDto 응답_페이지_생성(final Long totalDataCount, final Long totalPages, final boolean firstPage, - final boolean lastPage, final Long requestPage, final Long requestSize) { - return new PageDto(totalDataCount, totalPages, firstPage, lastPage, requestPage, requestSize); - } - - public static Pageable 페이지요청_좋아요_내림차순_생성(final int page, final int size) { - final var sort = Sort.by(Direction.DESC, "favoriteCount"); - - return PageRequest.of(page, size, sort); - } - - public static Pageable 페이지요청_최신순_생성(final int page, final int size) { - final var sort = Sort.by(Direction.DESC, "createdAt"); - - return PageRequest.of(page, size, sort); - } - - public static Pageable 페이지요청_평점_오름차순_생성(final int page, final int size) { - final var sort = Sort.by(Direction.ASC, "rating"); - - return PageRequest.of(page, size, sort); - } - - public static Pageable 페이지요청_평점_내림차순_생성(final int page, final int size) { - final var sort = Sort.by(Direction.DESC, "rating"); - - return PageRequest.of(page, size, sort); - } - - public static Long 총_데이터_개수(final Long count) { - return count; - } - - public static Long 총_페이지(final Long page) { - return page; - } -} diff --git a/backend/src/test/java/com/funeat/fixture/ProductFixture.java b/backend/src/test/java/com/funeat/fixture/ProductFixture.java deleted file mode 100644 index 6bb7da0d9..000000000 --- a/backend/src/test/java/com/funeat/fixture/ProductFixture.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.funeat.fixture; - -import com.funeat.product.domain.Category; -import com.funeat.product.domain.Product; -import com.funeat.product.domain.ProductRecipe; -import com.funeat.recipe.domain.Recipe; - -@SuppressWarnings("NonAsciiCharacters") -public class ProductFixture { - - public static final Long 존재하지_않는_상품 = 99999L; - public static final Long 상품 = 1L; - public static final Long 상품1 = 1L; - public static final Long 상품2 = 2L; - public static final Long 상품3 = 3L; - public static final Long 상품4 = 4L; - public static final Long 상품5 = 5L; - public static final Long 상품6 = 6L; - public static final Long 상품7 = 7L; - public static final Long 상품8 = 8L; - public static final Long 상품9 = 9L; - public static final Long 상품10 = 10L; - public static final Long 상품11 = 11L; - - public static Product 상품_삼각김밥_가격1000원_평점1점_생성(final Category category) { - return new Product("삼각김밥", 1000L, "image.png", "맛있는 삼각김밥", 1.0, category); - } - - public static Product 상품_삼각김밥_가격1000원_평점2점_생성(final Category category) { - return new Product("삼각김밥", 1000L, "image.png", "맛있는 삼각김밥", 2.0, category); - } - - public static Product 상품_삼각김밥_가격1000원_평점3점_생성(final Category category) { - return new Product("삼각김밥", 1000L, "image.png", "맛있는 삼각김밥", 3.0, category); - } - - public static Product 상품_삼각김밥_가격1000원_평점4점_생성(final Category category) { - return new Product("삼각김밥", 1000L, "image.png", "맛있는 삼각김밥", 4.0, category); - } - - public static Product 상품_삼각김밥_가격1000원_평점5점_생성(final Category category) { - return new Product("삼각김밥", 1000L, "image.png", "맛있는 삼각김밥", 5.0, category); - } - - public static Product 상품_삼각김밥_가격2000원_평점1점_생성(final Category category) { - return new Product("삼각김밥", 2000L, "image.png", "맛있는 삼각김밥", 1.0, category); - } - - public static Product 상품_삼각김밥_가격2000원_평점2점_생성(final Category category) { - return new Product("삼각김밥", 2000L, "image.png", "맛있는 삼각김밥", 2.0, category); - } - - public static Product 상품_삼각김밥_가격2000원_평점3점_생성(final Category category) { - return new Product("삼각김밥", 2000L, "image.png", "맛있는 삼각김밥", 3.0, category); - } - - public static Product 상품_삼각김밥_가격2000원_평점4점_생성(final Category category) { - return new Product("삼각김밥", 2000L, "image.png", "맛있는 삼각김밥", 4.0, category); - } - - public static Product 상품_삼각김밥_가격2000원_평점5점_생성(final Category category) { - return new Product("삼각김밥", 2000L, "image.png", "맛있는 삼각김밥", 5.0, category); - } - - public static Product 상품_삼각김밥_가격3000원_평점1점_생성(final Category category) { - return new Product("삼각김밥", 3000L, "image.png", "맛있는 삼각김밥", 1.0, category); - } - - public static Product 상품_삼각김밥_가격3000원_평점2점_생성(final Category category) { - return new Product("삼각김밥", 3000L, "image.png", "맛있는 삼각김밥", 2.0, category); - } - - public static Product 상품_삼각김밥_가격3000원_평점3점_생성(final Category category) { - return new Product("삼각김밥", 3000L, "image.png", "맛있는 삼각김밥", 3.0, category); - } - - public static Product 상품_삼각김밥_가격3000원_평점4점_생성(final Category category) { - return new Product("삼각김밥", 3000L, "image.png", "맛있는 삼각김밥", 4.0, category); - } - - public static Product 상품_삼각김밥_가격3000원_평점5점_생성(final Category category) { - return new Product("삼각김밥", 3000L, "image.png", "맛있는 삼각김밥", 5.0, category); - } - - public static Product 상품_삼각김밥_가격4000원_평점1점_생성(final Category category) { - return new Product("삼각김밥", 4000L, "image.png", "맛있는 삼각김밥", 1.0, category); - } - - public static Product 상품_삼각김밥_가격4000원_평점2점_생성(final Category category) { - return new Product("삼각김밥", 4000L, "image.png", "맛있는 삼각김밥", 2.0, category); - } - - public static Product 상품_삼각김밥_가격4000원_평점3점_생성(final Category category) { - return new Product("삼각김밥", 4000L, "image.png", "맛있는 삼각김밥", 3.0, category); - } - - public static Product 상품_삼각김밥_가격4000원_평점4점_생성(final Category category) { - return new Product("삼각김밥", 4000L, "image.png", "맛있는 삼각김밥", 4.0, category); - } - - public static Product 상품_삼각김밥_가격4000원_평점5점_생성(final Category category) { - return new Product("삼각김밥", 4000L, "image.png", "맛있는 삼각김밥", 5.0, category); - } - - public static Product 상품_삼각김밥_가격5000원_평점1점_생성(final Category category) { - return new Product("삼각김밥", 5000L, "image.png", "맛있는 삼각김밥", 1.0, category); - } - - public static Product 상품_삼각김밥_가격5000원_평점2점_생성(final Category category) { - return new Product("삼각김밥", 5000L, "image.png", "맛있는 삼각김밥", 2.0, category); - } - - public static Product 상품_삼각김밥_가격5000원_평점3점_생성(final Category category) { - return new Product("삼각김밥", 5000L, "image.png", "맛있는 삼각김밥", 3.0, category); - } - - public static Product 상품_삼각김밥_가격5000원_평점4점_생성(final Category category) { - return new Product("삼각김밥", 5000L, "image.png", "맛있는 삼각김밥", 4.0, category); - } - - public static Product 상품_삼각김밥_가격5000원_평점5점_생성(final Category category) { - return new Product("삼각김밥", 5000L, "image.png", "맛있는 삼각김밥", 5.0, category); - } - - public static Product 상품_망고빙수_가격5000원_평점4점_생성(final Category category) { - return new Product("망고빙수", 5000L, "image.png", "맛있는 망고빙수", 4.0, category); - } - - public static Product 상품_애플망고_가격3000원_평점5점_생성(final Category category) { - return new Product("애플망고", 3000L, "image.png", "맛있는 애플망고", 5.0, category); - } - - public static Product 상품_삼각김밥_가격5000원_리뷰0개_생성(final Category category) { - return new Product("삼각김밥", 5000L, "image.png", "맛있는 삼각김밥", category, 0L); - } - - public static Product 상품_삼각김밥_가격2000원_리뷰1개_생성(final Category category) { - return new Product("삼각김밥", 2000L, "image.png", "맛있는 삼각김밥", category, 1L); - } - - public static Product 상품_삼각김밥_가격1000원_리뷰3개_생성(final Category category) { - return new Product("삼각김밥", 1000L, "image.png", "맛있는 삼각김밥", category, 3L); - } - - public static Product 상품_삼각김밥_가격3000원_리뷰5개_생성(final Category category) { - return new Product("삼각김밥", 3000L, "image.png", "맛있는 삼각김밥", category, 5L); - } - - public static ProductRecipe 레시피_안에_들어가는_상품_생성(final Product product, final Recipe recipe) { - return new ProductRecipe(product, recipe); - } -} diff --git a/backend/src/test/java/com/funeat/fixture/RecipeFixture.java b/backend/src/test/java/com/funeat/fixture/RecipeFixture.java deleted file mode 100644 index 2d3bb3deb..000000000 --- a/backend/src/test/java/com/funeat/fixture/RecipeFixture.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.funeat.fixture; - -import com.funeat.member.domain.Member; -import com.funeat.member.domain.favorite.RecipeFavorite; -import com.funeat.recipe.domain.Recipe; -import com.funeat.recipe.domain.RecipeImage; -import com.funeat.recipe.dto.RecipeCreateRequest; -import com.funeat.recipe.dto.RecipeFavoriteRequest; -import java.time.LocalDateTime; -import java.util.List; - -@SuppressWarnings("NonAsciiCharacters") -public class RecipeFixture { - - public static final Long 레시피 = 1L; - public static final Long 존재하지_않는_레시피 = 99999L; - public static final Long 레시피1 = 1L; - public static final Long 레시피2 = 2L; - public static final Long 레시피3 = 3L; - public static final Long 레시피4 = 4L; - - public static final boolean 좋아요O = true; - public static final boolean 좋아요X = false; - - public static final String 레시피_제목 = "The most delicious recipes"; - public static final String 레시피_본문 = "More rice, more rice, more rice.. Done!!"; - - - public static Recipe 레시피_생성(final Member member) { - return new Recipe("The most delicious recipes", "More rice, more rice, more rice.. Done!!", member); - } - - public static Recipe 레시피_생성(final Member member, final Long favoriteCount) { - return new Recipe("The most delicious recipes", "More rice, more rice, more rice.. Done!!", member, favoriteCount); - } - - public static Recipe 레시피_생성(final Member member, final Long favoriteCount, final LocalDateTime createdAt) { - return new Recipe("The most delicious recipes", "More rice, more rice, more rice.. Done!!", - member, favoriteCount, createdAt); - } - - public static RecipeFavorite 레시피_좋아요_생성(final Member member, final Recipe recipe, final Boolean favorite) { - return new RecipeFavorite(member, recipe, favorite); - } - - public static RecipeCreateRequest 레시피추가요청_생성(final String title, final List productIds, final String content) { - return new RecipeCreateRequest(title, productIds, content); - } - - public static RecipeCreateRequest 레시피추가요청_생성(final Long... productIds) { - return new RecipeCreateRequest("The most delicious recipes", List.of(productIds), "More rice, more rice, more rice.. Done!!"); - } - - public static RecipeCreateRequest 레시피추가요청_생성(final List productIds) { - return new RecipeCreateRequest("The most delicious recipes", productIds, "More rice, more rice, more rice.. Done!!"); - } - - public static RecipeFavoriteRequest 레시피좋아요요청_생성(final Boolean favorite) { - return new RecipeFavoriteRequest(favorite); - } - - public static RecipeImage 레시피이미지_생성(final Recipe recipe) { - return new RecipeImage("제일로 맛없는 사진", recipe); - } -} diff --git a/backend/src/test/java/com/funeat/fixture/ReviewFixture.java b/backend/src/test/java/com/funeat/fixture/ReviewFixture.java deleted file mode 100644 index fee2b0b7b..000000000 --- a/backend/src/test/java/com/funeat/fixture/ReviewFixture.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.funeat.fixture; - -import static com.funeat.fixture.PageFixture.좋아요수_내림차순; -import static com.funeat.fixture.PageFixture.최신순; -import static com.funeat.fixture.PageFixture.평점_내림차순; -import static com.funeat.fixture.PageFixture.평점_오름차순; - -import com.funeat.member.domain.Member; -import com.funeat.product.domain.Product; -import com.funeat.review.domain.Review; -import com.funeat.review.dto.ReviewCreateRequest; -import com.funeat.review.dto.ReviewFavoriteRequest; -import com.funeat.review.dto.SortingReviewRequest; -import java.time.LocalDateTime; -import java.util.List; - -@SuppressWarnings("NonAsciiCharacters") -public class ReviewFixture { - - public static final Long 리뷰 = 1L; - public static final Long 존재하지_않는_리뷰 = 99999L; - public static final Long 리뷰1 = 1L; - public static final Long 리뷰2 = 2L; - public static final Long 리뷰3 = 3L; - public static final Long 리뷰4 = 4L; - public static final boolean 좋아요O = true; - public static final boolean 좋아요X = false; - public static final boolean 재구매O = true; - public static final boolean 재구매X = false; - - public static final Long 첫_목록을_가져옴 = 0L; - public static final boolean 다음_데이터_존재O = true; - public static final boolean 다음_데이터_존재X = false; - - public static Review 리뷰_이미지test1_평점1점_재구매O_생성(final Member member, final Product product, final Long count) { - return new Review(member, product, "test1", 1L, "test", true, count); - } - - public static Review 리뷰_이미지없음_평점1점_재구매O_생성(final Member member, final Product product, final Long count) { - return new Review(member, product, "", 1L, "test", true, count); - } - - public static Review 리뷰_이미지test1_평점1점_재구매X_생성(final Member member, final Product product, final Long count) { - return new Review(member, product, "test1", 1L, "test", false, count); - } - - public static Review 리뷰_이미지test2_평점2점_재구매O_생성(final Member member, final Product product, final Long count) { - return new Review(member, product, "test2", 2L, "test", true, count); - } - - public static Review 리뷰_이미지test2_평점2점_재구매X_생성(final Member member, final Product product, final Long count) { - return new Review(member, product, "test2", 2L, "test", false, count); - } - - public static Review 리뷰_이미지test3_평점3점_재구매O_생성(final Member member, final Product product, final Long count) { - return new Review(member, product, "test3", 3L, "test", true, count); - } - - public static Review 리뷰_이미지test3_평점3점_재구매X_생성(final Member member, final Product product, final Long count) { - return new Review(member, product, "test3", 3L, "test", false, count); - } - - public static Review 리뷰_이미지test4_평점4점_재구매O_생성(final Member member, final Product product, final Long count) { - return new Review(member, product, "test4", 4L, "test", true, count); - } - - public static Review 리뷰_이미지test4_평점4점_재구매X_생성(final Member member, final Product product, final Long count) { - return new Review(member, product, "test4", 4L, "test", false, count); - } - - public static Review 리뷰_이미지test5_평점5점_재구매O_생성(final Member member, final Product product, final Long count) { - return new Review(member, product, "test5", 5L, "test", true, count); - } - - public static Review 리뷰_이미지test5_평점5점_재구매X_생성(final Member member, final Product product, final Long count) { - return new Review(member, product, "test5", 5L, "test", false, count); - } - - public static Review 리뷰_이미지test5_평점5점_재구매X_생성(final Member member, final Product product, final Long count, - final LocalDateTime createdAt) { - return new Review(member, product, "test5", 5L, "test", false, count, createdAt); - } - - public static Review 리뷰_이미지없음_평점1점_재구매X_생성(final Member member, final Product product, final Long count) { - return new Review(member, product, "", 1L, "test", false, count); - } - - public static ReviewCreateRequest 리뷰추가요청_생성(final Long rating, final List tagIds, final String content, - final Boolean rebuy) { - return new ReviewCreateRequest(rating, tagIds, content, rebuy); - } - - public static ReviewCreateRequest 리뷰추가요청_재구매O_생성(final Long rating, final List tagIds) { - return new ReviewCreateRequest(rating, tagIds, "test", true); - } - - public static ReviewCreateRequest 리뷰추가요청_재구매X_생성(final Long rating, final List tagIds) { - return new ReviewCreateRequest(rating, tagIds, "test", false); - } - - public static ReviewFavoriteRequest 리뷰좋아요요청_생성(final Boolean favorite) { - return new ReviewFavoriteRequest(favorite); - } - - public static SortingReviewRequest 리뷰정렬요청_좋아요수_내림차순_생성(final Long lastReviewId) { - return new SortingReviewRequest(좋아요수_내림차순, lastReviewId); - } - - public static SortingReviewRequest 리뷰정렬요청_최신순_생성(final Long lastReviewId) { - return new SortingReviewRequest(최신순, lastReviewId); - } - - public static SortingReviewRequest 리뷰정렬요청_평점_오름차순_생성(final Long lastReviewId) { - return new SortingReviewRequest(평점_오름차순, lastReviewId); - } - - public static SortingReviewRequest 리뷰정렬요청_평점_내림차순_생성(final Long lastReviewId) { - return new SortingReviewRequest(평점_내림차순, lastReviewId); - } - - public static SortingReviewRequest 리뷰정렬요청_존재하지않는정렬_생성() { - return new SortingReviewRequest("test,test", 1L); - } -} diff --git a/backend/src/test/java/com/funeat/fixture/ScoreFixture.java b/backend/src/test/java/com/funeat/fixture/ScoreFixture.java deleted file mode 100644 index 3f9ce8ece..000000000 --- a/backend/src/test/java/com/funeat/fixture/ScoreFixture.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.funeat.fixture; - -@SuppressWarnings("NonAsciiCharacters") -public class ScoreFixture { - - public static final Long 점수_1점 = 1L; - public static final Long 점수_2점 = 2L; - public static final Long 점수_3점 = 3L; - public static final Long 점수_4점 = 4L; - public static final Long 점수_5점 = 5L; -} diff --git a/backend/src/test/java/com/funeat/fixture/TagFixture.java b/backend/src/test/java/com/funeat/fixture/TagFixture.java deleted file mode 100644 index 2aa4da188..000000000 --- a/backend/src/test/java/com/funeat/fixture/TagFixture.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.funeat.fixture; - -import static com.funeat.tag.domain.TagType.ETC; -import static com.funeat.tag.domain.TagType.QUANTITY; -import static com.funeat.tag.domain.TagType.TASTE; - -import com.funeat.tag.domain.Tag; - -@SuppressWarnings("NonAsciiCharacters") -public class TagFixture { - - public static Tag 태그_맛있어요_TASTE_생성() { - return new Tag("맛있어요", TASTE); - } - - public static Tag 태그_단짠단짠_TASTE_생성() { - return new Tag("단짠딴짠", TASTE); - } - - public static Tag 태그_갓성비_PRICE_생성() { - return new Tag("갓성비", QUANTITY); - } - - public static Tag 태그_푸짐해요_PRICE_생성() { - return new Tag("푸짐해요", QUANTITY); - } - - public static Tag 태그_간식_ETC_생성() { - return new Tag("간식", ETC); - } - - public static Tag 태그_아침식사_ETC_생성() { - return new Tag("아침식사", ETC); - } -} diff --git a/backend/src/test/java/com/funeat/member/application/MemberServiceTest.java b/backend/src/test/java/com/funeat/member/application/MemberServiceTest.java deleted file mode 100644 index e67eb942d..000000000 --- a/backend/src/test/java/com/funeat/member/application/MemberServiceTest.java +++ /dev/null @@ -1,309 +0,0 @@ -package com.funeat.member.application; - -import static com.funeat.fixture.ImageFixture.이미지_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import com.funeat.auth.dto.UserInfoDto; -import com.funeat.common.ServiceTest; -import com.funeat.member.dto.MemberProfileResponse; -import com.funeat.member.dto.MemberRequest; -import com.funeat.member.exception.MemberException.MemberNotFoundException; -import com.funeat.member.exception.MemberException.MemberUpdateException; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.mock.web.MockMultipartFile; - -@SuppressWarnings("NonAsciiCharacters") -class MemberServiceTest extends ServiceTest { - - @Nested - class findOrCreateMember_성공_테스트 { - - @Test - void 이미_가입된_사용자면_가입하지_않고_반환한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var userInfoDto = new UserInfoDto(1L, "test", "www.test.com"); - - final var expected = memberRepository.findAll(); - - // when - final var actual = memberService.findOrCreateMember(userInfoDto); - - // then - assertSoftly(soft -> { - soft.assertThat(actual.isSignUp()) - .isFalse(); - soft.assertThat(expected) - .containsExactly(actual.getMember()); - }); - } - - @Test - void 가입되지_않은_사용자면_가입하고_반환하다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var userInfoDto = new UserInfoDto(2L, "test", "www.test.com"); - - final var expected = memberRepository.findAll(); - - // when - final var actual = memberService.findOrCreateMember(userInfoDto); - - // then - assertSoftly(soft -> { - soft.assertThat(actual.isSignUp()) - .isTrue(); - soft.assertThat(expected) - .doesNotContain(actual.getMember()); - }); - } - } - - @Nested - class findOrCreateMember_실패_테스트 { - - @Test - void platformId가_null이면_예외가_발생한다() { - // given - final var userInfoDto = new UserInfoDto(null, "test", "www.test.com"); - - // when & then - assertThatThrownBy(() -> memberService.findOrCreateMember(userInfoDto)) - .isInstanceOf(NullPointerException.class); - } - } - - @Nested - class getMemberProfile_성공_테스트 { - - @Test - void 사용자_정보를_확인하다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var expected = MemberProfileResponse.toResponse(member); - - // when - final var actual = memberService.getMemberProfile(memberId); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class getMemberProfile_실패_테스트 { - - @Test - void 존재하지_않는_사용자_정보를_조회하면_예외가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - final var wrongMemberId = 단일_멤버_저장(member) + 1L; - - // when & then - assertThatThrownBy(() -> memberService.getMemberProfile(wrongMemberId)) - .isInstanceOf(MemberNotFoundException.class); - } - } - - @Nested - class modify_성공_테스트 { - - @Test - void 닉네임과_프로필_사진이_그대로면_사용자_정보는_바뀌지_않는다() { - // given - final var nickname = "member1"; - final var image = new MockMultipartFile("image", "www.member1.com", "image/jpeg", new byte[]{1, 2, 3}); - final var request = new MemberRequest(nickname); - - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var expected = memberRepository.findById(memberId).get(); - final var expectedNickname = expected.getNickname(); - final var expectedProfileImage = expected.getProfileImage(); - - // when - memberService.modify(memberId, image, request); - final var actual = memberRepository.findById(memberId).get(); - final var actualNickname = actual.getNickname(); - final var actualProfileImage = actual.getProfileImage(); - - // then - assertSoftly(soft -> { - soft.assertThat(actualNickname) - .isEqualTo(expectedNickname); - soft.assertThat(actualProfileImage) - .isEqualTo(expectedProfileImage); - }); - } - - @Test - void 닉네임만_바뀌고_프로필_사진은_그대로면_닉네임만_바뀐다() { - // given - final var profileImage = new MockMultipartFile("image", "www.member1.com", "image/jpeg", - new byte[]{1, 2, 3}); - final var afterNickname = "after"; - final var request = new MemberRequest(afterNickname); - - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var expected = memberRepository.findById(memberId).get(); - final var expectedNickname = expected.getNickname(); - final var expectedProfileImage = expected.getProfileImage(); - - // when - memberService.modify(memberId, profileImage, request); - final var actual = memberRepository.findById(memberId).get(); - final var actualNickname = actual.getNickname(); - final var actualProfileImage = actual.getProfileImage(); - - // then - assertSoftly(soft -> { - soft.assertThat(actualNickname) - .isNotEqualTo(expectedNickname); - soft.assertThat(actualProfileImage) - .isEqualTo(expectedProfileImage); - }); - } - - @Test - void 닉네임은_그대로이고_프로필_사진이_바뀌면_프로필_사진만_바뀐다() { - // given - final var nickname = "member1"; - final var afterProfileImage = 이미지_생성(); - final var request = new MemberRequest(nickname); - - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var expected = memberRepository.findById(memberId).get(); - final var expectedNickname = expected.getNickname(); - final var expectedProfileImage = expected.getProfileImage(); - - // when - memberService.modify(memberId, afterProfileImage, request); - final var actual = memberRepository.findById(memberId).get(); - final var actualNickname = actual.getNickname(); - final var actualProfileImage = actual.getProfileImage(); - - // then - assertSoftly(soft -> { - soft.assertThat(actualNickname) - .isEqualTo(expectedNickname); - soft.assertThat(actualProfileImage) - .isNotEqualTo(expectedProfileImage); - }); - } - - @Test - void 닉네임과_프로필_사진_모두_바뀌면_모두_바뀐다() { - // given - final var afterNickname = "after"; - final var afterProfileImage = 이미지_생성(); - - final var request = new MemberRequest(afterNickname); - - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var expected = memberRepository.findById(memberId).get(); - final var expectedNickname = expected.getNickname(); - final var expectedProfileImage = expected.getProfileImage(); - - // when - memberService.modify(memberId, afterProfileImage, request); - final var actual = memberRepository.findById(memberId).get(); - final var actualNickname = actual.getNickname(); - final var actualProfileImage = actual.getProfileImage(); - - // then - assertSoftly(soft -> { - soft.assertThat(actualNickname) - .isNotEqualTo(expectedNickname); - soft.assertThat(actualProfileImage) - .isNotEqualTo(expectedProfileImage); - }); - } - } - - @Nested - class modify_실패_테스트 { - - @Test - void 존재하지않는_멤버를_수정하면_예외가_발생한다() { - // given - final var afterNickname = "after"; - final var afterProfileImage = 이미지_생성(); - - final var member = 멤버_멤버1_생성(); - final var wrongMemberId = 단일_멤버_저장(member) + 1L; - - final var request = new MemberRequest(afterNickname); - - // when - assertThatThrownBy(() -> memberService.modify(wrongMemberId, afterProfileImage, request)) - .isInstanceOf(MemberNotFoundException.class); - } - - @Test - void 닉네임_수정_요청_값을_null로_설정하면_예외가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var request = new MemberRequest(null); - final var image = 이미지_생성(); - - // when & then - assertThatThrownBy(() -> memberService.modify(memberId, image, request)) - .isInstanceOf(MemberUpdateException.class); - } - } - - @Nested - class findPlatformId_성공_테스트 { - - @Test - void 사용자의_OAUTH_회원번호를_확인하다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var expected = member.getPlatformId(); - - // when - final String actual = memberService.findPlatformId(memberId); - - // then - assertThat(actual).isEqualTo(expected); - } - - } - - @Nested - class findPlatformId_실패_테스트 { - - @Test - void 존재하지_않는_사용자의_OAUTH_회원번호를_확인하면_예외가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - final var wrongMemberId = 단일_멤버_저장(member) + 1L; - - // when & then - assertThatThrownBy(() -> memberService.findPlatformId(wrongMemberId)) - .isInstanceOf(MemberNotFoundException.class); - } - } -} diff --git a/backend/src/test/java/com/funeat/member/application/TestMemberService.java b/backend/src/test/java/com/funeat/member/application/TestMemberService.java deleted file mode 100644 index f0bb441b5..000000000 --- a/backend/src/test/java/com/funeat/member/application/TestMemberService.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.funeat.member.application; - -import com.funeat.auth.dto.SignUserDto; -import com.funeat.auth.dto.UserInfoDto; -import com.funeat.common.ImageUploader; -import com.funeat.member.persistence.MemberRepository; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -public class TestMemberService extends MemberService { - - public TestMemberService(final MemberRepository memberRepository, final ImageUploader imageUploader) { - super(memberRepository, imageUploader); - } - - @Override - @Transactional - public SignUserDto findOrCreateMember(final UserInfoDto userInfoDto) { - return super.findOrCreateMember(userInfoDto); - } -} diff --git a/backend/src/test/java/com/funeat/member/domain/MemberTest.java b/backend/src/test/java/com/funeat/member/domain/MemberTest.java deleted file mode 100644 index 25bbda6dc..000000000 --- a/backend/src/test/java/com/funeat/member/domain/MemberTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.funeat.member.domain; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import com.funeat.member.exception.MemberException.MemberUpdateException; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(ReplaceUnderscores.class) -public class MemberTest { - - @Nested - class modifyProfile_성공_테스트 { - - @Test - void 사용자의_닉네임과_이미지_주소를_변경할_수_있다() { - // given - final var member = new Member("before", "http://www.before.com", "1"); - - final var expectedNickname = "after"; - final var expectedProfileImage = "http://www.after.com"; - - // when - member.modifyProfile(expectedNickname, expectedProfileImage); - final var actualNickname = member.getNickname(); - final var actualProfileImage = member.getProfileImage(); - - // then - assertSoftly(soft -> { - soft.assertThat(actualNickname) - .isEqualTo(expectedNickname); - soft.assertThat(actualProfileImage) - .isEqualTo(expectedProfileImage); - }); - } - } - - @Nested - class modifyProfile_실패_테스트 { - - @Test - void 사용자의_닉네임_변경_값이_null이면_예외가_발생한다() { - // given - final var member = new Member("test", "http://www.before.com", "1"); - - final var expectedProfileImage = "http://www.after.com"; - - // when & then - assertThatThrownBy(() -> member.modifyProfile(null, expectedProfileImage)) - .isInstanceOf(MemberUpdateException.class); - } - - @Test - void 사용자의_프로필_이미지_변경_값이_null이면_예외가_발생한다() { - // given - final var member = new Member("test", "http://www.before.com", "1"); - - final var expectedNickname = "after"; - - // when & then - assertThatThrownBy(() -> member.modifyProfile(expectedNickname, null)) - .isInstanceOf(MemberUpdateException.class); - } - } -} diff --git a/backend/src/test/java/com/funeat/member/domain/favorite/RecipeFavoriteTest.java b/backend/src/test/java/com/funeat/member/domain/favorite/RecipeFavoriteTest.java deleted file mode 100644 index 4b2cc940b..000000000 --- a/backend/src/test/java/com/funeat/member/domain/favorite/RecipeFavoriteTest.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.funeat.member.domain.favorite; - -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.RecipeFixture.레시피_생성; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -class RecipeFavoriteTest { - - @Nested - class updateFavorite_성공_테스트 { - - @Test - void 기존_false_신규_true_경우_recipe의_favoriteCount는_1_증가() { - // given - final var member = 멤버_멤버1_생성(); - final var recipe = 레시피_생성(member); - final var recipeFavorite = RecipeFavorite.create(member, recipe, false); - - // when - recipeFavorite.updateFavorite(true); - - // then - assertSoftly(soft -> { - soft.assertThat(recipeFavorite.getRecipe().getFavoriteCount()) - .isOne(); - soft.assertThat(recipeFavorite.getFavorite()) - .isTrue(); - }); - } - - @Test - void 기존_true_신규_false_경우_recipe의_favoriteCount는_1_감소() { - // given - final var member = 멤버_멤버1_생성(); - final var recipe = 레시피_생성(member); - - final var recipeFavorite = RecipeFavorite.create(member, recipe, true); - - // when - recipeFavorite.updateFavorite(false); - - // then - assertSoftly(soft -> { - soft.assertThat(recipeFavorite.getRecipe().getFavoriteCount()) - .isZero(); - soft.assertThat(recipeFavorite.getFavorite()) - .isFalse(); - }); - } - - @Test - void 기존_true_신규_true_경우_recipe의_favoriteCount는_유지() { - // given - final var member = 멤버_멤버1_생성(); - final var recipe = 레시피_생성(member); - - final var recipeFavorite = RecipeFavorite.create(member, recipe, true); - - // when - recipeFavorite.updateFavorite(true); - - // then - assertSoftly(soft -> { - soft.assertThat(recipeFavorite.getRecipe().getFavoriteCount()) - .isOne(); - soft.assertThat(recipeFavorite.getFavorite()) - .isTrue(); - }); - } - - @Test - void 기존_false_신규_false_경우_recipe의_favoriteCount는_유지() { - // given - final var member = 멤버_멤버1_생성(); - final var recipe = 레시피_생성(member); - - final var recipeFavorite = RecipeFavorite.create(member, recipe, false); - - // when - recipeFavorite.updateFavorite(false); - - // then - assertSoftly(soft -> { - soft.assertThat(recipeFavorite.getRecipe().getFavoriteCount()) - .isZero(); - soft.assertThat(recipeFavorite.getFavorite()) - .isFalse(); - }); - } - } -} diff --git a/backend/src/test/java/com/funeat/member/persistence/MemberRepositoryTest.java b/backend/src/test/java/com/funeat/member/persistence/MemberRepositoryTest.java deleted file mode 100644 index 21b890137..000000000 --- a/backend/src/test/java/com/funeat/member/persistence/MemberRepositoryTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.funeat.member.persistence; - -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import com.funeat.common.RepositoryTest; -import java.util.NoSuchElementException; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -public class MemberRepositoryTest extends RepositoryTest { - - @Nested - class findByPlatformId_성공_테스트 { - - @Test - void platformId를_통해_멤버를_반환한다() { - // given - final var platformId = "1"; - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - // when - final var actual = memberRepository.findByPlatformId(platformId).get(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(member); - } - } - - @Nested - class findByPlatformId_실패_테스트 { - - @Test - void platform_id가_잘못된_값으로_멤버를_조회할_때_예외가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var wrongPlatformId = "2"; - - // when & then - assertThatThrownBy(() -> memberRepository.findByPlatformId(wrongPlatformId).get()) - .isInstanceOf(NoSuchElementException.class); - } - } -} diff --git a/backend/src/test/java/com/funeat/member/persistence/RecipeFavoriteRepositoryTest.java b/backend/src/test/java/com/funeat/member/persistence/RecipeFavoriteRepositoryTest.java deleted file mode 100644 index 1d87dd531..000000000 --- a/backend/src/test/java/com/funeat/member/persistence/RecipeFavoriteRepositoryTest.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.funeat.member.persistence; - -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버2_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버3_생성; -import static com.funeat.fixture.ProductFixture.레시피_안에_들어가는_상품_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점2점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격3000원_평점2점_생성; -import static com.funeat.fixture.RecipeFixture.레시피_생성; -import static com.funeat.fixture.RecipeFixture.레시피_좋아요_생성; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import com.funeat.common.DataCleaner; -import com.funeat.common.DataClearExtension; -import com.funeat.common.RepositoryTest; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.context.annotation.Import; - -@DataJpaTest -@Import(DataCleaner.class) -@ExtendWith(DataClearExtension.class) -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(ReplaceUnderscores.class) -class RecipeFavoriteRepositoryTest extends RepositoryTest { - - @Nested - class findByMemberAndRecipe_성공_테스트 { - - @Test - void 해당_사용자의_해당_레시피에_대한_좋아요_현황을_반환할_수_있다() { - // given - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점1점_생성(category); - final var product2 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product3 = 상품_삼각김밥_가격3000원_평점2점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var recipeAuthor = 멤버_멤버1_생성(); - 단일_멤버_저장(recipeAuthor); - - final var recipe = 레시피_생성(recipeAuthor); - 단일_레시피_저장(recipe); - - final var productRecipe1 = 레시피_안에_들어가는_상품_생성(product1, recipe); - final var productRecipe2 = 레시피_안에_들어가는_상품_생성(product2, recipe); - final var productRecipe3 = 레시피_안에_들어가는_상품_생성(product3, recipe); - 복수_레시피_상품_저장(productRecipe1, productRecipe2, productRecipe3); - - final var realMember = 멤버_멤버2_생성(); - final var fakeMember = 멤버_멤버3_생성(); - 복수_멤버_저장(realMember, fakeMember); - - final var recipeFavorite = 레시피_좋아요_생성(realMember, recipe, true); - 레시피_좋아요_저장(recipeFavorite); - - // when - final var realMemberActual = recipeFavoriteRepository.findByMemberAndRecipe(realMember, recipe); - final var fakeMemberActual = recipeFavoriteRepository.findByMemberAndRecipe(fakeMember, recipe); - - // then - assertSoftly(soft -> { - soft.assertThat(realMemberActual) - .isNotEmpty(); - soft.assertThat(fakeMemberActual) - .isEmpty(); - }); - } - } - - @Nested - class existsByMemberAndRecipeAndFavoriteTrue_성공_테스트 { - - @Test - void 해당_사용자가_해당_레시피에_좋아요를_눌렀는지_알_수_있다() { - // given - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점1점_생성(category); - final var product2 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product3 = 상품_삼각김밥_가격3000원_평점2점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var recipeAuthor = 멤버_멤버1_생성(); - 단일_멤버_저장(recipeAuthor); - - final var recipe = 레시피_생성(recipeAuthor); - 단일_레시피_저장(recipe); - - final var productRecipe1 = 레시피_안에_들어가는_상품_생성(product1, recipe); - final var productRecipe2 = 레시피_안에_들어가는_상품_생성(product2, recipe); - final var productRecipe3 = 레시피_안에_들어가는_상품_생성(product3, recipe); - 복수_레시피_상품_저장(productRecipe1, productRecipe2, productRecipe3); - - final var realMember = 멤버_멤버2_생성(); - final var fakeMember = 멤버_멤버3_생성(); - 복수_멤버_저장(realMember, fakeMember); - - final var recipeFavorite = 레시피_좋아요_생성(realMember, recipe, true); - 레시피_좋아요_저장(recipeFavorite); - - // when - final var realMemberActual = recipeFavoriteRepository.existsByMemberAndRecipeAndFavoriteTrue(realMember, - recipe); - final var fakeMemberActual = recipeFavoriteRepository.existsByMemberAndRecipeAndFavoriteTrue(fakeMember, - recipe); - - // then - assertSoftly(soft -> { - soft.assertThat(realMemberActual) - .isTrue(); - soft.assertThat(fakeMemberActual) - .isFalse(); - }); - } - } -} diff --git a/backend/src/test/java/com/funeat/member/persistence/ReviewFavoriteRepositoryTest.java b/backend/src/test/java/com/funeat/member/persistence/ReviewFavoriteRepositoryTest.java deleted file mode 100644 index fcb190d28..000000000 --- a/backend/src/test/java/com/funeat/member/persistence/ReviewFavoriteRepositoryTest.java +++ /dev/null @@ -1,184 +0,0 @@ -package com.funeat.member.persistence; - -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버2_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버3_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점3점_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test4_평점4점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test5_평점5점_재구매O_생성; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import com.funeat.common.RepositoryTest; -import com.funeat.member.domain.favorite.ReviewFavorite; -import java.util.List; -import java.util.NoSuchElementException; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -class ReviewFavoriteRepositoryTest extends RepositoryTest { - - @Nested - class findByMemberAndReview_성공_테스트 { - - @Test - void 멤버와_리뷰로_리뷰_좋아요를_조회할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - 단일_상품_저장(product); - - final var review = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 0L); - 단일_리뷰_저장(review); - - final var reviewFavorite = ReviewFavorite.create(member, review, true); - 단일_리뷰_좋아요_저장(reviewFavorite); - - final var expected = ReviewFavorite.create(member, review, true); - - // when - final var actual = reviewFavoriteRepository.findByMemberAndReview(member, review).get(); - - // then - assertThat(actual).usingRecursiveComparison() - .ignoringExpectedNullFields() - .isEqualTo(expected); - } - } - - @Nested - class findByMemberAndReview_실패_테스트 { - - @Test - void 잘못된_멤버로_좋아요를_조회하면_에러가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - 단일_상품_저장(product); - - final var review = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 0L); - 단일_리뷰_저장(review); - - final var wrongMember = 멤버_멤버2_생성(); - 단일_멤버_저장(wrongMember); - - // when & then - assertThatThrownBy(() -> reviewFavoriteRepository.findByMemberAndReview(wrongMember, review).get()) - .isInstanceOf(NoSuchElementException.class); - } - - @Test - void 잘못된_리뷰로_좋아요를_조회하면_에러가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var anotherMember = 멤버_멤버2_생성(); - 단일_멤버_저장(anotherMember); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - 단일_상품_저장(product); - - final var review = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 0L); - 단일_리뷰_저장(review); - - final var reviewFavorite = ReviewFavorite.create(member, review, true); - 단일_리뷰_좋아요_저장(reviewFavorite); - - final var wrongReview = 리뷰_이미지test5_평점5점_재구매O_생성(member, product, 0L); - 단일_리뷰_저장(wrongReview); - - // when & then - assertThatThrownBy(() -> reviewFavoriteRepository.findByMemberAndReview(member, wrongReview).get()) - .isInstanceOf(NoSuchElementException.class); - } - } - - @Nested - class deleteByReview_성공_테스트 { - - @Test - void 해당_리뷰에_달린_좋아요를_삭제할_수_있다() { - // given - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - final var member3 = 멤버_멤버3_생성(); - 복수_멤버_저장(member1, member2, member3); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - 단일_상품_저장(product); - - final var review1 = 리뷰_이미지test4_평점4점_재구매O_생성(member1, product, 0L); - final var review2 = 리뷰_이미지test5_평점5점_재구매O_생성(member1, product, 0L); - 복수_리뷰_저장(review1, review2); - - final var reviewFavorite1_1 = ReviewFavorite.create(member1, review1, true); - final var reviewFavorite1_2 = ReviewFavorite.create(member2, review1, true); - final var reviewFavorite1_3 = ReviewFavorite.create(member3, review1, true); - final var reviewFavorite2_1 = ReviewFavorite.create(member1, review2, true); - final var reviewFavorite2_2 = ReviewFavorite.create(member2, review2, true); - 복수_리뷰_좋아요_저장(reviewFavorite1_1, reviewFavorite1_2, reviewFavorite1_3, reviewFavorite2_1, reviewFavorite2_2); - - final var expected = List.of(reviewFavorite2_1, reviewFavorite2_2); - - // when - reviewFavoriteRepository.deleteByReview(review1); - - // then - final var remainings = reviewFavoriteRepository.findAll(); - assertThat(remainings).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class findByReview_성공_테스트 { - - @Test - void 리뷰로_해당_리뷰에_달린_좋아요를_조회할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - 단일_상품_저장(product); - - final var review = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 0L); - 단일_리뷰_저장(review); - - final var reviewFavorite = ReviewFavorite.create(member, review, true); - 단일_리뷰_좋아요_저장(reviewFavorite); - - final var expected = List.of(reviewFavorite); - - // when - final var actual = reviewFavoriteRepository.findByReview(review); - - // then - assertThat(actual).usingRecursiveComparison() - .ignoringExpectedNullFields() - .isEqualTo(expected); - } - } -} diff --git a/backend/src/test/java/com/funeat/product/application/ProductServiceTest.java b/backend/src/test/java/com/funeat/product/application/ProductServiceTest.java deleted file mode 100644 index 9f5b88c5b..000000000 --- a/backend/src/test/java/com/funeat/product/application/ProductServiceTest.java +++ /dev/null @@ -1,204 +0,0 @@ -package com.funeat.product.application; - -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버2_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버3_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점2점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점3점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점4점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점5점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점4점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점5점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격4000원_평점4점_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test1_평점1점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test2_평점2점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test3_평점3점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test3_평점3점_재구매X_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test4_평점4점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test4_평점4점_재구매X_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test5_평점5점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test5_평점5점_재구매X_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.common.ServiceTest; -import com.funeat.product.dto.RankingProductDto; -import com.funeat.product.dto.RankingProductsResponse; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -class ProductServiceTest extends ServiceTest { - - @Nested - class getTop3Products_성공_테스트 { - - @Nested - class 상품_개수에_대한_테스트 { - - @Test - void 전체_상품이_하나도_없어도_반환값은_있어야한다() { - // given - final var expected = RankingProductsResponse.toResponse(Collections.emptyList()); - - // when - final var actual = productService.getTop3Products(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 전체_상품이_1개_이상_3개_미만이라도_상품이_나와야한다() { - // given - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점4점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점5점_생성(category); - 복수_상품_저장(product1, product2); - - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var review1_1 = 리뷰_이미지test5_평점5점_재구매O_생성(member, product1, 0L); - final var review1_2 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product1, 0L); - final var review1_3 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product1, 0L); - final var review1_4 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product1, 0L); - final var review2_1 = 리뷰_이미지test5_평점5점_재구매X_생성(member, product2, 0L); - final var review2_2 = 리뷰_이미지test5_평점5점_재구매X_생성(member, product2, 0L); - 복수_리뷰_저장(review1_1, review1_2, review1_3, review1_4, review2_1, review2_2); - - final var rankingProductDto1 = RankingProductDto.toDto(product2); - final var rankingProductDto2 = RankingProductDto.toDto(product1); - final var rankingProductDtos = List.of(rankingProductDto1, rankingProductDto2); - final var expected = RankingProductsResponse.toResponse(rankingProductDtos); - - // when - final var actual = productService.getTop3Products(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 전체_상품_중_랭킹이_높은_상위_3개_상품을_구할_수_있다() { - // given - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점3점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점4점_생성(category); - final var product3 = 상품_삼각김밥_가격1000원_평점5점_생성(category); - final var product4 = 상품_삼각김밥_가격4000원_평점4점_생성(category); - 복수_상품_저장(product1, product2, product3, product4); - - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - final var member3 = 멤버_멤버3_생성(); - 복수_멤버_저장(member1, member2, member3); - - final var review1_1 = 리뷰_이미지test5_평점5점_재구매O_생성(member1, product1, 0L); - final var review1_2 = 리뷰_이미지test3_평점3점_재구매O_생성(member2, product1, 0L); - final var review1_3 = 리뷰_이미지test3_평점3점_재구매O_생성(member3, product1, 0L); - final var review1_4 = 리뷰_이미지test3_평점3점_재구매O_생성(member1, product1, 0L); - final var review2_1 = 리뷰_이미지test4_평점4점_재구매O_생성(member1, product2, 0L); - final var review2_2 = 리뷰_이미지test4_평점4점_재구매O_생성(member1, product2, 0L); - final var review3_1 = 리뷰_이미지test5_평점5점_재구매X_생성(member1, product3, 0L); - final var review4_1 = 리뷰_이미지test4_평점4점_재구매X_생성(member1, product4, 0L); - final var review4_2 = 리뷰_이미지test3_평점3점_재구매X_생성(member1, product4, 0L); - final var review4_3 = 리뷰_이미지test5_평점5점_재구매X_생성(member1, product4, 0L); - 복수_리뷰_저장(review1_1, review1_2, review1_3, review1_4, review2_1, review2_2, review3_1, review4_1, - review4_2, review4_3); - - final var rankingProductDto1 = RankingProductDto.toDto(product3); - final var rankingProductDto2 = RankingProductDto.toDto(product4); - final var rankingProductDto3 = RankingProductDto.toDto(product2); - final var rankingProductDtos = List.of(rankingProductDto1, rankingProductDto2, rankingProductDto3); - final var expected = RankingProductsResponse.toResponse(rankingProductDtos); - - // when - final var actual = productService.getTop3Products(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class 상품_점수에_대한_테스트 { - - @Test - void 모든_상품의_평점이_3점_미만이면_빈_배열을_반환한다() { - // given - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점1점_생성(category); - 복수_상품_저장(product1, product2); - - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - final var member3 = 멤버_멤버3_생성(); - 복수_멤버_저장(member1, member2, member3); - - final var review1_1 = 리뷰_이미지test1_평점1점_재구매O_생성(member1, product1, 0L); - final var review1_2 = 리뷰_이미지test2_평점2점_재구매O_생성(member2, product1, 0L); - final var review1_3 = 리뷰_이미지test3_평점3점_재구매O_생성(member3, product1, 0L); - final var review2_1 = 리뷰_이미지test1_평점1점_재구매O_생성(member1, product2, 0L); - final var review2_2 = 리뷰_이미지test2_평점2점_재구매O_생성(member2, product2, 0L); - 복수_리뷰_저장(review1_1, review1_2, review1_3, review2_1, review2_2); - - final var expected = RankingProductsResponse.toResponse(Collections.emptyList()); - - // when - final var actual = productService.getTop3Products(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 일부_상품이_평점_3점_이상이면_일부_상품만_반환한다() { - // given - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점4점_생성(category); - 복수_상품_저장(product1, product2); - - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - final var member3 = 멤버_멤버3_생성(); - 복수_멤버_저장(member1, member2, member3); - - final var review1_1 = 리뷰_이미지test1_평점1점_재구매O_생성(member1, product1, 0L); - final var review1_2 = 리뷰_이미지test2_평점2점_재구매O_생성(member2, product1, 0L); - final var review1_3 = 리뷰_이미지test3_평점3점_재구매O_생성(member3, product1, 0L); - final var review2_1 = 리뷰_이미지test4_평점4점_재구매X_생성(member3, product2, 0L); - final var review2_2 = 리뷰_이미지test5_평점5점_재구매X_생성(member3, product2, 0L); - 복수_리뷰_저장(review1_1, review1_2, review1_3, review2_1, review2_2); - - final var rankingProductDtos = List.of(RankingProductDto.toDto(product2)); - - final var expected = RankingProductsResponse.toResponse(rankingProductDtos); - - // when - final var actual = productService.getTop3Products(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - } -} diff --git a/backend/src/test/java/com/funeat/product/domain/ProductTest.java b/backend/src/test/java/com/funeat/product/domain/ProductTest.java deleted file mode 100644 index 7e012dd22..000000000 --- a/backend/src/test/java/com/funeat/product/domain/ProductTest.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.funeat.product.domain; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(ReplaceUnderscores.class) -class ProductTest { - - @Nested - class updateAverageRatingForInsert_성공_테스트 { - - @Test - void 평균_평점을_업데이트_할_수_있다() { - // given - final var product = new Product("testName", 1000L, "testImage", "testContent", null); - final var reviewCount = 1L; - final var reviewRating = 4L; - - // when - product.updateAverageRatingForInsert(reviewCount, reviewRating); - final var actual = product.getAverageRating(); - - // then - assertThat(actual).isEqualTo(4.0); - } - - @Test - void 평균_평점을_여러번_업데이트_할_수_있다() { - // given - final var product = new Product("testName", 1000L, "testImage", "testContent", null); - final var reviewCount1 = 1L; - final var reviewCount2 = 2L; - final var reviewRating1 = 4L; - final var reviewRating2 = 2L; - - // when - product.updateAverageRatingForInsert(reviewCount1, reviewRating1); - final var actual1 = product.getAverageRating(); - - product.updateAverageRatingForInsert(reviewCount2, reviewRating2); - final var actual2 = product.getAverageRating(); - - // then - assertSoftly(soft -> { - soft.assertThat(actual1) - .isEqualTo(4.0); - soft.assertThat(actual2) - .isEqualTo(3.0); - }); - } - } - - @Nested - class updateAverageRatingForDelete_성공_테스트 { - - @Test - void 리뷰가_하나인_상품의_리뷰를_삭제하면_평균평점은_0점이_된다() { - // given - final var product = new Product("testName", 1000L, "testImage", "testContent", 4.0, null, 1L); - final var reviewRating = 4L; - - // when - product.updateAverageRatingForDelete(reviewRating); - final var actual = product.getAverageRating(); - - // then - assertThat(actual).isEqualTo(0.0); - } - - @Test - void 리뷰가_여러개인_상품의_리뷰를_삭제하면_평균평점이_갱신된다() { - // given - final var product = new Product("testName", 1000L, "testImage", "testContent", 4.0, null, 4L); - final var reviewRating = 5L; - - // when - product.updateAverageRatingForDelete(reviewRating); - final var actual = product.getAverageRating(); - - // then - assertThat(actual).isEqualTo(3.7); - } - } - - @Nested - class calculateRankingScore_성공_테스트 { - - @Test - void 평균_평점과_리뷰_수로_해당_상품의_랭킹_점수를_구할_수_있다() { - // given - final var product = new Product("testName", 1000L, "testImage", "testContent", 4.0, null); - final var reviewCount = 9L; - - // when - final var rankingScore = product.calculateRankingScore(reviewCount); - - // then - assertThat(rankingScore).isEqualTo(3.5); - } - } - - @Nested - class calculateRankingScore_실패_테스트 { - - @Test - void 리뷰_수가_마이너스_일로_나온다면_무한대로_나와서_예외가_나와야하는데_통과하고_있다() { - // given - final var product = new Product("testName", 1000L, "testImage", "testContent", 4.0, null); - final var reviewCount = -1L; - - // when - final var rankingScore = product.calculateRankingScore(reviewCount); - - // then - assertThat(rankingScore).isInfinite(); - } - } -} diff --git a/backend/src/test/java/com/funeat/product/domain/favorite/ReviewFavoriteTest.java b/backend/src/test/java/com/funeat/product/domain/favorite/ReviewFavoriteTest.java deleted file mode 100644 index e38e256d6..000000000 --- a/backend/src/test/java/com/funeat/product/domain/favorite/ReviewFavoriteTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.funeat.product.domain.favorite; - -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점1점_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test1_평점1점_재구매O_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.member.domain.favorite.ReviewFavorite; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(ReplaceUnderscores.class) -public class ReviewFavoriteTest { - - @Nested - class updateChecked_성공_테스트 { - - @Test - void 좋아요가_있는_상태에서_좋아요_취소한다() { - // given - final var category = 카테고리_즉석조리_생성(); - final var product = 상품_삼각김밥_가격1000원_평점1점_생성(category); - - final var member = 멤버_멤버1_생성(); - final var review = 리뷰_이미지test1_평점1점_재구매O_생성(member, product, 0L); - final var reviewFavorite = ReviewFavorite.create(member, review, true); - - // when - reviewFavorite.updateChecked(false); - final var actual = reviewFavorite.getFavorite(); - - // then - assertThat(actual).isFalse(); - } - - @Test - void 좋아요가_없는_상태에서_좋아요를_적용한다() { - // given - final var category = 카테고리_즉석조리_생성(); - final var product = 상품_삼각김밥_가격1000원_평점1점_생성(category); - - final var member = 멤버_멤버1_생성(); - final var review = 리뷰_이미지test1_평점1점_재구매O_생성(member, product, 0L); - final var reviewFavorite = ReviewFavorite.create(member, review, false); - - // when - reviewFavorite.updateChecked(true); - final var actual = reviewFavorite.getFavorite(); - - // then - assertThat(actual).isTrue(); - } - } - - @Nested - class updateChecked_실패_테스트 { - - @Test - void 좋아요가_있는_상태에서_좋아요를_적용하면_예외가_발생해야하는데_통과하고_있다() { - // given - final var category = 카테고리_즉석조리_생성(); - final var product = 상품_삼각김밥_가격1000원_평점1점_생성(category); - - final var member = 멤버_멤버1_생성(); - final var review = 리뷰_이미지test1_평점1점_재구매O_생성(member, product, 0L); - final var reviewFavorite = ReviewFavorite.create(member, review, true); - - // when - reviewFavorite.updateChecked(true); - final var actual = reviewFavorite.getFavorite(); - - // then - assertThat(actual).isTrue(); - } - - @Test - void 좋아요가_없는_상태에서_좋아요를_취소하면_예외가_발생해야하는데_통과하고_있다() { - // given - final var category = 카테고리_즉석조리_생성(); - final var product = 상품_삼각김밥_가격1000원_평점1점_생성(category); - - final var member = 멤버_멤버1_생성(); - final var review = 리뷰_이미지test1_평점1점_재구매O_생성(member, product, 0L); - final var reviewFavorite = ReviewFavorite.create(member, review, false); - - // when - reviewFavorite.updateChecked(false); - final var actual = reviewFavorite.getFavorite(); - - // then - assertThat(actual).isFalse(); - } - } -} diff --git a/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java b/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java deleted file mode 100644 index 5782fd8c9..000000000 --- a/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.funeat.product.persistence; - -import static com.funeat.fixture.CategoryFixture.카테고리_CU_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_EMART24_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_GS25_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_과자류_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_세븐일레븐_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_식품_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_아이스크림_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_음료_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.common.RepositoryTest; -import com.funeat.product.domain.CategoryType; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -public class CategoryRepositoryTest extends RepositoryTest { - - @Nested - class findAllByType_성공_테스트 { - - @Test - void 카테고리_타입이_FOOD인_모든_카테고리를_조회한다() { - // given - final var 간편식사 = 카테고리_간편식사_생성(); - final var 즉석조리 = 카테고리_즉석조리_생성(); - final var 과자류 = 카테고리_과자류_생성(); - final var 아이스크림 = 카테고리_아이스크림_생성(); - final var 식품 = 카테고리_식품_생성(); - final var 음료 = 카테고리_음료_생성(); - final var CU = 카테고리_CU_생성(); - final var GS25 = 카테고리_GS25_생성(); - final var EMART24 = 카테고리_EMART24_생성(); - final var 세븐일레븐 = 카테고리_세븐일레븐_생성(); - 복수_카테고리_저장(간편식사, 즉석조리, 과자류, 아이스크림, 식품, 음료, CU, GS25, EMART24, 세븐일레븐); - - final var expected = List.of(간편식사, 즉석조리, 과자류, 아이스크림, 식품, 음료); - - // when - final var actual = categoryRepository.findAllByType(CategoryType.FOOD); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 카테고리_타입이_STORE인_모든_카테고리를_조회한다() { - // given - final var 간편식사 = 카테고리_간편식사_생성(); - final var 즉석조리 = 카테고리_즉석조리_생성(); - final var 과자류 = 카테고리_과자류_생성(); - final var 아이스크림 = 카테고리_아이스크림_생성(); - final var 식품 = 카테고리_식품_생성(); - final var 음료 = 카테고리_음료_생성(); - final var CU = 카테고리_CU_생성(); - final var GS25 = 카테고리_GS25_생성(); - final var EMART24 = 카테고리_EMART24_생성(); - final var 세븐일레븐 = 카테고리_세븐일레븐_생성(); - 복수_카테고리_저장(간편식사, 즉석조리, 과자류, 아이스크림, 식품, 음료, CU, GS25, EMART24, 세븐일레븐); - - final var expected = List.of(CU, GS25, EMART24, 세븐일레븐); - - // when - final var actual = categoryRepository.findAllByType(CategoryType.STORE); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } -} diff --git a/backend/src/test/java/com/funeat/product/persistence/ProductRecipeRepositoryTest.java b/backend/src/test/java/com/funeat/product/persistence/ProductRecipeRepositoryTest.java deleted file mode 100644 index 612edad15..000000000 --- a/backend/src/test/java/com/funeat/product/persistence/ProductRecipeRepositoryTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.funeat.product.persistence; - -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.ProductFixture.레시피_안에_들어가는_상품_생성; -import static com.funeat.fixture.ProductFixture.상품_망고빙수_가격5000원_평점4점_생성; -import static com.funeat.fixture.ProductFixture.상품_애플망고_가격3000원_평점5점_생성; -import static com.funeat.fixture.RecipeFixture.레시피_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.common.RepositoryTest; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -class ProductRecipeRepositoryTest extends RepositoryTest { - - @Nested - class findProductByRecipe_성공_테스트 { - - @Test - void 레시피에_사용된_상품들을_조회할_수_있다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_애플망고_가격3000원_평점5점_생성(category); - final var product2 = 상품_망고빙수_가격5000원_평점4점_생성(category); - 복수_상품_저장(product1, product2); - - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var recipe = 레시피_생성(member, 1L); - 단일_꿀조합_저장(recipe); - - final var product_recipe_1 = 레시피_안에_들어가는_상품_생성(product1, recipe); - final var product_recipe_2 = 레시피_안에_들어가는_상품_생성(product2, recipe); - 복수_꿀조합_상품_저장(product_recipe_1, product_recipe_2); - - final var expected = List.of(product1, product2); - - // when - final var actual = productRecipeRepository.findProductByRecipe(recipe); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } -} diff --git a/backend/src/test/java/com/funeat/product/persistence/ProductRepositoryTest.java b/backend/src/test/java/com/funeat/product/persistence/ProductRepositoryTest.java deleted file mode 100644 index 107585475..000000000 --- a/backend/src/test/java/com/funeat/product/persistence/ProductRepositoryTest.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.funeat.product.persistence; - -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버2_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버3_생성; -import static com.funeat.fixture.PageFixture.페이지요청_기본_생성; -import static com.funeat.fixture.ProductFixture.상품_망고빙수_가격5000원_평점4점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점3점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점4점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격3000원_평점5점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격4000원_평점2점_생성; -import static com.funeat.fixture.ProductFixture.상품_애플망고_가격3000원_평점5점_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test1_평점1점_재구매X_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test3_평점3점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test5_평점5점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test5_평점5점_재구매X_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.common.RepositoryTest; -import com.funeat.product.dto.ProductReviewCountDto; -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -class ProductRepositoryTest extends RepositoryTest { - - @Nested - class findAllByAverageRatingGreaterThan3_성공_테스트 { - - @Test - void 평점이_3보다_큰_모든_상품들과_리뷰_수를_조회한다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점3점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점4점_생성(category); - final var product3 = 상품_삼각김밥_가격3000원_평점5점_생성(category); - final var product4 = 상품_삼각김밥_가격4000원_평점2점_생성(category); - 복수_상품_저장(product1, product2, product3, product4); - - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - final var member3 = 멤버_멤버3_생성(); - 복수_멤버_저장(member1, member2, member3); - - final var review1_1 = 리뷰_이미지test5_평점5점_재구매X_생성(member1, product1, 0L, LocalDateTime.now().minusDays(2L)); - final var review1_2 = 리뷰_이미지test5_평점5점_재구매X_생성(member2, product1, 0L, LocalDateTime.now().minusDays(3L)); - final var review2_1 = 리뷰_이미지test5_평점5점_재구매X_생성(member3, product2, 0L, LocalDateTime.now().minusDays(10L)); - final var review2_2 = 리뷰_이미지test5_평점5점_재구매X_생성(member1, product2, 0L, LocalDateTime.now().minusDays(1L)); - final var review2_3 = 리뷰_이미지test5_평점5점_재구매X_생성(member2, product2, 0L, LocalDateTime.now().minusDays(9L)); - final var review3_1 = 리뷰_이미지test5_평점5점_재구매X_생성(member1, product3, 0L, LocalDateTime.now().minusDays(8L)); - 복수_리뷰_저장(review1_1, review1_2, review2_1, review2_2, review2_3, review3_1); - - final var productReviewCountDto1 = new ProductReviewCountDto(product2, 3L); - final var productReviewCountDto2 = new ProductReviewCountDto(product3, 1L); - final var expected = List.of(productReviewCountDto1, productReviewCountDto2); - - // when - final var startDateTime = LocalDateTime.now().minusWeeks(2L); - final var endDateTime = LocalDateTime.now(); - final var actual = productRepository.findAllByAverageRatingGreaterThan3(startDateTime, endDateTime); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 기간_안에_리뷰가_존재하는_상품이_없으면_빈_리스트를_반환한다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점3점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점4점_생성(category); - 복수_상품_저장(product1, product2); - - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - 복수_멤버_저장(member1, member2); - - final var review1 = 리뷰_이미지test5_평점5점_재구매X_생성(member1, product1, 0L, LocalDateTime.now().minusDays(15L)); - final var review2 = 리뷰_이미지test5_평점5점_재구매X_생성(member2, product2, 0L, LocalDateTime.now().minusWeeks(3L)); - 복수_리뷰_저장(review1, review2); - - final var expected = Collections.emptyList(); - - // when - final var startDateTime = LocalDateTime.now().minusWeeks(2L); - final var endDateTime = LocalDateTime.now(); - final var actual = productRepository.findAllByAverageRatingGreaterThan3(startDateTime, endDateTime); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class findAllByNameContaining_성공_테스트 { - - @Test - void 상품명에_검색어가_포함된_상품들을_조회한다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_애플망고_가격3000원_평점5점_생성(category); - final var product2 = 상품_망고빙수_가격5000원_평점4점_생성(category); - 복수_상품_저장(product1, product2); - - final var page = 페이지요청_기본_생성(0, 10); - - final var expected = List.of(product2, product1); - - // when - final var actual = productRepository.findAllByNameContaining("망고", page).getContent(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class findAllWithReviewCountByNameContaining_성공_테스트 { - - @Test - void 상품명에_검색어가_포함된_상품들과_리뷰_수를_조회한다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_애플망고_가격3000원_평점5점_생성(category); - final var product2 = 상품_망고빙수_가격5000원_평점4점_생성(category); - 복수_상품_저장(product1, product2); - - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - 복수_멤버_저장(member1, member2); - - final var review1_1 = 리뷰_이미지test1_평점1점_재구매X_생성(member1, product1, 0L); - final var review1_2 = 리뷰_이미지test5_평점5점_재구매O_생성(member2, product1, 0L); - final var review2_1 = 리뷰_이미지test3_평점3점_재구매O_생성(member1, product2, 0L); - 복수_리뷰_저장(review1_1, review1_2, review2_1); - - final var page = 페이지요청_기본_생성(0, 10); - - final var expectedDto1 = new ProductReviewCountDto(product1, 2L); - final var expectedDto2 = new ProductReviewCountDto(product2, 1L); - final var expected = List.of(expectedDto2, expectedDto1); - - // when - final var actual = productRepository.findAllWithReviewCountByNameContaining("망고", page).getContent(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } -} diff --git a/backend/src/test/java/com/funeat/recipe/application/RecipeServiceTest.java b/backend/src/test/java/com/funeat/recipe/application/RecipeServiceTest.java deleted file mode 100644 index 81badf4f6..000000000 --- a/backend/src/test/java/com/funeat/recipe/application/RecipeServiceTest.java +++ /dev/null @@ -1,913 +0,0 @@ -package com.funeat.recipe.application; - -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static com.funeat.fixture.ImageFixture.여러_이미지_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버2_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버3_생성; -import static com.funeat.fixture.PageFixture.과거순; -import static com.funeat.fixture.PageFixture.좋아요수_내림차순; -import static com.funeat.fixture.PageFixture.최신순; -import static com.funeat.fixture.PageFixture.페이지요청_생성; -import static com.funeat.fixture.ProductFixture.레시피_안에_들어가는_상품_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점2점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점5점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점3점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격3000원_평점4점_생성; -import static com.funeat.fixture.RecipeFixture.레시피_생성; -import static com.funeat.fixture.RecipeFixture.레시피이미지_생성; -import static com.funeat.fixture.RecipeFixture.레시피좋아요요청_생성; -import static com.funeat.fixture.RecipeFixture.레시피추가요청_생성; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import com.funeat.comment.domain.Comment; -import com.funeat.common.ServiceTest; -import com.funeat.common.dto.PageDto; -import com.funeat.member.domain.Member; -import com.funeat.member.dto.MemberRecipeDto; -import com.funeat.member.dto.MemberRecipeProductDto; -import com.funeat.member.dto.MemberRecipesResponse; -import com.funeat.member.exception.MemberException.MemberNotFoundException; -import com.funeat.product.domain.Category; -import com.funeat.product.domain.CategoryType; -import com.funeat.product.domain.Product; -import com.funeat.product.exception.ProductException.ProductNotFoundException; -import com.funeat.recipe.dto.RankingRecipeDto; -import com.funeat.recipe.dto.RankingRecipesResponse; -import com.funeat.recipe.dto.RecipeAuthorDto; -import com.funeat.recipe.dto.RecipeCommentCondition; -import com.funeat.recipe.dto.RecipeCommentCreateRequest; -import com.funeat.recipe.dto.RecipeCommentResponse; -import com.funeat.recipe.dto.RecipeCreateRequest; -import com.funeat.recipe.dto.RecipeDetailResponse; -import com.funeat.recipe.dto.RecipeDto; -import com.funeat.recipe.exception.RecipeException.RecipeNotFoundException; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -class RecipeServiceTest extends ServiceTest { - - @Nested - class create_성공_테스트 { - - @Test - void 레시피를_추가할_수_있다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격3000원_평점4점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - 복수_상품_저장(product1, product2, product3); - final var productIds = 상품_아이디_변환(product1, product2, product3); - - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var images = 여러_이미지_생성(3); - - // when - final var request = 레시피추가요청_생성(productIds); - - final var expected = 레시피_생성(member); - - // when - final var recipeId = recipeService.create(memberId, images, request); - final var actual = recipeRepository.findById(recipeId).get(); - - // then - assertThat(actual).usingRecursiveComparison() - .ignoringFields("id", "createdAt") - .isEqualTo(expected); - assertThat(actual.getCreatedAt()).isNotNull(); - } - } - - @Test - void 레시피의_상세_정보를_조회할_수_있다() { - // given - final var category = 카테고리_추가_요청(new Category("간편식사", CategoryType.FOOD, "siksa.jpeg")); - final var product1 = new Product("불닭볶음면", 1000L, "image.png", "엄청 매운 불닭", category); - final var product2 = new Product("참치 삼김", 2000L, "image.png", "담백한 참치마요 삼김", category); - final var product3 = new Product("스트링 치즈", 1500L, "image.png", "고소한 치즈", category); - 복수_상품_저장(product1, product2, product3); - final var products = List.of(product1, product2, product3); - final var author = new Member("author", "image.png", "1"); - 단일_멤버_저장(author); - final var authorId = author.getId(); - - final var images = 여러_이미지_생성(3); - - final var productIds = List.of(product1.getId(), product2.getId(), product3.getId()); - final var request = new RecipeCreateRequest("제일로 맛있는 레시피", productIds, - "우선 밥을 넣어요. 그리고 밥을 또 넣어요. 그리고 밥을 또 넣으면.. 끝!!"); - - final var recipeId = recipeService.create(authorId, images, request); - - // when - final var actual = recipeService.getRecipeDetail(authorId, recipeId); - - // then - final var recipe = recipeRepository.findById(recipeId).get(); - final var expected = RecipeDetailResponse.toResponse( - recipe, recipeImageRepository.findByRecipe(recipe), - products, 4500L, false); - - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Nested - class create_실패_테스트 { - - @Test - void 존재하지_않는_멤버가_레시피를_추가하면_예외가_발생한다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격3000원_평점4점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - 복수_상품_저장(product1, product2, product3); - final var productIds = 상품_아이디_변환(product1, product2, product3); - - final var member = 멤버_멤버1_생성(); - final var wrongMemberId = 단일_멤버_저장(member) + 1L; - - final var images = 여러_이미지_생성(3); - - final var request = 레시피추가요청_생성(productIds); - - // when & then - assertThatThrownBy(() -> recipeService.create(wrongMemberId, images, request)) - .isInstanceOf(MemberNotFoundException.class); - } - - @Test - void 존재하지_않는_상품을_레시피에_추가하면_예외가_발생한다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격3000원_평점4점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - 복수_상품_저장(product1, product2, product3); - final var wrongProductIds = 상품_아이디_변환(product1, product2, product3); - wrongProductIds.add(4L); - - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var images = 여러_이미지_생성(3); - - final var request = 레시피추가요청_생성(wrongProductIds); - - // when & then - assertThatThrownBy(() -> recipeService.create(memberId, images, request)) - .isInstanceOf(ProductNotFoundException.class); - } - } - - @Nested - class findRecipeByMember_성공_테스트 { - - @Test - void 사용자가_작성한_꿀조합을_조회한다() { - // given - final var member1 = 멤버_멤버1_생성(); - 복수_멤버_저장(member1); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점5점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점1점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var recipe1_1 = 레시피_생성(member1); - final var recipe1_2 = 레시피_생성(member1); - 복수_꿀조합_저장(recipe1_1, recipe1_2); - - final var product_recipe_1_1_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_1); - final var product_recipe_1_1_2 = 레시피_안에_들어가는_상품_생성(product2, recipe1_1); - final var product_recipe_1_1_3 = 레시피_안에_들어가는_상품_생성(product3, recipe1_1); - final var product_recipe_1_2_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_2); - final var product_recipe_1_2_2 = 레시피_안에_들어가는_상품_생성(product3, recipe1_2); - 복수_꿀조합_상품_저장(product_recipe_1_1_1, product_recipe_1_1_2, product_recipe_1_1_3, product_recipe_1_2_1, - product_recipe_1_2_2); - - final var recipeImage1_1 = 레시피이미지_생성(recipe1_1); - final var recipeImage1_2 = 레시피이미지_생성(recipe1_2); - 복수_꿀조합_이미지_저장(recipeImage1_1, recipeImage1_2); - - final var page = 페이지요청_생성(0, 10, 최신순); - - // when - final var actual = recipeService.findRecipeByMember(member1.getId(), page); - - // then - final var expectedRecipes = List.of(recipe1_2, recipe1_1); - final var expectedRecipesDtos = expectedRecipes.stream() - .map(recipe -> { - final var findRecipeImages = recipeImageRepository.findByRecipe(recipe); - final var productsByRecipe = productRecipeRepository.findProductByRecipe(recipe); - final var memberRecipeProductDtos = productsByRecipe.stream() - .map(MemberRecipeProductDto::toDto) - .collect(Collectors.toList()); - return MemberRecipeDto.toDto(recipe, findRecipeImages, memberRecipeProductDtos); - }) - .collect(Collectors.toList()); - final var expectedPage = new PageDto(2L, 1L, true, true, 0L, 10L); - - 해당멤버의_꿀조합과_페이징_결과를_검증한다(actual, expectedRecipesDtos, expectedPage); - } - - @Test - void 사용자가_작성한_꿀조합이_없을때_꿀조합은_빈상태로_조회된다() { - // given - final var member1 = 멤버_멤버1_생성(); - 단일_멤버_저장(member1); - - final var page = 페이지요청_생성(0, 10, 최신순); - - // when - final var actual = recipeService.findRecipeByMember(member1.getId(), page); - - // then - final var expectedRecipes = Collections.emptyList(); - final var expectedPage = new PageDto(0L, 0L, true, true, 0L, 10L); - - 해당멤버의_꿀조합과_페이징_결과를_검증한다(actual, expectedRecipes, expectedPage); - } - } - - @Nested - class findRecipeByMember_실패_테스트 { - - @Test - void 존재하지_않는_멤버가_해당_멤버의_레시피를_조회하면_예외가_발생한다() { - // given - final var notExistMemberId = 99999L; - final var page = 페이지요청_생성(0, 10, 최신순); - - // when & then - assertThatThrownBy(() -> recipeService.findRecipeByMember(notExistMemberId, page)) - .isInstanceOf(MemberNotFoundException.class); - } - } - - @Nested - class getSortingRecipes_성공_테스트 { - - @Test - void 꿀조합을_좋아요가_많은_순으로_정렬할_수_있다() { - // given - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - final var member3 = 멤버_멤버3_생성(); - 복수_멤버_저장(member1, member2, member3); - - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점5점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점1점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var recipe1_1 = 레시피_생성(member1, 1L); - final var recipe1_2 = 레시피_생성(member1, 3L); - final var recipe1_3 = 레시피_생성(member1, 2L); - 복수_꿀조합_저장(recipe1_1, recipe1_2, recipe1_3); - - final var product_recipe_1_1_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_1); - final var product_recipe_1_1_2 = 레시피_안에_들어가는_상품_생성(product2, recipe1_1); - final var product_recipe_1_1_3 = 레시피_안에_들어가는_상품_생성(product3, recipe1_1); - final var product_recipe_1_2_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_2); - final var product_recipe_1_2_2 = 레시피_안에_들어가는_상품_생성(product3, recipe1_2); - 복수_꿀조합_상품_저장(product_recipe_1_1_1, product_recipe_1_1_2, product_recipe_1_1_3, product_recipe_1_2_1, - product_recipe_1_2_2); - - final var recipeImage1_1_1 = 레시피이미지_생성(recipe1_1); - final var recipeImage1_2_1 = 레시피이미지_생성(recipe1_2); - final var recipeImage1_2_2 = 레시피이미지_생성(recipe1_2); - 복수_꿀조합_이미지_저장(recipeImage1_1_1, recipeImage1_2_1); - - final var page = 페이지요청_생성(0, 10, 좋아요수_내림차순); - - // when - final var actual = recipeService.getSortingRecipes(page).getRecipes(); - final var expected = List.of( - RecipeDto.toDto(recipe1_2, List.of(recipeImage1_2_1, recipeImage1_2_2), - List.of(product1, product3)), - RecipeDto.toDto(recipe1_3, List.of(), List.of()), - RecipeDto.toDto(recipe1_1, List.of(recipeImage1_1_1), List.of(product1, product2, product3))); - - // then - assertThat(actual) - .usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 꿀조합을_최신순으로_정렬할_수_있다() throws InterruptedException { - // given - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - final var member3 = 멤버_멤버3_생성(); - 복수_멤버_저장(member1, member2, member3); - - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점5점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점1점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var recipe1_1 = 레시피_생성(member1, 1L); - Thread.sleep(100); - final var recipe1_2 = 레시피_생성(member1, 3L); - Thread.sleep(100); - final var recipe1_3 = 레시피_생성(member1, 2L); - 복수_꿀조합_저장(recipe1_1, recipe1_2, recipe1_3); - - final var product_recipe_1_1_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_1); - final var product_recipe_1_1_2 = 레시피_안에_들어가는_상품_생성(product2, recipe1_1); - final var product_recipe_1_1_3 = 레시피_안에_들어가는_상품_생성(product3, recipe1_1); - final var product_recipe_1_2_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_2); - final var product_recipe_1_2_2 = 레시피_안에_들어가는_상품_생성(product3, recipe1_2); - 복수_꿀조합_상품_저장(product_recipe_1_1_1, product_recipe_1_1_2, product_recipe_1_1_3, product_recipe_1_2_1, - product_recipe_1_2_2); - - final var recipeImage1_1_1 = 레시피이미지_생성(recipe1_1); - final var recipeImage1_2_1 = 레시피이미지_생성(recipe1_2); - final var recipeImage1_2_2 = 레시피이미지_생성(recipe1_2); - 복수_꿀조합_이미지_저장(recipeImage1_1_1, recipeImage1_2_1); - - final var page = 페이지요청_생성(0, 10, 최신순); - - // when - final var actual = recipeService.getSortingRecipes(page).getRecipes(); - final var expected = List.of( - RecipeDto.toDto(recipe1_3, List.of(), List.of()), - RecipeDto.toDto(recipe1_2, List.of(recipeImage1_2_1, recipeImage1_2_2), - List.of(product1, product3)), - RecipeDto.toDto(recipe1_1, List.of(recipeImage1_1_1), List.of(product1, product2, product3))); - - // then - assertThat(actual) - .usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 꿀조합을_오래된순으로_정렬할_수_있다() { - // given - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - final var member3 = 멤버_멤버3_생성(); - 복수_멤버_저장(member1, member2, member3); - - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점5점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점1점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var recipe1_1 = 레시피_생성(member1, 1L); - final var recipe1_2 = 레시피_생성(member1, 3L); - final var recipe1_3 = 레시피_생성(member1, 2L); - 복수_꿀조합_저장(recipe1_1, recipe1_2, recipe1_3); - - final var product_recipe_1_1_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_1); - final var product_recipe_1_1_2 = 레시피_안에_들어가는_상품_생성(product2, recipe1_1); - final var product_recipe_1_1_3 = 레시피_안에_들어가는_상품_생성(product3, recipe1_1); - final var product_recipe_1_2_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_2); - final var product_recipe_1_2_2 = 레시피_안에_들어가는_상품_생성(product3, recipe1_2); - 복수_꿀조합_상품_저장(product_recipe_1_1_1, product_recipe_1_1_2, product_recipe_1_1_3, product_recipe_1_2_1, - product_recipe_1_2_2); - - final var recipeImage1_1_1 = 레시피이미지_생성(recipe1_1); - final var recipeImage1_2_1 = 레시피이미지_생성(recipe1_2); - final var recipeImage1_2_2 = 레시피이미지_생성(recipe1_2); - 복수_꿀조합_이미지_저장(recipeImage1_1_1, recipeImage1_2_1); - - final var page = 페이지요청_생성(0, 10, 과거순); - - // when - final var actual = recipeService.getSortingRecipes(page).getRecipes(); - final var expected = List.of( - RecipeDto.toDto(recipe1_1, List.of(recipeImage1_1_1), List.of(product1, product2, product3)), - RecipeDto.toDto(recipe1_2, List.of(recipeImage1_2_1, recipeImage1_2_2), - List.of(product1, product3)), - RecipeDto.toDto(recipe1_3, List.of(), List.of())); - - // then - assertThat(actual) - .usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class likeRecipe_성공_테스트 { - - @Test - void 꿀조합에_좋아요를_할_수_있다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격3000원_평점4점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - 복수_상품_저장(product1, product2, product3); - final var productIds = 상품_아이디_변환(product1, product2, product3); - - final var author = 멤버_멤버1_생성(); - final var authorId = 단일_멤버_저장(author); - final var member = 멤버_멤버2_생성(); - final var memberId = 단일_멤버_저장(member); - - final var images = 여러_이미지_생성(3); - - final var createRequest = 레시피추가요청_생성(productIds); - final var recipeId = recipeService.create(authorId, images, createRequest); - - // when - final var favoriteRequest = 레시피좋아요요청_생성(true); - recipeService.likeRecipe(memberId, recipeId, favoriteRequest); - - final var actualRecipe = recipeRepository.findById(recipeId).get(); - final var actualRecipeFavorite = recipeFavoriteRepository.findByMemberAndRecipe(member, actualRecipe).get(); - - // then - assertSoftly(soft -> { - soft.assertThat(actualRecipe.getFavoriteCount()) - .isOne(); - soft.assertThat(actualRecipeFavorite.getFavorite()) - .isTrue(); - }); - } - - @Test - void 꿀조합에_좋아요를_취소_할_수_있다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격3000원_평점4점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - 복수_상품_저장(product1, product2, product3); - final var productIds = 상품_아이디_변환(product1, product2, product3); - - final var author = 멤버_멤버1_생성(); - final var authorId = 단일_멤버_저장(author); - final var member = 멤버_멤버2_생성(); - final var memberId = 단일_멤버_저장(member); - - final var images = 여러_이미지_생성(3); - - final var createRequest = 레시피추가요청_생성(productIds); - final var recipeId = recipeService.create(authorId, images, createRequest); - - final var favoriteRequest = 레시피좋아요요청_생성(true); - recipeService.likeRecipe(memberId, recipeId, favoriteRequest); - - // when - final var cancelFavoriteRequest = 레시피좋아요요청_생성(false); - recipeService.likeRecipe(memberId, recipeId, cancelFavoriteRequest); - - final var actualRecipe = recipeRepository.findById(recipeId).get(); - final var actualRecipeFavorite = recipeFavoriteRepository.findByMemberAndRecipe(member, actualRecipe).get(); - - // then - assertSoftly(soft -> { - soft.assertThat(actualRecipe.getFavoriteCount()) - .isZero(); - soft.assertThat(actualRecipeFavorite.getFavorite()) - .isFalse(); - }); - } - } - - @Nested - class likeRecipe_실패_테스트 { - - @Test - void 존재하지_않는_멤버가_레시피에_좋아요를_하면_예외가_발생한다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격3000원_평점4점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - 복수_상품_저장(product1, product2, product3); - final var productIds = 상품_아이디_변환(product1, product2, product3); - - final var author = 멤버_멤버1_생성(); - final var authorId = 단일_멤버_저장(author); - final var wrongMemberId = authorId + 1L; - - final var images = 여러_이미지_생성(3); - - final var createRequest = 레시피추가요청_생성(productIds); - final var recipeId = recipeService.create(authorId, images, createRequest); - - // when & then - final var favoriteRequest = 레시피좋아요요청_생성(true); - assertThatThrownBy(() -> recipeService.likeRecipe(wrongMemberId, recipeId, favoriteRequest)) - .isInstanceOf(MemberNotFoundException.class); - } - - @Test - void 멤버가_존재하지_않는_레시피에_좋아요를_하면_예외가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var wrongRecipeId = 999L; - - // when & then - final var favoriteRequest = 레시피좋아요요청_생성(true); - assertThatThrownBy(() -> recipeService.likeRecipe(memberId, wrongRecipeId, favoriteRequest)) - .isInstanceOf(RecipeNotFoundException.class); - } - } - - @Nested - class getTop3Recipes_성공_테스트 { - - @Nested - class 꿀조합_개수에_대한_테스트 { - - @Test - void 전체_꿀조합이_하나도_없어도_반환값은_있어야한다() { - // given - final var expected = RankingRecipesResponse.toResponse(Collections.emptyList()); - - // when - final var actual = recipeService.getTop3Recipes(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 랭킹_조건에_부합하는_꿀조합이_1개면_꿀조합이_1개_반환된다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var now = LocalDateTime.now(); - final var recipe = 레시피_생성(member, 2L, now); - 단일_꿀조합_저장(recipe); - - final var author = RecipeAuthorDto.toDto(member); - final var rankingRecipeDto = RankingRecipeDto.toDto(recipe, Collections.emptyList(), author); - final var rankingRecipesDtos = Collections.singletonList(rankingRecipeDto); - final var expected = RankingRecipesResponse.toResponse(rankingRecipesDtos); - - // when - final var actual = recipeService.getTop3Recipes(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 랭킹_조건에_부합하는_꿀조합이_2개면_꿀조합이_2개_반환된다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var now = LocalDateTime.now(); - final var recipe1 = 레시피_생성(member, 2L, now.minusDays(1L)); - final var recipe2 = 레시피_생성(member, 2L, now); - 복수_꿀조합_저장(recipe1, recipe2); - - final var author = RecipeAuthorDto.toDto(member); - final var rankingRecipeDto1 = RankingRecipeDto.toDto(recipe1, Collections.emptyList(), author); - final var rankingRecipeDto2 = RankingRecipeDto.toDto(recipe2, Collections.emptyList(), author); - final var rankingRecipesDtos = List.of(rankingRecipeDto2, rankingRecipeDto1); - final var expected = RankingRecipesResponse.toResponse(rankingRecipesDtos); - - // when - final var actual = recipeService.getTop3Recipes(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 전체_꿀조합_중_랭킹이_높은_상위_3개_꿀조합을_구할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var now = LocalDateTime.now(); - final var recipe1 = 레시피_생성(member, 4L, now.minusDays(10L)); - final var recipe2 = 레시피_생성(member, 6L, now.minusDays(10L)); - final var recipe3 = 레시피_생성(member, 5L, now); - final var recipe4 = 레시피_생성(member, 6L, now); - 복수_꿀조합_저장(recipe1, recipe2, recipe3, recipe4); - - final var author = RecipeAuthorDto.toDto(member); - final var rankingRecipeDto1 = RankingRecipeDto.toDto(recipe1, Collections.emptyList(), author); - final var rankingRecipeDto2 = RankingRecipeDto.toDto(recipe2, Collections.emptyList(), author); - final var rankingRecipeDto3 = RankingRecipeDto.toDto(recipe3, Collections.emptyList(), author); - final var rankingRecipeDto4 = RankingRecipeDto.toDto(recipe4, Collections.emptyList(), author); - final var rankingRecipesDtos = List.of(rankingRecipeDto4, rankingRecipeDto3, rankingRecipeDto2); - final var expected = RankingRecipesResponse.toResponse(rankingRecipesDtos); - - // when - final var actual = recipeService.getTop3Recipes(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class 꿀조합_랭킹_점수에_대한_테스트 { - - @Test - void 꿀조합_좋아요_수가_같으면_최근_생성된_꿀조합의_랭킹을_더_높게_반환한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var now = LocalDateTime.now(); - final var recipe1 = 레시피_생성(member, 10L, now.minusDays(9L)); - final var recipe2 = 레시피_생성(member, 10L, now.minusDays(4L)); - 복수_꿀조합_저장(recipe1, recipe2); - - final var author = RecipeAuthorDto.toDto(member); - final var rankingRecipeDto1 = RankingRecipeDto.toDto(recipe1, Collections.emptyList(), author); - final var rankingRecipeDto2 = RankingRecipeDto.toDto(recipe2, Collections.emptyList(), author); - final var rankingRecipesDtos = List.of(rankingRecipeDto2, rankingRecipeDto1); - final var expected = RankingRecipesResponse.toResponse(rankingRecipesDtos); - - // when - final var actual = recipeService.getTop3Recipes(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 꿀조합_생성_일자가_같으면_좋아요_수가_많은_꿀조합의_랭킹을_더_높게_반환한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var now = LocalDateTime.now(); - final var recipe1 = 레시피_생성(member, 2L, now.minusDays(1L)); - final var recipe2 = 레시피_생성(member, 4L, now.minusDays(1L)); - 복수_꿀조합_저장(recipe1, recipe2); - - final var author = RecipeAuthorDto.toDto(member); - final var rankingRecipeDto1 = RankingRecipeDto.toDto(recipe1, Collections.emptyList(), author); - final var rankingRecipeDto2 = RankingRecipeDto.toDto(recipe2, Collections.emptyList(), author); - final var rankingRecipesDtos = List.of(rankingRecipeDto2, rankingRecipeDto1); - final var expected = RankingRecipesResponse.toResponse(rankingRecipesDtos); - - // when - final var actual = recipeService.getTop3Recipes(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - } - - @Nested - class writeCommentOfRecipe_성공_테스트 { - - @Test - void 꿀조합에_댓글을_작성할_수_있다() { - // given - final var category = 카테고리_간편식사_생성(); - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격3000원_평점4점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - 복수_상품_저장(product1, product2, product3); - final var author = 멤버_멤버1_생성(); - 단일_멤버_저장(author); - final var authorId = author.getId(); - - final var images = 여러_이미지_생성(3); - - final var productIds = List.of(product1.getId(), product2.getId(), product3.getId()); - final var recipeCreateRequest = new RecipeCreateRequest("제일로 맛있는 레시피", productIds, - "우선 밥을 넣어요. 그리고 밥을 또 넣어요. 그리고 밥을 또 넣으면.. 끝!!"); - - final var savedMemberId = 단일_멤버_저장(멤버_멤버1_생성()); - final var savedRecipeId = recipeService.create(authorId, images, recipeCreateRequest); - - // when - final var request = new RecipeCommentCreateRequest("꿀조합 댓글이에요"); - final var savedCommentId = recipeService.writeCommentOfRecipe(savedMemberId, savedRecipeId, request); - - // then - final var result = commentRepository.findById(savedCommentId).get(); - final var savedRecipe = recipeRepository.findById(savedRecipeId).get(); - final var savedMember = memberRepository.findById(savedMemberId).get(); - - assertThat(result).usingRecursiveComparison() - .ignoringFields("id", "createdAt") - .isEqualTo(new Comment(savedRecipe, savedMember, request.getComment())); - } - } - - @Nested - class writeCommentOfRecipe_실패_테스트 { - - @Test - void 존재하지_않은_멤버가_꿀조합에_댓글을_작성하면_예외가_발생한다() { - // given - final var category = 카테고리_간편식사_생성(); - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격3000원_평점4점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - 복수_상품_저장(product1, product2, product3); - final var author = new Member("author", "image.png", "1"); - 단일_멤버_저장(author); - final var authorId = author.getId(); - - final var images = 여러_이미지_생성(3); - - final var productIds = List.of(product1.getId(), product2.getId(), product3.getId()); - final var recipeCreateRequest = new RecipeCreateRequest("제일로 맛있는 레시피", productIds, - "우선 밥을 넣어요. 그리고 밥을 또 넣어요. 그리고 밥을 또 넣으면.. 끝!!"); - - final var notExistMemberId = 999999999L; - final var savedRecipeId = recipeService.create(authorId, images, recipeCreateRequest); - final var request = new RecipeCommentCreateRequest("꿀조합 댓글이에요"); - - // when then - assertThatThrownBy(() -> recipeService.writeCommentOfRecipe(notExistMemberId, savedRecipeId, request)) - .isInstanceOf(MemberNotFoundException.class); - } - - @Test - void 존재하지_않은_꿀조합에_댓글을_작성하면_예외가_발생한다() { - // given - final var memberId = 단일_멤버_저장(멤버_멤버1_생성()); - final var request = new RecipeCommentCreateRequest("꿀조합 댓글이에요"); - final var notExistRecipeId = 999999999L; - - // when then - assertThatThrownBy(() -> recipeService.writeCommentOfRecipe(memberId, notExistRecipeId, request)) - .isInstanceOf(RecipeNotFoundException.class); - } - } - - @Nested - class getCommentsOfRecipe_성공_테스트 { - - @Test - void 꿀조합에_달린_댓글들을_커서페이징을_통해_조회할_수_있다_총_댓글_15개_중_첫페이지_댓글_10개조회() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격3000원_평점4점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - 복수_상품_저장(product1, product2, product3); - final var author = 멤버_멤버1_생성(); - 단일_멤버_저장(author); - final var authorId = author.getId(); - - final var images = 여러_이미지_생성(3); - - final var productIds = List.of(product1.getId(), product2.getId(), product3.getId()); - final var recipeCreateRequest = new RecipeCreateRequest("제일로 맛있는 레시피", productIds, - "우선 밥을 넣어요. 그리고 밥을 또 넣어요. 그리고 밥을 또 넣으면.. 끝!!"); - - final var savedMemberId = 단일_멤버_저장(멤버_멤버1_생성()); - final var savedRecipeId = recipeService.create(authorId, images, recipeCreateRequest); - - for (int i = 1; i <= 15; i++) { - final var request = new RecipeCommentCreateRequest("꿀조합 댓글이에요" + i); - recipeService.writeCommentOfRecipe(savedMemberId, savedRecipeId, request); - } - - // when - final var result = recipeService.getCommentsOfRecipe(savedRecipeId, - new RecipeCommentCondition(null, null)); - - // - final var savedRecipe = recipeRepository.findById(savedRecipeId).get(); - final var savedMember = memberRepository.findById(savedMemberId).get(); - - final var expectedCommentResponses = new ArrayList<>(); - for (int i = 0; i < result.getComments().size(); i++) { - expectedCommentResponses.add(RecipeCommentResponse.toResponse( - new Comment(savedRecipe, savedMember, "꿀조합 댓글이에요" + (15 - i)))); - } - - assertThat(result.getHasNext()).isTrue(); - assertThat(result.getTotalElements()).isEqualTo(15); - assertThat(result.getComments()).hasSize(10); - assertThat(result.getComments()).usingRecursiveComparison() - .ignoringFields("id", "createdAt") - .isEqualTo(expectedCommentResponses); - } - - @Test - void 꿀조합에_달린_댓글들을_커서페이징을_통해_조회할_수_있다_총_댓글_15개_중_마지막페이지_댓글_5개조회() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격3000원_평점4점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - 복수_상품_저장(product1, product2, product3); - final var author = 멤버_멤버1_생성(); - 단일_멤버_저장(author); - final var authorId = author.getId(); - - final var images = 여러_이미지_생성(3); - - final var productIds = List.of(product1.getId(), product2.getId(), product3.getId()); - final var recipeCreateRequest = new RecipeCreateRequest("제일로 맛있는 레시피", productIds, - "우선 밥을 넣어요. 그리고 밥을 또 넣어요. 그리고 밥을 또 넣으면.. 끝!!"); - - final var savedMemberId = 단일_멤버_저장(멤버_멤버1_생성()); - final var savedRecipeId = recipeService.create(authorId, images, recipeCreateRequest); - - for (int i = 1; i <= 15; i++) { - final var request = new RecipeCommentCreateRequest("꿀조합 댓글이에요" + i); - recipeService.writeCommentOfRecipe(savedMemberId, savedRecipeId, request); - } - - // when - final var result = recipeService.getCommentsOfRecipe(savedRecipeId, - new RecipeCommentCondition(6L, 15L)); - - // - final var savedRecipe = recipeRepository.findById(savedRecipeId).get(); - final var savedMember = memberRepository.findById(savedMemberId).get(); - - final var expectedCommentResponses = new ArrayList<>(); - for (int i = 0; i < result.getComments().size(); i++) { - expectedCommentResponses.add(RecipeCommentResponse.toResponse( - new Comment(savedRecipe, savedMember, "꿀조합 댓글이에요" + (5 - i)))); - } - - assertThat(result.getHasNext()).isFalse(); - assertThat(result.getTotalElements()).isEqualTo(15); - assertThat(result.getComments()).hasSize(5); - assertThat(result.getComments()).usingRecursiveComparison() - .ignoringFields("id", "createdAt") - .isEqualTo(expectedCommentResponses); - } - } - - private void 해당멤버의_꿀조합과_페이징_결과를_검증한다(final MemberRecipesResponse actual, final List expectedRecipesDtos, - final PageDto expectedPage) { - assertSoftly(soft -> { - assertThat(actual.getRecipes()).usingRecursiveComparison() - .isEqualTo(expectedRecipesDtos); - assertThat(actual.getPage()).usingRecursiveComparison() - .isEqualTo(expectedPage); - }); - } - - private Category 카테고리_추가_요청(final Category category) { - return categoryRepository.save(category); - } - - private List 상품_아이디_변환(final Product... products) { - return Stream.of(products) - .map(Product::getId) - .collect(Collectors.toList()); - } -} diff --git a/backend/src/test/java/com/funeat/recipe/domain/RecipeTest.java b/backend/src/test/java/com/funeat/recipe/domain/RecipeTest.java deleted file mode 100644 index 7a0d28030..000000000 --- a/backend/src/test/java/com/funeat/recipe/domain/RecipeTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.funeat.recipe.domain; - -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.RecipeFixture.레시피_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.LocalDateTime; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -class RecipeTest { - - @Nested - class calculateRankingScore_성공_테스트 { - - @Test - void 꿀조합_좋아요_수와_꿀조합_생성_시간으로_해당_꿀조합의_랭킹_점수를_구할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - final var favoriteCount = 4L; - final var recipe = 레시피_생성(member, favoriteCount, LocalDateTime.now().minusDays(1L)); - - final var expected = favoriteCount / Math.pow(2.0, 0.1); - - // when - final var actual = recipe.calculateRankingScore(); - - // then - assertThat(actual).isEqualTo(expected); - } - } -} diff --git a/backend/src/test/java/com/funeat/recipe/persistence/RecipeImageRepositoryTest.java b/backend/src/test/java/com/funeat/recipe/persistence/RecipeImageRepositoryTest.java deleted file mode 100644 index bb2c80923..000000000 --- a/backend/src/test/java/com/funeat/recipe/persistence/RecipeImageRepositoryTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.funeat.recipe.persistence; - -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.ProductFixture.레시피_안에_들어가는_상품_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격3000원_평점1점_생성; -import static com.funeat.fixture.RecipeFixture.레시피_생성; -import static com.funeat.fixture.RecipeFixture.레시피이미지_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.common.RepositoryTest; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -class RecipeImageRepositoryTest extends RepositoryTest { - - @Nested - class findByRecipe_성공_테스트 { - - @Test - void 레시피에_사용된_이미지들을_조회할_수_있다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점1점_생성(category); - final var product2 = 상품_삼각김밥_가격3000원_평점1점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점1점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var recipe = 레시피_생성(member); - 단일_레시피_저장(recipe); - - final var productRecipe1 = 레시피_안에_들어가는_상품_생성(product1, recipe); - final var productRecipe2 = 레시피_안에_들어가는_상품_생성(product2, recipe); - final var productRecipe3 = 레시피_안에_들어가는_상품_생성(product3, recipe); - 복수_레시피_상품_저장(productRecipe1, productRecipe2, productRecipe3); - - final var image1 = 레시피이미지_생성(recipe); - final var image2 = 레시피이미지_생성(recipe); - final var image3 = 레시피이미지_생성(recipe); - 복수_레시피_이미지_저장(image1, image2, image3); - - // when - final var images = recipeImageRepository.findByRecipe(recipe); - - // then - assertThat(images.size()).isEqualTo(3); - } - } -} diff --git a/backend/src/test/java/com/funeat/recipe/persistence/RecipeRepositoryTest.java b/backend/src/test/java/com/funeat/recipe/persistence/RecipeRepositoryTest.java deleted file mode 100644 index 9c53177db..000000000 --- a/backend/src/test/java/com/funeat/recipe/persistence/RecipeRepositoryTest.java +++ /dev/null @@ -1,307 +0,0 @@ -package com.funeat.recipe.persistence; - -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버2_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버3_생성; -import static com.funeat.fixture.PageFixture.과거순; -import static com.funeat.fixture.PageFixture.좋아요수_내림차순; -import static com.funeat.fixture.PageFixture.최신순; -import static com.funeat.fixture.PageFixture.페이지요청_기본_생성; -import static com.funeat.fixture.PageFixture.페이지요청_생성; -import static com.funeat.fixture.ProductFixture.레시피_안에_들어가는_상품_생성; -import static com.funeat.fixture.ProductFixture.상품_망고빙수_가격5000원_평점4점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점5점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점3점_생성; -import static com.funeat.fixture.ProductFixture.상품_애플망고_가격3000원_평점5점_생성; -import static com.funeat.fixture.RecipeFixture.레시피_생성; -import static com.funeat.fixture.RecipeFixture.레시피이미지_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.common.RepositoryTest; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -class RecipeRepositoryTest extends RepositoryTest { - - @Nested - class findAllByProductNameContaining_성공_테스트 { - - @Test - void 상품명에_검색어가_포함된_상품이_있는_레시피들을_조회한다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점1점_생성(category); - final var product2 = 상품_망고빙수_가격5000원_평점4점_생성(category); - final var product3 = 상품_애플망고_가격3000원_평점5점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var recipe1 = 레시피_생성(member); - final var recipe2 = 레시피_생성(member); - 복수_레시피_저장(recipe1, recipe2); - - final var productRecipe1 = 레시피_안에_들어가는_상품_생성(product1, recipe1); - final var productRecipe2 = 레시피_안에_들어가는_상품_생성(product2, recipe1); - final var productRecipe3 = 레시피_안에_들어가는_상품_생성(product3, recipe2); - 복수_레시피_상품_저장(productRecipe1, productRecipe2, productRecipe3); - - final var image1 = 레시피이미지_생성(recipe1); - final var image2 = 레시피이미지_생성(recipe2); - 복수_레시피_이미지_저장(image1, image2); - - final var page = 페이지요청_기본_생성(0, 10); - final var expected = List.of(recipe1, recipe2); - - // when - final var actual = recipeRepository.findAllByProductNameContaining("망고", page).getContent(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class findAllRecipes_성공_테스트 { - - @Test - void 꿀조합을_좋아요가_많은_순으로_정렬한다() { - // given - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - final var member3 = 멤버_멤버3_생성(); - 복수_멤버_저장(member1, member2, member3); - - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점5점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점1점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var recipe1_1 = 레시피_생성(member1, 1L); - final var recipe1_2 = 레시피_생성(member1, 3L); - final var recipe1_3 = 레시피_생성(member1, 2L); - 복수_꿀조합_저장(recipe1_1, recipe1_2, recipe1_3); - - final var product_recipe_1_1_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_1); - final var product_recipe_1_1_2 = 레시피_안에_들어가는_상품_생성(product2, recipe1_1); - final var product_recipe_1_1_3 = 레시피_안에_들어가는_상품_생성(product3, recipe1_1); - final var product_recipe_1_2_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_2); - final var product_recipe_1_2_2 = 레시피_안에_들어가는_상품_생성(product3, recipe1_2); - 복수_꿀조합_상품_저장(product_recipe_1_1_1, product_recipe_1_1_2, product_recipe_1_1_3, product_recipe_1_2_1, - product_recipe_1_2_2); - - final var recipeImage1_1 = 레시피이미지_생성(recipe1_1); - final var recipeImage1_2 = 레시피이미지_생성(recipe1_2); - 복수_꿀조합_이미지_저장(recipeImage1_1, recipeImage1_2); - - final var page = 페이지요청_생성(0, 10, 좋아요수_내림차순); - final var expected = List.of(recipe1_2, recipe1_3, recipe1_1); - - // when - final var actual = recipeRepository.findAll(page).getContent(); - - // then - assertThat(actual) - .usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 꿀조합을_최신순으로_정렬한다() throws InterruptedException { - // given - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - final var member3 = 멤버_멤버3_생성(); - 복수_멤버_저장(member1, member2, member3); - - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점5점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점1점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var recipe1_1 = 레시피_생성(member1, 1L); - Thread.sleep(100); - final var recipe1_2 = 레시피_생성(member1, 3L); - Thread.sleep(100); - final var recipe1_3 = 레시피_생성(member1, 2L); - 복수_꿀조합_저장(recipe1_1, recipe1_2, recipe1_3); - - final var product_recipe_1_1_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_1); - final var product_recipe_1_1_2 = 레시피_안에_들어가는_상품_생성(product2, recipe1_1); - final var product_recipe_1_1_3 = 레시피_안에_들어가는_상품_생성(product3, recipe1_1); - final var product_recipe_1_2_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_2); - final var product_recipe_1_2_2 = 레시피_안에_들어가는_상품_생성(product3, recipe1_2); - 복수_꿀조합_상품_저장(product_recipe_1_1_1, product_recipe_1_1_2, product_recipe_1_1_3, product_recipe_1_2_1, - product_recipe_1_2_2); - - final var recipeImage1_1 = 레시피이미지_생성(recipe1_1); - final var recipeImage1_2 = 레시피이미지_생성(recipe1_2); - 복수_꿀조합_이미지_저장(recipeImage1_1, recipeImage1_2); - - final var page = 페이지요청_생성(0, 10, 최신순); - final var expected = List.of(recipe1_3, recipe1_2, recipe1_1); - - // when - final var actual = recipeRepository.findAll(page).getContent(); - - // then - assertThat(actual) - .usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 꿀조합을_오래된순으로_정렬한다() { - // given - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - final var member3 = 멤버_멤버3_생성(); - 복수_멤버_저장(member1, member2, member3); - - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점5점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점1점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var recipe1_1 = 레시피_생성(member1, 1L); - final var recipe1_2 = 레시피_생성(member1, 3L); - final var recipe1_3 = 레시피_생성(member1, 2L); - 복수_꿀조합_저장(recipe1_1, recipe1_2, recipe1_3); - - final var product_recipe_1_1_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_1); - final var product_recipe_1_1_2 = 레시피_안에_들어가는_상품_생성(product2, recipe1_1); - final var product_recipe_1_1_3 = 레시피_안에_들어가는_상품_생성(product3, recipe1_1); - final var product_recipe_1_2_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1_2); - final var product_recipe_1_2_2 = 레시피_안에_들어가는_상품_생성(product3, recipe1_2); - 복수_꿀조합_상품_저장(product_recipe_1_1_1, product_recipe_1_1_2, product_recipe_1_1_3, product_recipe_1_2_1, - product_recipe_1_2_2); - - final var recipeImage1_1 = 레시피이미지_생성(recipe1_1); - final var recipeImage1_2 = 레시피이미지_생성(recipe1_2); - 복수_꿀조합_이미지_저장(recipeImage1_1, recipeImage1_2); - - final var page = 페이지요청_생성(0, 10, 과거순); - final var expected = List.of(recipe1_1, recipe1_2, recipe1_3); - - // when - final var actual = recipeRepository.findAll(page).getContent(); - - // then - assertThat(actual) - .usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class findRecipesByProduct_성공_테스트 { - - @Test - void 상품이_포함된_레시피들을_조회할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점5점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점1점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var recipe1 = 레시피_생성(member, 1L); - final var recipe2 = 레시피_생성(member, 3L); - final var recipe3 = 레시피_생성(member, 2L); - 복수_꿀조합_저장(recipe1, recipe2, recipe3); - - final var product_recipe_1_1 = 레시피_안에_들어가는_상품_생성(product1, recipe1); - final var product_recipe_1_2 = 레시피_안에_들어가는_상품_생성(product1, recipe2); - final var product_recipe_2_1 = 레시피_안에_들어가는_상품_생성(product2, recipe1); - final var product_recipe_3_1 = 레시피_안에_들어가는_상품_생성(product3, recipe1); - final var product_recipe_3_2 = 레시피_안에_들어가는_상품_생성(product3, recipe2); - 복수_꿀조합_상품_저장(product_recipe_1_1, product_recipe_2_1, product_recipe_3_1, product_recipe_1_2, - product_recipe_3_2); - - final var recipeImage1_1 = 레시피이미지_생성(recipe1); - final var recipeImage2_1 = 레시피이미지_생성(recipe2); - final var recipeImage2_2 = 레시피이미지_생성(recipe2); - 복수_꿀조합_이미지_저장(recipeImage1_1, recipeImage2_1, recipeImage2_2); - - final var page = 페이지요청_생성(0, 10, 좋아요수_내림차순); - final var expected = List.of(recipe2, recipe1); - - // when - final var actual = recipeRepository.findRecipesByProduct(product1, page).getContent(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class findRecipesByFavoriteCountGreaterThanEqual_성공_테스트 { - - @Test - void 특정_좋아요_수_이상인_모든_꿀조합들을_조회한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var recipe1 = 레시피_생성(member, 0L); - final var recipe2 = 레시피_생성(member, 1L); - final var recipe3 = 레시피_생성(member, 10L); - final var recipe4 = 레시피_생성(member, 100L); - 복수_꿀조합_저장(recipe1, recipe2, recipe3, recipe4); - - final var expected = List.of(recipe2, recipe3, recipe4); - - // when - final var actual = recipeRepository.findRecipesByFavoriteCountGreaterThanEqual(1L); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 특정_좋아요_수_이상인_꿀조합이_없으면_빈_리스트를_반환한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var recipe1 = 레시피_생성(member, 0L); - final var recipe2 = 레시피_생성(member, 0L); - 복수_꿀조합_저장(recipe1, recipe2); - - final var expected = Collections.emptyList(); - - // when - final var actual = recipeRepository.findRecipesByFavoriteCountGreaterThanEqual(1L); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } -} diff --git a/backend/src/test/java/com/funeat/review/application/ReviewDeleteEventListenerTest.java b/backend/src/test/java/com/funeat/review/application/ReviewDeleteEventListenerTest.java deleted file mode 100644 index 1349eaa37..000000000 --- a/backend/src/test/java/com/funeat/review/application/ReviewDeleteEventListenerTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.funeat.review.application; - -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버2_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점2점_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test1_평점1점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test2_평점2점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test4_평점4점_재구매O_생성; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; - -import com.funeat.common.EventTest; -import com.funeat.common.ImageUploader; -import com.funeat.common.exception.CommonException.S3DeleteFailException; -import com.funeat.exception.CommonErrorCode; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -@SuppressWarnings("NonAsciiCharacters") -class ReviewDeleteEventListenerTest extends EventTest { - - @MockBean - private ImageUploader uploader; - - @Nested - class 리뷰_삭제_이벤트_발행 { - - @Test - void 리뷰_작성자가_리뷰_삭제_시도시_리뷰_삭제_이벤트가_발행된다() { - // given - final var author = 멤버_멤버1_생성(); - final var authorId = 단일_멤버_저장(author); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var review = reviewRepository.save(리뷰_이미지test1_평점1점_재구매O_생성(author, product, 0L)); - - // when - reviewService.deleteReview(review.getId(), authorId); - - // then - final var count = events.stream(ReviewDeleteEvent.class).count(); - assertThat(count).isEqualTo(1); - } - - @Test - void 리뷰_작성자가_아닌_사람이_리뷰_삭제_시도시_리뷰_삭제_이벤트가_발행되지_않는다() { - // given - final var author = 멤버_멤버1_생성(); - final var authorId = 단일_멤버_저장(author); - - final var member = 멤버_멤버2_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var review = reviewRepository.save(리뷰_이미지test2_평점2점_재구매O_생성(author, product, 0L)); - - // when - try { - reviewService.deleteReview(review.getId(), memberId); - } catch (Exception ignored) { - } - - // then - final var count = events.stream(ReviewDeleteEvent.class).count(); - assertThat(count).isEqualTo(0); - } - } - - @Nested - class 이미지_삭제_로직_작동 { - - @Test - void 이미지_삭제_로직이_실패해도_메인로직까지_롤백되어서는_안된다() { - // given - final var author = 멤버_멤버1_생성(); - final var authorId = 단일_멤버_저장(author); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var review = reviewRepository.save(리뷰_이미지test4_평점4점_재구매O_생성(author, product, 0L)); - - doThrow(new S3DeleteFailException(CommonErrorCode.UNKNOWN_SERVER_ERROR_CODE)) - .when(uploader) - .delete(any()); - - // when - reviewService.deleteReview(review.getId(), authorId); - - // then - assertThat(reviewRepository.findById(review.getId())).isEmpty(); - } - } -} diff --git a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java deleted file mode 100644 index 93fd0e046..000000000 --- a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java +++ /dev/null @@ -1,1224 +0,0 @@ -package com.funeat.review.application; - -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static com.funeat.fixture.ImageFixture.이미지_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버2_생성; -import static com.funeat.fixture.PageFixture.최신순; -import static com.funeat.fixture.PageFixture.페이지요청_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점2점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점3점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점4점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점5점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점3점_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test1_평점1점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test1_평점1점_재구매X_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test2_평점2점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test2_평점2점_재구매X_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test3_평점3점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test3_평점3점_재구매X_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test4_평점4점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test5_평점5점_재구매X_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지없음_평점1점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰정렬요청_좋아요수_내림차순_생성; -import static com.funeat.fixture.ReviewFixture.리뷰정렬요청_최신순_생성; -import static com.funeat.fixture.ReviewFixture.리뷰정렬요청_평점_내림차순_생성; -import static com.funeat.fixture.ReviewFixture.리뷰정렬요청_평점_오름차순_생성; -import static com.funeat.fixture.ReviewFixture.리뷰좋아요요청_생성; -import static com.funeat.fixture.ReviewFixture.리뷰추가요청_재구매O_생성; -import static com.funeat.fixture.TagFixture.태그_맛있어요_TASTE_생성; -import static com.funeat.fixture.TagFixture.태그_아침식사_ETC_생성; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import com.funeat.common.ServiceTest; -import com.funeat.common.dto.PageDto; -import com.funeat.member.dto.MemberReviewDto; -import com.funeat.member.exception.MemberException.MemberNotFoundException; -import com.funeat.product.exception.ProductException.ProductNotFoundException; -import com.funeat.review.domain.Review; -import com.funeat.review.dto.MostFavoriteReviewResponse; -import com.funeat.review.dto.RankingReviewDto; -import com.funeat.review.dto.RankingReviewsResponse; -import com.funeat.review.dto.ReviewDetailResponse; -import com.funeat.review.dto.SortingReviewDto; -import com.funeat.review.exception.ReviewException.NotAuthorOfReviewException; -import com.funeat.review.exception.ReviewException.ReviewNotFoundException; -import com.funeat.tag.domain.Tag; -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -class ReviewServiceTest extends ServiceTest { - - @Nested - class create_성공_테스트 { - - @Test - void 이미지가_존재하는_리뷰를_추가할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_아침식사_ETC_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - final var image = 이미지_생성(); - final var imageFileName = image.getOriginalFilename(); - - final var request = 리뷰추가요청_재구매O_생성(4L, tagIds); - - final var expected = new Review(member, product, imageFileName, 4L, "test", true); - - // when - reviewService.create(productId, memberId, image, request); - final var actual = reviewRepository.findAll().get(0); - - // then - assertThat(actual).usingRecursiveComparison() - .comparingOnlyFields("member", "product", "image", "rating", "content", "reBuy") - .isEqualTo(expected); - } - - @Test - void 이미지가_없는_리뷰를_추가할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_아침식사_ETC_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - - final var request = 리뷰추가요청_재구매O_생성(4L, tagIds); - - final var expected = new Review(member, product, "", 4L, "test", true); - - // when - reviewService.create(productId, memberId, null, request); - final var actual = reviewRepository.findAll().get(0); - - // then - assertThat(actual).usingRecursiveComparison() - .comparingOnlyFields("member", "product", "image", "rating", "content", "reBuy") - .isEqualTo(expected); - } - } - - @Nested - class create_실패_테스트 { - - @Test - void 존재하지_않는_멤버로_상품에_리뷰를_추가하면_예외가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - final var wrongMemberId = 단일_멤버_저장(member) + 1L; - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_아침식사_ETC_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - final var image = 이미지_생성(); - - final var request = 리뷰추가요청_재구매O_생성(4L, tagIds); - - // when & then - assertThatThrownBy(() -> reviewService.create(productId, wrongMemberId, image, request)) - .isInstanceOf(MemberNotFoundException.class); - } - - @Test - void 멤버로_존재하지_않는_상품에_리뷰를_추가하면_예외가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var wrongProductId = 단일_상품_저장(product) + 1L; - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_아침식사_ETC_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - final var image = 이미지_생성(); - - final var request = 리뷰추가요청_재구매O_생성(4L, tagIds); - - // when & then - assertThatThrownBy(() -> reviewService.create(wrongProductId, memberId, image, request)) - .isInstanceOf(ProductNotFoundException.class); - } - } - - @Nested - class likeReview_성공_테스트 { - - @Test - void 리뷰에_좋아요를_할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_아침식사_ETC_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - final var image = 이미지_생성(); - final var reviewCreaterequest = 리뷰추가요청_재구매O_생성(4L, tagIds); - reviewService.create(productId, memberId, image, reviewCreaterequest); - - final var review = reviewRepository.findAll().get(0); - final var reviewId = review.getId(); - - final var favoriteRequest = 리뷰좋아요요청_생성(true); - - // when - reviewService.likeReview(reviewId, memberId, favoriteRequest); - - final var actualReview = reviewRepository.findAll().get(0); - final var actualReviewFavorite = reviewFavoriteRepository.findAll().get(0); - - // then - assertSoftly(soft -> { - soft.assertThat(actualReview.getFavoriteCount()) - .isOne(); - soft.assertThat(actualReviewFavorite.getFavorite()) - .isTrue(); - }); - } - - @Test - void 리뷰에_좋아요를_취소_할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_아침식사_ETC_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - final var image = 이미지_생성(); - final var reviewCreaterequest = 리뷰추가요청_재구매O_생성(4L, tagIds); - reviewService.create(productId, memberId, image, reviewCreaterequest); - - final var review = reviewRepository.findAll().get(0); - final var reviewId = review.getId(); - - final var favoriteRequest = 리뷰좋아요요청_생성(true); - reviewService.likeReview(reviewId, memberId, favoriteRequest); - - // when - final var cancelFavoriteRequest = 리뷰좋아요요청_생성(false); - reviewService.likeReview(reviewId, memberId, cancelFavoriteRequest); - - final var actualReview = reviewRepository.findAll().get(0); - final var actualReviewFavorite = reviewFavoriteRepository.findAll().get(0); - - // then - assertSoftly(soft -> { - soft.assertThat(actualReview.getFavoriteCount()) - .isZero(); - soft.assertThat(actualReviewFavorite.getFavorite()) - .isFalse(); - }); - } - } - - @Nested - class likeReview_실패_테스트 { - - @Test - void 존재하지_않는_멤버가_리뷰에_좋아요를_하면_예외가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - final var wrongMemberId = memberId + 1L; - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_아침식사_ETC_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - final var image = 이미지_생성(); - final var reviewCreaterequest = 리뷰추가요청_재구매O_생성(4L, tagIds); - reviewService.create(productId, memberId, image, reviewCreaterequest); - - final var review = reviewRepository.findAll().get(0); - final var reviewId = review.getId(); - - final var favoriteRequest = 리뷰좋아요요청_생성(true); - - // when - assertThatThrownBy(() -> reviewService.likeReview(reviewId, wrongMemberId, favoriteRequest)) - .isInstanceOf(MemberNotFoundException.class); - } - - @Test - void 멤버가_존재하지_않는_리뷰에_좋아요를_하면_예외가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_아침식사_ETC_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - final var image = 이미지_생성(); - final var reviewCreaterequest = 리뷰추가요청_재구매O_생성(4L, tagIds); - reviewService.create(productId, memberId, image, reviewCreaterequest); - - final var review = reviewRepository.findAll().get(0); - final var reviewId = review.getId(); - final var wrongReviewId = reviewId + 1L; - - final var favoriteRequest = 리뷰좋아요요청_생성(true); - - // when - assertThatThrownBy(() -> reviewService.likeReview(wrongReviewId, memberId, favoriteRequest)) - .isInstanceOf(ReviewNotFoundException.class); - } - } - - @Nested - class sortingReviews_성공_테스트 { - - @Test - void 좋아요_기준으로_내림차순_정렬을_할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - final var productId = 단일_상품_저장(product); - - final var review1 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 351L); - final var review2 = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 24L); - final var review3 = 리뷰_이미지test3_평점3점_재구매X_생성(member, product, 130L); - 복수_리뷰_저장(review1, review2, review3); - - final var request = 리뷰정렬요청_좋아요수_내림차순_생성(0L); - - final var expected = List.of(review1.getId(), review3.getId(), review2.getId()); - - // when - final var actual = reviewService.sortingReviews(productId, memberId, request).getReviews(); - - // then - assertThat(actual).extracting(SortingReviewDto::getId) - .containsExactlyElementsOf(expected); - } - - @Test - void 최신순으로_정렬을_할_수_있다() throws InterruptedException { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - final var productId = 단일_상품_저장(product); - - final var review1 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 351L); - Thread.sleep(100); - final var review2 = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 24L); - Thread.sleep(100); - final var review3 = 리뷰_이미지test3_평점3점_재구매X_생성(member, product, 130L); - 복수_리뷰_저장(review1, review2, review3); - - final var request = 리뷰정렬요청_최신순_생성(3L); - - final var expected = List.of(review2.getId(), review1.getId()); - - // when - final var actual = reviewService.sortingReviews(productId, memberId, request).getReviews(); - - // then - assertThat(actual).extracting(SortingReviewDto::getId) - .containsExactlyElementsOf(expected); - } - - @Test - void 평점_기준으로_오름차순_정렬을_할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - final var productId = 단일_상품_저장(product); - - final var review1 = 리뷰_이미지test2_평점2점_재구매O_생성(member, product, 351L); - final var review2 = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 24L); - final var review3 = 리뷰_이미지test3_평점3점_재구매X_생성(member, product, 130L); - 복수_리뷰_저장(review1, review2, review3); - - final var request = 리뷰정렬요청_평점_오름차순_생성(0L); - - final var expected = List.of(review1.getId(), review3.getId(), review2.getId()); - - // when - final var actual = reviewService.sortingReviews(productId, memberId, request).getReviews(); - - // then - assertThat(actual).extracting(SortingReviewDto::getId) - .containsExactlyElementsOf(expected); - } - - @Test - void 평점_기준으로_내림차순_정렬을_할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - final var productId = 단일_상품_저장(product); - - final var review1 = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 5L); - final var review2 = 리뷰_이미지test2_평점2점_재구매O_생성(member, product, 24L); - final var review3 = 리뷰_이미지test3_평점3점_재구매X_생성(member, product, 13L); - 복수_리뷰_저장(review1, review2, review3); - - final var request = 리뷰정렬요청_평점_내림차순_생성(1L); - - final var expected = List.of(review3.getId(), review2.getId()); - - // when - final var actual = reviewService.sortingReviews(productId, memberId, request).getReviews(); - - // then - assertThat(actual).extracting(SortingReviewDto::getId) - .containsExactlyElementsOf(expected); - } - } - - @Nested - class sortingReviews_실패_테스트 { - - @Test - void 존재하지_않는_멤버가_상품에_있는_리뷰들을_정렬하면_예외가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - final var wrongMemberId = 단일_멤버_저장(member) + 3L; - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - final var productId = 단일_상품_저장(product); - - final var review1 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 351L); - final var review2 = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 24L); - final var review3 = 리뷰_이미지test3_평점3점_재구매X_생성(member, product, 130L); - 복수_리뷰_저장(review1, review2, review3); - - final var request = 리뷰정렬요청_평점_내림차순_생성(1L); - - // when & then - assertThatThrownBy(() -> reviewService.sortingReviews(productId, wrongMemberId, request)) - .isInstanceOf(MemberNotFoundException.class); - } - - @Test - void 멤버가_존재하지_않는_상품에_있는_리뷰들을_정렬하면_예외가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - final var wrongProductId = 단일_상품_저장(product) + 1L; - - final var review1 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 351L); - final var review2 = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 24L); - final var review3 = 리뷰_이미지test3_평점3점_재구매X_생성(member, product, 130L); - 복수_리뷰_저장(review1, review2, review3); - - final var request = 리뷰정렬요청_평점_내림차순_생성(1L); - - // when & then - assertThatThrownBy(() -> reviewService.sortingReviews(wrongProductId, memberId, request)) - .isInstanceOf(ProductNotFoundException.class); - } - } - - @Nested - class findReviewByMember_성공_테스트 { - - @Test - void 사용자가_작성한_리뷰를_조회한다() { - // given - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - 복수_멤버_저장(member1, member2); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점5점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - final var product3 = 상품_삼각김밥_가격2000원_평점1점_생성(category); - 복수_상품_저장(product1, product2, product3); - - final var review1_1 = 리뷰_이미지test2_평점2점_재구매X_생성(member1, product3, 0L); - - final var review2_1 = 리뷰_이미지test1_평점1점_재구매X_생성(member1, product2, 0L); - final var review2_2 = 리뷰_이미지test1_평점1점_재구매X_생성(member2, product2, 0L); - - final var review3_1 = 리뷰_이미지test3_평점3점_재구매O_생성(member1, product1, 0L); - final var review3_2 = 리뷰_이미지test3_평점3점_재구매O_생성(member2, product1, 0L); - 복수_리뷰_저장(review1_1, review2_1, review2_2, review3_1, review3_2); - - // when - final var page = 페이지요청_생성(0, 10, 최신순); - final var member1Id = member1.getId(); - final var result = reviewService.findReviewByMember(member1Id, page); - - // then - final var expectedReviews = List.of(review3_1, review2_1, review1_1); - final var expectedReviewDtos = expectedReviews.stream() - .map(MemberReviewDto::toDto) - .collect(Collectors.toList()); - final var expectedPage = new PageDto(3L, 1L, true, true, 0L, 10L); - - assertThat(result.getReviews()).usingRecursiveComparison() - .isEqualTo(expectedReviewDtos); - assertThat(result.getPage()).usingRecursiveComparison() - .isEqualTo(expectedPage); - } - } - - @Nested - class findReviewByMember_실패_테스트 { - - @Test - void 존재하지_않은_사용자가_작성한_리뷰를_조회할때_예외가_발생한다() { - // given - final var notExistMemberId = 999999L; - final var page = 페이지요청_생성(0, 10, 최신순); - - // when & then - assertThatThrownBy(() -> reviewService.findReviewByMember(notExistMemberId, page)) - .isInstanceOf(MemberNotFoundException.class); - } - } - - @Nested - class updateProductImage_성공_테스트 { - - @Test - void 처음_리뷰가_등록되면_해당_리뷰의_이미지로_상품_이미지가_변경된다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var review = 리뷰_이미지test1_평점1점_재구매O_생성(member, product, 0L); - final var reviewId = 단일_리뷰_저장(review); - - final var expected = review.getImage(); - - // when - reviewService.updateProductImage(product.getId()); - final var actual = product.getImage(); - - // then - assertThat(actual).isEqualTo(expected); - } - - @Test - void 가장_많은_좋아요를_받은_리뷰가_바뀌면_해당_리뷰의_이미지로_상품_이미지가_변경된다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var firstReview = 리뷰_이미지test1_평점1점_재구매O_생성(member, product, 1L); - final var firstReviewId = 단일_리뷰_저장(firstReview); - reviewService.updateProductImage(firstReviewId); - - final var secondReview = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 2L); - final var secondReviewId = 단일_리뷰_저장(secondReview); - - final var expected = secondReview.getImage(); - - // when - reviewService.updateProductImage(product.getId()); - final var actual = product.getImage(); - - // then - assertThat(actual).isEqualTo(expected); - } - - @Test - void 리뷰에_좋아요를_했지만_가장_많은_좋아요인_리뷰가_바뀌지_않으면_상품_이미지는_변경되지_않는다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var firstReview = 리뷰_이미지test1_평점1점_재구매O_생성(member, product, 100L); - final var firstReviewId = 단일_리뷰_저장(firstReview); - reviewService.updateProductImage(firstReviewId); - - final var secondReview = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 0L); - final var secondReviewId = 단일_리뷰_저장(secondReview); - - final var expected = firstReview.getImage(); - - // when - reviewService.updateProductImage(product.getId()); - final var actual = product.getImage(); - - // then - assertThat(actual).isEqualTo(expected); - } - - @Test - void 리뷰에_좋아요를_했는데_가장_많은_좋아요가_된_리뷰가_여러개면_최근에_가장_많은_좋아요_리뷰_이미지로_바뀐다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var firstReview = 리뷰_이미지test1_평점1점_재구매O_생성(member, product, 0L); - final var firstReviewId = 단일_리뷰_저장(firstReview); - reviewService.updateProductImage(firstReviewId); - - final var secondReview = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 0L); - final var secondReviewId = 단일_리뷰_저장(secondReview); - - final var expected = secondReview.getImage(); - - // when - reviewService.updateProductImage(product.getId()); - final var actual = product.getImage(); - - // then - assertThat(actual).isEqualTo(expected); - } - - @Test - void 가장_많은_좋아요를_받은_리뷰들에_이미지가_없으면_이미지가_존재하는_좋아요를_많이_받은_리뷰_이미지로_바뀐다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var firstReview = 리뷰_이미지없음_평점1점_재구매O_생성(member, product, 3L); - final var firstReviewId = 단일_리뷰_저장(firstReview); - reviewService.updateProductImage(product.getId()); - - final var secondReview = 리뷰_이미지없음_평점1점_재구매O_생성(member, product, 2L); - final var secondReviewId = 단일_리뷰_저장(secondReview); - reviewService.updateProductImage(product.getId()); - - final var thirdReview = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 1L); - final var thirdReviewId = 단일_리뷰_저장(thirdReview); - - final var expected = thirdReview.getImage(); - - // when - reviewService.updateProductImage(product.getId()); - final var actual = product.getImage(); - - // then - assertThat(actual).isEqualTo(expected); - } - - @Test - void 이미지가_존재하지_않는_리뷰들만_있으면_상품_이미지는_바뀌지_않는다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var firstReview = 리뷰_이미지없음_평점1점_재구매O_생성(member, product, 3L); - final var firstReviewId = 단일_리뷰_저장(firstReview); - reviewService.updateProductImage(product.getId()); - - final var secondReview = 리뷰_이미지없음_평점1점_재구매O_생성(member, product, 2L); - final var secondReviewId = 단일_리뷰_저장(secondReview); - - final var expected = secondReview.getImage(); - - // when - reviewService.updateProductImage(product.getId()); - final var actual = product.getImage(); - - // then - assertThat(actual).isNotEqualTo(expected); - } - } - - @Nested - class updateProductImage_실패_테스트 { - - @Test - void 존재하지_않는_상품으로_상품_업데이트를_시도하면_예외가_발생한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var wrongProductId = 999L; - - // when & then - assertThatThrownBy(() -> reviewService.updateProductImage(wrongProductId)) - .isInstanceOf(ProductNotFoundException.class); - } - } - - @Nested - class deleteReview_성공_테스트 { - - @Test - void 자신이_작성한_리뷰를_삭제할_수_있다() { - // given - final var author = 멤버_멤버1_생성(); - final var authorId = 단일_멤버_저장(author); - final var member = 멤버_멤버2_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_아침식사_ETC_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - final var image = 이미지_생성(); - final var reviewCreateRequest1 = 리뷰추가요청_재구매O_생성(2L, tagIds); - final var reviewCreateRequest2 = 리뷰추가요청_재구매O_생성(4L, tagIds); - - reviewService.create(productId, authorId, image, reviewCreateRequest1); - reviewService.create(productId, authorId, image, reviewCreateRequest2); - - final var reviews = reviewRepository.findAll(); - final var rating2_review = reviews.stream() - .filter(it -> it.getRating() == 2L) - .findFirst() - .get(); - - final var favoriteRequest = 리뷰좋아요요청_생성(true); - reviewService.likeReview(rating2_review.getId(), authorId, favoriteRequest); - reviewService.likeReview(rating2_review.getId(), memberId, favoriteRequest); - - // when - reviewService.deleteReview(rating2_review.getId(), authorId); - - // then - final var tags = reviewTagRepository.findByReview(rating2_review); - final var favorites = reviewFavoriteRepository.findByReview(rating2_review); - final var findReview = reviewRepository.findById(rating2_review.getId()); - final var findProduct = productRepository.findById(productId).get(); - - assertSoftly(soft -> { - soft.assertThat(tags).isEmpty(); - soft.assertThat(favorites).isEmpty(); - soft.assertThat(findReview).isEmpty(); - soft.assertThat(findProduct.getAverageRating()).isEqualTo(4.0); - soft.assertThat(findProduct.getReviewCount()).isEqualTo(1); - }); - } - } - - @Nested - class deleteReview_실패_테스트 { - - @Test - void 존재하지_않는_사용자가_리뷰를_삭제하려하면_에러가_발생한다() { - // given - final var author = 멤버_멤버1_생성(); - final var authorId = 단일_멤버_저장(author); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_아침식사_ETC_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - final var image = 이미지_생성(); - final var reviewCreateRequest = 리뷰추가요청_재구매O_생성(4L, tagIds); - reviewService.create(productId, authorId, image, reviewCreateRequest); - - final var review = reviewRepository.findAll().get(0); - final var reviewId = review.getId(); - - final var wrongMemberId = 999L; - - // when & then - assertThatThrownBy(() -> reviewService.deleteReview(reviewId, wrongMemberId)) - .isInstanceOf(MemberNotFoundException.class); - } - - @Test - void 존재하지_않는_리뷰를_삭제하려하면_에러가_발생한다() { - // given - final var author = 멤버_멤버1_생성(); - final var authorId = 단일_멤버_저장(author); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_아침식사_ETC_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - final var image = 이미지_생성(); - final var reviewCreateRequest = 리뷰추가요청_재구매O_생성(4L, tagIds); - reviewService.create(productId, authorId, image, reviewCreateRequest); - - final var wrongReviewId = 999L; - - // when & then - assertThatThrownBy(() -> reviewService.deleteReview(wrongReviewId, authorId)) - .isInstanceOf(ReviewNotFoundException.class); - } - - @Test - void 자신이_작성하지_않은_리뷰를_삭제하려하면_에러가_발생한다() { - // given - final var author = 멤버_멤버1_생성(); - final var authorId = 단일_멤버_저장(author); - final var member = 멤버_멤버2_생성(); - final var memberId = 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_아침식사_ETC_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - final var image = 이미지_생성(); - final var reviewCreateRequest = 리뷰추가요청_재구매O_생성(4L, tagIds); - reviewService.create(productId, authorId, image, reviewCreateRequest); - - final var review = reviewRepository.findAll().get(0); - final var reviewId = review.getId(); - - // when & then - assertThatThrownBy(() -> reviewService.deleteReview(reviewId, memberId)) - .isInstanceOf(NotAuthorOfReviewException.class); - } - } - - @Nested - class getMostFavoriteReview_성공_테스트 { - - @Test - void 리뷰가_여러개_존재하면_좋아요를_가장_많이_받은_리뷰를_반환한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - final var productId = 단일_상품_저장(product); - - final var review1 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 351L); - final var review2 = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 24L); - 복수_리뷰_저장(review1, review2); - - final var expected = MostFavoriteReviewResponse.toResponse(Optional.of(review1)); - - // when - final var actual = reviewService.getMostFavoriteReview(productId); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 좋아요_수가_같은_리뷰가_여러개_존재하면_가장_최근_작성된_리뷰를_반환한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - final var productId = 단일_상품_저장(product); - - final var review1 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 351L); - final var review2 = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 351L); - 복수_리뷰_저장(review1, review2); - - final var expected = MostFavoriteReviewResponse.toResponse(Optional.of(review2)); - - // when - final var actual = reviewService.getMostFavoriteReview(productId); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 리뷰가_존재하지_않으면_Optional_empty를_반환한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점3점_생성(category); - final var productId = 단일_상품_저장(product); - - final var expected = Optional.empty(); - - // when - final var actual = reviewService.getMostFavoriteReview(productId); - - // then - assertThat(actual).isEqualTo(expected); - } - } - - @Nested - class getMostFavoriteReview_실패_테스트 { - - @Test - void 존재하지_않는_상품에_가장_많은_좋아요를_받은_리뷰를_찾으면_예외가_발생한다() { - // given - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var wrongProductId = 단일_상품_저장(product) + 1L; - - // when & then - assertThatThrownBy(() -> reviewService.getMostFavoriteReview(wrongProductId)) - .isInstanceOf(ProductNotFoundException.class); - } - } - - @Nested - class getReviewDetail_성공_테스트 { - - @Test - void 리뷰_상세_정보를_조회한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점5점_생성(category); - 단일_상품_저장(product); - - final var review = 리뷰_이미지test2_평점2점_재구매X_생성(member, product, 0L); - 단일_리뷰_저장(review); - - // when - final var actual = reviewService.getReviewDetail(review.getId()); - - // then - final var expected = ReviewDetailResponse.toResponse(review); - - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class getReviewDetail_실패_테스트 { - - @Test - void 존재하지_않는_리뷰를_조회할때_예외가_발생한다() { - // given - final var notExistReviewId = 999999L; - - // when & then - assertThatThrownBy(() -> reviewService.getReviewDetail(notExistReviewId)) - .isInstanceOf(ReviewNotFoundException.class); - } - } - - @Nested - class getTopReviews_성공_테스트 { - - @Nested - class 리뷰_개수에_대한_테스트 { - - @Test - void 전체_리뷰가_하나도_없어도_반환값은_있어야한다() { - // given - final var expected = RankingReviewsResponse.toResponse(Collections.emptyList()); - - // when - final var actual = reviewService.getTopReviews(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 전체_리뷰가_1개_이상_3개_미만이라도_리뷰가_나와야한다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점4점_생성(category); - 단일_상품_저장(product); - - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var now = LocalDateTime.now(); - final var review1 = 리뷰_이미지test5_평점5점_재구매X_생성(member, product, 2L, now.minusDays(1L)); - final var review2 = 리뷰_이미지test5_평점5점_재구매X_생성(member, product, 2L, now); - 복수_리뷰_저장(review1, review2); - - final var rankingReviewDto1 = RankingReviewDto.toDto(review1); - final var rankingReviewDto2 = RankingReviewDto.toDto(review2); - final var rankingReviewDtos = List.of(rankingReviewDto2, rankingReviewDto1); - final var expected = RankingReviewsResponse.toResponse(rankingReviewDtos); - - // when - final var actual = reviewService.getTopReviews(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 전체_리뷰_중_랭킹이_높은_상위_3개_리뷰를_구할_수_있다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점4점_생성(category); - 단일_상품_저장(product); - - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var now = LocalDateTime.now(); - final var review1 = 리뷰_이미지test5_평점5점_재구매X_생성(member, product, 4L, now.minusDays(3L)); - final var review2 = 리뷰_이미지test5_평점5점_재구매X_생성(member, product, 6L, now.minusDays(2L)); - final var review3 = 리뷰_이미지test5_평점5점_재구매X_생성(member, product, 4L, now); - final var review4 = 리뷰_이미지test5_평점5점_재구매X_생성(member, product, 5L, now); - 복수_리뷰_저장(review1, review2, review3, review4); - - final var rankingReviewDto1 = RankingReviewDto.toDto(review1); - final var rankingReviewDto2 = RankingReviewDto.toDto(review2); - final var rankingReviewDto3 = RankingReviewDto.toDto(review3); - final var rankingReviewDto4 = RankingReviewDto.toDto(review4); - final var rankingReviewDtos = List.of(rankingReviewDto4, rankingReviewDto3, rankingReviewDto2); - final var expected = RankingReviewsResponse.toResponse(rankingReviewDtos); - - // when - final var actual = reviewService.getTopReviews(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class 리뷰_랭킹_점수에_대한_테스트 { - - @Test - void 리뷰_좋아요_수가_같으면_최근_생성된_리뷰의_랭킹을_더_높게_반환한다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점4점_생성(category); - 단일_상품_저장(product); - - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var now = LocalDateTime.now(); - final var review1 = 리뷰_이미지test5_평점5점_재구매X_생성(member, product, 10L, now.minusDays(9L)); - final var review2 = 리뷰_이미지test5_평점5점_재구매X_생성(member, product, 10L, now.minusDays(4L)); - 복수_리뷰_저장(review1, review2); - - final var rankingReviewDto1 = RankingReviewDto.toDto(review1); - final var rankingReviewDto2 = RankingReviewDto.toDto(review2); - final var rankingReviewDtos = List.of(rankingReviewDto2, rankingReviewDto1); - final var expected = RankingReviewsResponse.toResponse(rankingReviewDtos); - - // when - final var actual = reviewService.getTopReviews(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 리뷰_생성_일자가_같으면_좋아요_수가_많은_리뷰의_랭킹을_더_높게_반환한다() { - // given - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점4점_생성(category); - 단일_상품_저장(product); - - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var now = LocalDateTime.now(); - final var review1 = 리뷰_이미지test5_평점5점_재구매X_생성(member, product, 2L, now.minusDays(1L)); - final var review2 = 리뷰_이미지test5_평점5점_재구매X_생성(member, product, 4L, now.minusDays(1L)); - 복수_리뷰_저장(review1, review2); - - final var rankingReviewDto1 = RankingReviewDto.toDto(review1); - final var rankingReviewDto2 = RankingReviewDto.toDto(review2); - final var rankingReviewDtos = List.of(rankingReviewDto2, rankingReviewDto1); - final var expected = RankingReviewsResponse.toResponse(rankingReviewDtos); - - // when - final var actual = reviewService.getTopReviews(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - } - - private List 태그_아이디_변환(final Tag... tags) { - return Stream.of(tags) - .map(Tag::getId) - .collect(Collectors.toList()); - } -} diff --git a/backend/src/test/java/com/funeat/review/domain/ReviewTest.java b/backend/src/test/java/com/funeat/review/domain/ReviewTest.java deleted file mode 100644 index a9b02876b..000000000 --- a/backend/src/test/java/com/funeat/review/domain/ReviewTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.funeat.review.domain; - -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점1점_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test5_평점5점_재구매X_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.LocalDateTime; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -class ReviewTest { - - @Nested - class calculateRankingScore_성공_테스트 { - - @Test - void 리뷰_좋아요_수와_리뷰_생성_시간으로_해당_리뷰의_랭킹_점수를_구할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - final var category = 카테고리_간편식사_생성(); - final var product = 상품_삼각김밥_가격1000원_평점1점_생성(category); - final var favoriteCount = 4L; - final var review = 리뷰_이미지test5_평점5점_재구매X_생성(member, product, favoriteCount, LocalDateTime.now().minusDays(1L)); - - final var expected = favoriteCount / Math.pow(2.0, 0.5); - - // when - final var actual = review.calculateRankingScore(); - - // then - assertThat(actual).isEqualTo(expected); - } - } -} diff --git a/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java b/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java deleted file mode 100644 index 46e4645f4..000000000 --- a/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java +++ /dev/null @@ -1,221 +0,0 @@ -package com.funeat.review.persistence; - -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버2_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버3_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점2점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점3점_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test1_평점1점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test3_평점3점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test3_평점3점_재구매X_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test4_평점4점_재구매O_생성; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -import com.funeat.common.RepositoryTest; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.data.domain.PageRequest; - -@SuppressWarnings("NonAsciiCharacters") -class ReviewRepositoryTest extends RepositoryTest { - - @Nested - class countByProduct_성공_테스트 { - - @Test - void 상품의_리뷰_수를_반환한다() { - // given - final var member1 = 멤버_멤버1_생성(); - final var member2 = 멤버_멤버2_생성(); - final var member3 = 멤버_멤버3_생성(); - 복수_멤버_저장(member1, member2, member3); - - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product1 = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var product2 = 상품_삼각김밥_가격2000원_평점3점_생성(category); - 복수_상품_저장(product1, product2); - - final var review1_1 = 리뷰_이미지test4_평점4점_재구매O_생성(member1, product1, 0L); - final var review1_2 = 리뷰_이미지test3_평점3점_재구매X_생성(member2, product1, 0L); - final var review1_3 = 리뷰_이미지test4_평점4점_재구매O_생성(member3, product1, 0L); - final var review2_1 = 리뷰_이미지test3_평점3점_재구매O_생성(member1, product2, 0L); - 복수_리뷰_저장(review1_1, review1_2, review1_3, review2_1); - - // when - final var actual1 = reviewRepository.countByProduct(product1); - final var actual2 = reviewRepository.countByProduct(product2); - - // then - assertSoftly(soft -> { - soft.assertThat(actual1) - .isEqualTo(3); - soft.assertThat(actual2) - .isEqualTo(1); - }); - } - } - - @Nested - class findPopularReviewWithImage_성공_테스트 { - - @Test - void 리뷰가_존재하지_않으면_빈_값을_반환하다() { - // given - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점1점_생성(category); - final var productId = 단일_상품_저장(product); - - final var pageable = PageRequest.of(0, 1); - - // when - final var actual = reviewRepository.findPopularReviewWithImage(productId, pageable); - - // then - assertThat(actual).isEmpty(); - } - - @Test - void 리뷰가_존재하면_좋아요_수가_몇개이든_리뷰를_반환하다() { - // given - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var review1 = 리뷰_이미지test1_평점1점_재구매O_생성(member, product, 2L); - final var review2 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 1L); - 복수_리뷰_저장(review1, review2); - - final var pageable = PageRequest.of(0, 1); - - // when - final var actual = reviewRepository.findPopularReviewWithImage(productId, pageable).get(0); - - // then - assertThat(actual).usingRecursiveComparison().isEqualTo(review1); - } - - @Test - void 좋아요_수가_같으면_최신_리뷰를_반환하다() { - // given - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var review1 = 리뷰_이미지test1_평점1점_재구매O_생성(member, product, 0L); - final var review2 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 0L); - 복수_리뷰_저장(review1, review2); - - final var pageable = PageRequest.of(0, 1); - - // when - final var actual = reviewRepository.findPopularReviewWithImage(productId, pageable).get(0); - - // then - assertThat(actual).usingRecursiveComparison().isEqualTo(review2); - } - } - - @Nested - class findTopByProductOrderByFavoriteCountDescIdDesc_성공_테스트 { - - @Test - void 좋아요가_가장_많은_리뷰를_반환하다() { - // given - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var review1 = 리뷰_이미지test1_평점1점_재구매O_생성(member, product, 0L); - final var review2 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 4L); - 복수_리뷰_저장(review1, review2); - - // when - final var actual = reviewRepository.findTopByProductOrderByFavoriteCountDescIdDesc(product); - - // then - assertThat(actual.get()).isEqualTo(review2); - } - } - - @Nested - class findReviewsByFavoriteCountGreaterThanEqual_성공_테스트 { - - @Test - void 특정_좋아요_수_이상인_모든_리뷰들을_조회한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var review1 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 1L); - final var review2 = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 0L); - final var review3 = 리뷰_이미지test3_평점3점_재구매X_생성(member, product, 100L); - 복수_리뷰_저장(review1, review2, review3); - - final var expected = List.of(review1, review3); - - // when - final var actual = reviewRepository.findReviewsByFavoriteCountGreaterThanEqual(1L); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 특정_좋아요_수_이상인_리뷰가_없으면_빈_리스트를_반환한다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_간편식사_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격1000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var review1 = 리뷰_이미지test3_평점3점_재구매O_생성(member, product, 0L); - final var review2 = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 0L); - 복수_리뷰_저장(review1, review2); - - final var expected = Collections.emptyList(); - - // when - final var actual = reviewRepository.findReviewsByFavoriteCountGreaterThanEqual(1L); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } -} diff --git a/backend/src/test/java/com/funeat/review/persistence/ReviewTagRepositoryTest.java b/backend/src/test/java/com/funeat/review/persistence/ReviewTagRepositoryTest.java deleted file mode 100644 index 4f5bbf152..000000000 --- a/backend/src/test/java/com/funeat/review/persistence/ReviewTagRepositoryTest.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.funeat.review.persistence; - -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.PageFixture.페이지요청_기본_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격3000원_평점2점_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test3_평점3점_재구매X_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test4_평점4점_재구매O_생성; -import static com.funeat.fixture.ReviewFixture.리뷰_이미지test5_평점5점_재구매O_생성; -import static com.funeat.fixture.TagFixture.태그_간식_ETC_생성; -import static com.funeat.fixture.TagFixture.태그_갓성비_PRICE_생성; -import static com.funeat.fixture.TagFixture.태그_단짠단짠_TASTE_생성; -import static com.funeat.fixture.TagFixture.태그_맛있어요_TASTE_생성; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.common.RepositoryTest; -import com.funeat.review.domain.Review; -import com.funeat.review.domain.ReviewTag; -import com.funeat.tag.domain.Tag; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -class ReviewTagRepositoryTest extends RepositoryTest { - - @Nested - class findTop3TagsByReviewIn_성공_테스트 { - - @Test - void 리뷰_목록에서_상위_3개에_해당하는_태그를_조회한다() { - - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격3000원_평점2점_생성(category); - final var productId = 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_단짠단짠_TASTE_생성(); - final var tag3 = 태그_갓성비_PRICE_생성(); - final var tag4 = 태그_간식_ETC_생성(); - 복수_태그_저장(tag1, tag2, tag3, tag4); - - final var review1 = 리뷰_이미지test5_평점5점_재구매O_생성(member, product, 0L); - final var review2 = 리뷰_이미지test3_평점3점_재구매X_생성(member, product, 0L); - final var review3 = 리뷰_이미지test4_평점4점_재구매O_생성(member, product, 0L); - 복수_리뷰_저장(review1, review2, review3); - - final var reviewTag1_1 = 리뷰_태그_생성(review1, tag1); - final var reviewTag1_2 = 리뷰_태그_생성(review1, tag2); - final var reviewTag2_1 = 리뷰_태그_생성(review2, tag1); - final var reviewTag2_2 = 리뷰_태그_생성(review2, tag2); - final var reviewTag2_3 = 리뷰_태그_생성(review2, tag3); - final var reviewTag3_1 = 리뷰_태그_생성(review3, tag1); - 복수_리뷰_태그_저장(reviewTag1_1, reviewTag1_2, reviewTag2_1, reviewTag2_2, reviewTag2_3, reviewTag3_1); - - final var page = 페이지요청_기본_생성(0, 3); - - final var expected = List.of(tag1, tag2, tag3); - - // when - final var top3Tags = reviewTagRepository.findTop3TagsByReviewIn(productId, page); - - // then - assertThat(top3Tags).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class deleteByReview_성공_테스트 { - - @Test - void 해당_리뷰에_달린_태그를_삭제할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격3000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - 단일_태그_저장(tag1); - - final var review1 = 리뷰_이미지test5_평점5점_재구매O_생성(member, product, 0L); - final var review2 = 리뷰_이미지test3_평점3점_재구매X_생성(member, product, 0L); - 복수_리뷰_저장(review1, review2); - - final var reviewTag1_1 = 리뷰_태그_생성(review1, tag1); - final var reviewTag2_1 = 리뷰_태그_생성(review2, tag1); - 복수_리뷰_태그_저장(reviewTag1_1, reviewTag2_1); - - final var expected = List.of(reviewTag2_1); - - // when - reviewTagRepository.deleteByReview(review1); - - // then - final var remainings = reviewTagRepository.findAll(); - assertThat(remainings).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class findByReview_성공_테스트 { - - @Test - void 해당_리뷰에_달린_태그를_확인할_수_있다() { - // given - final var member = 멤버_멤버1_생성(); - 단일_멤버_저장(member); - - final var category = 카테고리_즉석조리_생성(); - 단일_카테고리_저장(category); - - final var product = 상품_삼각김밥_가격3000원_평점2점_생성(category); - 단일_상품_저장(product); - - final var tag1 = 태그_맛있어요_TASTE_생성(); - 단일_태그_저장(tag1); - - final var review = 리뷰_이미지test5_평점5점_재구매O_생성(member, product, 0L); - 단일_리뷰_저장(review); - - final var reviewTag = 리뷰_태그_생성(review, tag1); - 단일_리뷰_태그_저장(reviewTag); - - final var expected = List.of(reviewTag); - - // when - final var actual = reviewTagRepository.findByReview(review); - - // then - assertThat(actual).usingRecursiveComparison() - .ignoringExpectedNullFields() - .isEqualTo(expected); - } - } - - private ReviewTag 리뷰_태그_생성(final Review review, final Tag tag) { - return ReviewTag.createReviewTag(review, tag); - } -} diff --git a/backend/src/test/java/com/funeat/tag/persistence/TagRepositoryTest.java b/backend/src/test/java/com/funeat/tag/persistence/TagRepositoryTest.java deleted file mode 100644 index 2b6dc178b..000000000 --- a/backend/src/test/java/com/funeat/tag/persistence/TagRepositoryTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.funeat.tag.persistence; - -import static com.funeat.fixture.TagFixture.태그_갓성비_PRICE_생성; -import static com.funeat.fixture.TagFixture.태그_맛있어요_TASTE_생성; -import static com.funeat.tag.domain.TagType.TASTE; -import static org.assertj.core.api.Assertions.assertThat; - -import com.funeat.common.RepositoryTest; -import com.funeat.tag.domain.Tag; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -class TagRepositoryTest extends RepositoryTest { - - @Nested - class findTagsByIdIn_성공_테스트 { - - @Test - void 여러_태그_아이디로_태그들을_조회_할_수_있다() { - // given - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_갓성비_PRICE_생성(); - 복수_태그_저장(tag1, tag2); - - final var tagIds = 태그_아이디_변환(tag1, tag2); - - final var expected = List.of(tag1, tag2); - - // then - final var actual = tagRepository.findTagsByIdIn(tagIds); - - // when - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - @Nested - class findTagsByTagType_성공_테스트 { - - @Test - void 태그_타입으로_태그들을_조회할_수_있다() { - // given - final var tag1 = 태그_맛있어요_TASTE_생성(); - final var tag2 = 태그_갓성비_PRICE_생성(); - 복수_태그_저장(tag1, tag2); - - final var expected = List.of(tag1); - - // when - final var actual = tagRepository.findTagsByTagType(TASTE); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - } - - private List 태그_아이디_변환(final Tag... tags) { - return Stream.of(tags) - .map(Tag::getId) - .collect(Collectors.toList()); - } -} diff --git a/backend/src/test/resources/application.yml b/backend/src/test/resources/application.yml deleted file mode 100644 index 5d2dd0387..000000000 --- a/backend/src/test/resources/application.yml +++ /dev/null @@ -1,45 +0,0 @@ -spring: - profiles: - active: test - - datasource: - driver-class-name: org.h2.Driver - url: jdbc:h2:mem:test${random.uuid};MODE=MySQL - username: sa - - jpa: - hibernate: - ddl-auto: create - properties: - hibernate: - format_sql: true - show_sql: true - - session: - store-type: none - -logging: - level: - org.hibernate.type.descriptor.sql: trace - -cloud: - aws: - region: - static: testRegion - s3: - bucket: testBucket - folder: testFolder - cloudfrontPath: testCloudfrontPath - image: - food: foodimage - store: storeimage - -back-office: - id: test - key: test - -server: - servlet: - session: - cookie: - name: SESSION diff --git a/frontend/jest.config.js b/jest.config.js similarity index 100% rename from frontend/jest.config.js rename to jest.config.js diff --git a/frontend/package.json b/package.json similarity index 97% rename from frontend/package.json rename to package.json index 335158204..d448e7cfb 100644 --- a/frontend/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build": "webpack --config webpack.prod.js", "build-dev": "webpack --config webpack.dev.js", "storybook": "storybook dev -p 6006", - "lint:styled": "stylelint './src/**/*.tsx' --fix", + "lint:styled": "stylelint 'src/**/*.tsx' --fix", "test": "jest", "test:coverage": "jest --watchAll --coverage" }, diff --git a/frontend/public/assets/apple-icon-180x180.png b/public/assets/apple-icon-180x180.png similarity index 100% rename from frontend/public/assets/apple-icon-180x180.png rename to public/assets/apple-icon-180x180.png diff --git a/frontend/public/assets/favicon-16x16.png b/public/assets/favicon-16x16.png similarity index 100% rename from frontend/public/assets/favicon-16x16.png rename to public/assets/favicon-16x16.png diff --git a/frontend/public/assets/favicon-32x32.png b/public/assets/favicon-32x32.png similarity index 100% rename from frontend/public/assets/favicon-32x32.png rename to public/assets/favicon-32x32.png diff --git a/frontend/public/assets/favicon.ico b/public/assets/favicon.ico similarity index 100% rename from frontend/public/assets/favicon.ico rename to public/assets/favicon.ico diff --git a/frontend/public/assets/og-image.png b/public/assets/og-image.png similarity index 100% rename from frontend/public/assets/og-image.png rename to public/assets/og-image.png diff --git a/frontend/public/index.html b/public/index.html similarity index 100% rename from frontend/public/index.html rename to public/index.html diff --git a/frontend/public/manifest.json b/public/manifest.json similarity index 100% rename from frontend/public/manifest.json rename to public/manifest.json diff --git a/frontend/public/mockServiceWorker.js b/public/mockServiceWorker.js similarity index 100% rename from frontend/public/mockServiceWorker.js rename to public/mockServiceWorker.js diff --git a/frontend/public/robots.txt b/public/robots.txt similarity index 100% rename from frontend/public/robots.txt rename to public/robots.txt diff --git a/frontend/public/sitemap.xml b/public/sitemap.xml similarity index 100% rename from frontend/public/sitemap.xml rename to public/sitemap.xml diff --git a/frontend/src/apis/ApiClient.ts b/src/apis/ApiClient.ts similarity index 100% rename from frontend/src/apis/ApiClient.ts rename to src/apis/ApiClient.ts diff --git a/frontend/src/apis/fetch.ts b/src/apis/fetch.ts similarity index 100% rename from frontend/src/apis/fetch.ts rename to src/apis/fetch.ts diff --git a/frontend/src/apis/index.ts b/src/apis/index.ts similarity index 100% rename from frontend/src/apis/index.ts rename to src/apis/index.ts diff --git a/frontend/src/assets/characters.svg b/src/assets/characters.svg similarity index 100% rename from frontend/src/assets/characters.svg rename to src/assets/characters.svg diff --git a/frontend/src/assets/logo.svg b/src/assets/logo.svg similarity index 100% rename from frontend/src/assets/logo.svg rename to src/assets/logo.svg diff --git a/frontend/src/assets/plate.svg b/src/assets/plate.svg similarity index 100% rename from frontend/src/assets/plate.svg rename to src/assets/plate.svg diff --git a/frontend/src/assets/samgakgimbab.svg b/src/assets/samgakgimbab.svg similarity index 100% rename from frontend/src/assets/samgakgimbab.svg rename to src/assets/samgakgimbab.svg diff --git a/frontend/src/components/Common/Banner/Banner.tsx b/src/components/Common/Banner/Banner.tsx similarity index 100% rename from frontend/src/components/Common/Banner/Banner.tsx rename to src/components/Common/Banner/Banner.tsx diff --git a/frontend/src/components/Common/CategoryFoodList/CategoryFoodList.stories.tsx b/src/components/Common/CategoryFoodList/CategoryFoodList.stories.tsx similarity index 100% rename from frontend/src/components/Common/CategoryFoodList/CategoryFoodList.stories.tsx rename to src/components/Common/CategoryFoodList/CategoryFoodList.stories.tsx diff --git a/frontend/src/components/Common/CategoryFoodList/CategoryFoodList.tsx b/src/components/Common/CategoryFoodList/CategoryFoodList.tsx similarity index 100% rename from frontend/src/components/Common/CategoryFoodList/CategoryFoodList.tsx rename to src/components/Common/CategoryFoodList/CategoryFoodList.tsx diff --git a/frontend/src/components/Common/CategoryFoodTab/CategoryFoodTab.stories.tsx b/src/components/Common/CategoryFoodTab/CategoryFoodTab.stories.tsx similarity index 100% rename from frontend/src/components/Common/CategoryFoodTab/CategoryFoodTab.stories.tsx rename to src/components/Common/CategoryFoodTab/CategoryFoodTab.stories.tsx diff --git a/frontend/src/components/Common/CategoryFoodTab/CategoryFoodTab.tsx b/src/components/Common/CategoryFoodTab/CategoryFoodTab.tsx similarity index 100% rename from frontend/src/components/Common/CategoryFoodTab/CategoryFoodTab.tsx rename to src/components/Common/CategoryFoodTab/CategoryFoodTab.tsx diff --git a/frontend/src/components/Common/CategoryItem/CategoryItem.stories.tsx b/src/components/Common/CategoryItem/CategoryItem.stories.tsx similarity index 100% rename from frontend/src/components/Common/CategoryItem/CategoryItem.stories.tsx rename to src/components/Common/CategoryItem/CategoryItem.stories.tsx diff --git a/frontend/src/components/Common/CategoryItem/CategoryItem.tsx b/src/components/Common/CategoryItem/CategoryItem.tsx similarity index 100% rename from frontend/src/components/Common/CategoryItem/CategoryItem.tsx rename to src/components/Common/CategoryItem/CategoryItem.tsx diff --git a/frontend/src/components/Common/CategoryStoreList/CategoryStoreList.stories.tsx b/src/components/Common/CategoryStoreList/CategoryStoreList.stories.tsx similarity index 100% rename from frontend/src/components/Common/CategoryStoreList/CategoryStoreList.stories.tsx rename to src/components/Common/CategoryStoreList/CategoryStoreList.stories.tsx diff --git a/frontend/src/components/Common/CategoryStoreList/CategoryStoreList.tsx b/src/components/Common/CategoryStoreList/CategoryStoreList.tsx similarity index 100% rename from frontend/src/components/Common/CategoryStoreList/CategoryStoreList.tsx rename to src/components/Common/CategoryStoreList/CategoryStoreList.tsx diff --git a/frontend/src/components/Common/CategoryStoreTab/CategoryStoreTab.stories.tsx b/src/components/Common/CategoryStoreTab/CategoryStoreTab.stories.tsx similarity index 100% rename from frontend/src/components/Common/CategoryStoreTab/CategoryStoreTab.stories.tsx rename to src/components/Common/CategoryStoreTab/CategoryStoreTab.stories.tsx diff --git a/frontend/src/components/Common/CategoryStoreTab/CategoryStoreTab.tsx b/src/components/Common/CategoryStoreTab/CategoryStoreTab.tsx similarity index 100% rename from frontend/src/components/Common/CategoryStoreTab/CategoryStoreTab.tsx rename to src/components/Common/CategoryStoreTab/CategoryStoreTab.tsx diff --git a/frontend/src/components/Common/ErrorBoundary/ErrorBoundary.tsx b/src/components/Common/ErrorBoundary/ErrorBoundary.tsx similarity index 100% rename from frontend/src/components/Common/ErrorBoundary/ErrorBoundary.tsx rename to src/components/Common/ErrorBoundary/ErrorBoundary.tsx diff --git a/frontend/src/components/Common/ErrorComponent/ErrorComponent.stories.tsx b/src/components/Common/ErrorComponent/ErrorComponent.stories.tsx similarity index 100% rename from frontend/src/components/Common/ErrorComponent/ErrorComponent.stories.tsx rename to src/components/Common/ErrorComponent/ErrorComponent.stories.tsx diff --git a/frontend/src/components/Common/ErrorComponent/ErrorComponent.tsx b/src/components/Common/ErrorComponent/ErrorComponent.tsx similarity index 100% rename from frontend/src/components/Common/ErrorComponent/ErrorComponent.tsx rename to src/components/Common/ErrorComponent/ErrorComponent.tsx diff --git a/frontend/src/components/Common/Header/Header.stories.tsx b/src/components/Common/Header/Header.stories.tsx similarity index 100% rename from frontend/src/components/Common/Header/Header.stories.tsx rename to src/components/Common/Header/Header.stories.tsx diff --git a/frontend/src/components/Common/Header/Header.tsx b/src/components/Common/Header/Header.tsx similarity index 100% rename from frontend/src/components/Common/Header/Header.tsx rename to src/components/Common/Header/Header.tsx diff --git a/frontend/src/components/Common/ImageUploader/ImageUploader.stories.tsx b/src/components/Common/ImageUploader/ImageUploader.stories.tsx similarity index 100% rename from frontend/src/components/Common/ImageUploader/ImageUploader.stories.tsx rename to src/components/Common/ImageUploader/ImageUploader.stories.tsx diff --git a/frontend/src/components/Common/ImageUploader/ImageUploader.tsx b/src/components/Common/ImageUploader/ImageUploader.tsx similarity index 100% rename from frontend/src/components/Common/ImageUploader/ImageUploader.tsx rename to src/components/Common/ImageUploader/ImageUploader.tsx diff --git a/frontend/src/components/Common/Loading/Loading.stories.tsx b/src/components/Common/Loading/Loading.stories.tsx similarity index 100% rename from frontend/src/components/Common/Loading/Loading.stories.tsx rename to src/components/Common/Loading/Loading.stories.tsx diff --git a/frontend/src/components/Common/Loading/Loading.tsx b/src/components/Common/Loading/Loading.tsx similarity index 100% rename from frontend/src/components/Common/Loading/Loading.tsx rename to src/components/Common/Loading/Loading.tsx diff --git a/frontend/src/components/Common/MarkedText/MarkedText.tsx b/src/components/Common/MarkedText/MarkedText.tsx similarity index 100% rename from frontend/src/components/Common/MarkedText/MarkedText.tsx rename to src/components/Common/MarkedText/MarkedText.tsx diff --git a/frontend/src/components/Common/NavigableSectionTitle/NavigableSectionTitle.stories.tsx b/src/components/Common/NavigableSectionTitle/NavigableSectionTitle.stories.tsx similarity index 100% rename from frontend/src/components/Common/NavigableSectionTitle/NavigableSectionTitle.stories.tsx rename to src/components/Common/NavigableSectionTitle/NavigableSectionTitle.stories.tsx diff --git a/frontend/src/components/Common/NavigableSectionTitle/NavigableSectionTitle.tsx b/src/components/Common/NavigableSectionTitle/NavigableSectionTitle.tsx similarity index 100% rename from frontend/src/components/Common/NavigableSectionTitle/NavigableSectionTitle.tsx rename to src/components/Common/NavigableSectionTitle/NavigableSectionTitle.tsx diff --git a/frontend/src/components/Common/NavigationBar/NavigationBar.stories.tsx b/src/components/Common/NavigationBar/NavigationBar.stories.tsx similarity index 100% rename from frontend/src/components/Common/NavigationBar/NavigationBar.stories.tsx rename to src/components/Common/NavigationBar/NavigationBar.stories.tsx diff --git a/frontend/src/components/Common/NavigationBar/NavigationBar.tsx b/src/components/Common/NavigationBar/NavigationBar.tsx similarity index 100% rename from frontend/src/components/Common/NavigationBar/NavigationBar.tsx rename to src/components/Common/NavigationBar/NavigationBar.tsx diff --git a/frontend/src/components/Common/RegisterButton/RegisterButton.tsx b/src/components/Common/RegisterButton/RegisterButton.tsx similarity index 100% rename from frontend/src/components/Common/RegisterButton/RegisterButton.tsx rename to src/components/Common/RegisterButton/RegisterButton.tsx diff --git a/frontend/src/components/Common/ScrollButton/ScrollButton.stories.tsx b/src/components/Common/ScrollButton/ScrollButton.stories.tsx similarity index 100% rename from frontend/src/components/Common/ScrollButton/ScrollButton.stories.tsx rename to src/components/Common/ScrollButton/ScrollButton.stories.tsx diff --git a/frontend/src/components/Common/ScrollButton/ScrollButton.tsx b/src/components/Common/ScrollButton/ScrollButton.tsx similarity index 100% rename from frontend/src/components/Common/ScrollButton/ScrollButton.tsx rename to src/components/Common/ScrollButton/ScrollButton.tsx diff --git a/frontend/src/components/Common/SectionTitle/SectionTitle.stories.tsx b/src/components/Common/SectionTitle/SectionTitle.stories.tsx similarity index 100% rename from frontend/src/components/Common/SectionTitle/SectionTitle.stories.tsx rename to src/components/Common/SectionTitle/SectionTitle.stories.tsx diff --git a/frontend/src/components/Common/SectionTitle/SectionTitle.tsx b/src/components/Common/SectionTitle/SectionTitle.tsx similarity index 100% rename from frontend/src/components/Common/SectionTitle/SectionTitle.tsx rename to src/components/Common/SectionTitle/SectionTitle.tsx diff --git a/frontend/src/components/Common/SortButton/SortButton.stories.tsx b/src/components/Common/SortButton/SortButton.stories.tsx similarity index 100% rename from frontend/src/components/Common/SortButton/SortButton.stories.tsx rename to src/components/Common/SortButton/SortButton.stories.tsx diff --git a/frontend/src/components/Common/SortButton/SortButton.tsx b/src/components/Common/SortButton/SortButton.tsx similarity index 100% rename from frontend/src/components/Common/SortButton/SortButton.tsx rename to src/components/Common/SortButton/SortButton.tsx diff --git a/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx b/src/components/Common/SortOptionList/SortOptionList.stories.tsx similarity index 100% rename from frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx rename to src/components/Common/SortOptionList/SortOptionList.stories.tsx diff --git a/frontend/src/components/Common/SortOptionList/SortOptionList.tsx b/src/components/Common/SortOptionList/SortOptionList.tsx similarity index 100% rename from frontend/src/components/Common/SortOptionList/SortOptionList.tsx rename to src/components/Common/SortOptionList/SortOptionList.tsx diff --git a/frontend/src/components/Common/Svg/SvgIcon.stories.tsx b/src/components/Common/Svg/SvgIcon.stories.tsx similarity index 100% rename from frontend/src/components/Common/Svg/SvgIcon.stories.tsx rename to src/components/Common/Svg/SvgIcon.stories.tsx diff --git a/frontend/src/components/Common/Svg/SvgIcon.tsx b/src/components/Common/Svg/SvgIcon.tsx similarity index 100% rename from frontend/src/components/Common/Svg/SvgIcon.tsx rename to src/components/Common/Svg/SvgIcon.tsx diff --git a/frontend/src/components/Common/Svg/SvgSprite.tsx b/src/components/Common/Svg/SvgSprite.tsx similarity index 100% rename from frontend/src/components/Common/Svg/SvgSprite.tsx rename to src/components/Common/Svg/SvgSprite.tsx diff --git a/frontend/src/components/Common/TabMenu/TabMenu.stories.tsx b/src/components/Common/TabMenu/TabMenu.stories.tsx similarity index 100% rename from frontend/src/components/Common/TabMenu/TabMenu.stories.tsx rename to src/components/Common/TabMenu/TabMenu.stories.tsx diff --git a/frontend/src/components/Common/TabMenu/TabMenu.tsx b/src/components/Common/TabMenu/TabMenu.tsx similarity index 100% rename from frontend/src/components/Common/TabMenu/TabMenu.tsx rename to src/components/Common/TabMenu/TabMenu.tsx diff --git a/frontend/src/components/Common/TagList/TagList.stories.tsx b/src/components/Common/TagList/TagList.stories.tsx similarity index 100% rename from frontend/src/components/Common/TagList/TagList.stories.tsx rename to src/components/Common/TagList/TagList.stories.tsx diff --git a/frontend/src/components/Common/TagList/TagList.tsx b/src/components/Common/TagList/TagList.tsx similarity index 100% rename from frontend/src/components/Common/TagList/TagList.tsx rename to src/components/Common/TagList/TagList.tsx diff --git a/frontend/src/components/Common/index.ts b/src/components/Common/index.ts similarity index 100% rename from frontend/src/components/Common/index.ts rename to src/components/Common/index.ts diff --git a/frontend/src/components/Layout/AuthLayout.tsx b/src/components/Layout/AuthLayout.tsx similarity index 100% rename from frontend/src/components/Layout/AuthLayout.tsx rename to src/components/Layout/AuthLayout.tsx diff --git a/frontend/src/components/Layout/DefaultLayout.tsx b/src/components/Layout/DefaultLayout.tsx similarity index 100% rename from frontend/src/components/Layout/DefaultLayout.tsx rename to src/components/Layout/DefaultLayout.tsx diff --git a/frontend/src/components/Layout/HeaderOnlyLayout.tsx b/src/components/Layout/HeaderOnlyLayout.tsx similarity index 100% rename from frontend/src/components/Layout/HeaderOnlyLayout.tsx rename to src/components/Layout/HeaderOnlyLayout.tsx diff --git a/frontend/src/components/Layout/MinimalLayout.tsx b/src/components/Layout/MinimalLayout.tsx similarity index 100% rename from frontend/src/components/Layout/MinimalLayout.tsx rename to src/components/Layout/MinimalLayout.tsx diff --git a/frontend/src/components/Layout/SimpleHeaderLayout.tsx b/src/components/Layout/SimpleHeaderLayout.tsx similarity index 100% rename from frontend/src/components/Layout/SimpleHeaderLayout.tsx rename to src/components/Layout/SimpleHeaderLayout.tsx diff --git a/frontend/src/components/Layout/index.ts b/src/components/Layout/index.ts similarity index 100% rename from frontend/src/components/Layout/index.ts rename to src/components/Layout/index.ts diff --git a/frontend/src/components/Members/MemberModifyInput/MemberModifyInput.tsx b/src/components/Members/MemberModifyInput/MemberModifyInput.tsx similarity index 100% rename from frontend/src/components/Members/MemberModifyInput/MemberModifyInput.tsx rename to src/components/Members/MemberModifyInput/MemberModifyInput.tsx diff --git a/frontend/src/components/Members/MemberRecipeList/MemberRecipeList.stories.tsx b/src/components/Members/MemberRecipeList/MemberRecipeList.stories.tsx similarity index 100% rename from frontend/src/components/Members/MemberRecipeList/MemberRecipeList.stories.tsx rename to src/components/Members/MemberRecipeList/MemberRecipeList.stories.tsx diff --git a/frontend/src/components/Members/MemberRecipeList/MemberRecipeList.tsx b/src/components/Members/MemberRecipeList/MemberRecipeList.tsx similarity index 100% rename from frontend/src/components/Members/MemberRecipeList/MemberRecipeList.tsx rename to src/components/Members/MemberRecipeList/MemberRecipeList.tsx diff --git a/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.stories.tsx b/src/components/Members/MemberReviewItem/MemberReviewItem.stories.tsx similarity index 100% rename from frontend/src/components/Members/MemberReviewItem/MemberReviewItem.stories.tsx rename to src/components/Members/MemberReviewItem/MemberReviewItem.stories.tsx diff --git a/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.tsx b/src/components/Members/MemberReviewItem/MemberReviewItem.tsx similarity index 100% rename from frontend/src/components/Members/MemberReviewItem/MemberReviewItem.tsx rename to src/components/Members/MemberReviewItem/MemberReviewItem.tsx diff --git a/frontend/src/components/Members/MemberReviewList/MemberReviewList.stories.tsx b/src/components/Members/MemberReviewList/MemberReviewList.stories.tsx similarity index 100% rename from frontend/src/components/Members/MemberReviewList/MemberReviewList.stories.tsx rename to src/components/Members/MemberReviewList/MemberReviewList.stories.tsx diff --git a/frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx b/src/components/Members/MemberReviewList/MemberReviewList.tsx similarity index 100% rename from frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx rename to src/components/Members/MemberReviewList/MemberReviewList.tsx diff --git a/frontend/src/components/Members/MembersInfo/MembersInfo.tsx b/src/components/Members/MembersInfo/MembersInfo.tsx similarity index 100% rename from frontend/src/components/Members/MembersInfo/MembersInfo.tsx rename to src/components/Members/MembersInfo/MembersInfo.tsx diff --git a/frontend/src/components/Members/MembersInfo/MyPageInfo.stories.tsx b/src/components/Members/MembersInfo/MyPageInfo.stories.tsx similarity index 100% rename from frontend/src/components/Members/MembersInfo/MyPageInfo.stories.tsx rename to src/components/Members/MembersInfo/MyPageInfo.stories.tsx diff --git a/frontend/src/components/Members/index.ts b/src/components/Members/index.ts similarity index 100% rename from frontend/src/components/Members/index.ts rename to src/components/Members/index.ts diff --git a/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.stories.tsx b/src/components/Product/ProductDetailItem/ProductDetailItem.stories.tsx similarity index 100% rename from frontend/src/components/Product/ProductDetailItem/ProductDetailItem.stories.tsx rename to src/components/Product/ProductDetailItem/ProductDetailItem.stories.tsx diff --git a/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.tsx b/src/components/Product/ProductDetailItem/ProductDetailItem.tsx similarity index 100% rename from frontend/src/components/Product/ProductDetailItem/ProductDetailItem.tsx rename to src/components/Product/ProductDetailItem/ProductDetailItem.tsx diff --git a/frontend/src/components/Product/ProductItem/ProductItem.stories.tsx b/src/components/Product/ProductItem/ProductItem.stories.tsx similarity index 100% rename from frontend/src/components/Product/ProductItem/ProductItem.stories.tsx rename to src/components/Product/ProductItem/ProductItem.stories.tsx diff --git a/frontend/src/components/Product/ProductItem/ProductItem.tsx b/src/components/Product/ProductItem/ProductItem.tsx similarity index 100% rename from frontend/src/components/Product/ProductItem/ProductItem.tsx rename to src/components/Product/ProductItem/ProductItem.tsx diff --git a/frontend/src/components/Product/ProductList/ProductList.stories.tsx b/src/components/Product/ProductList/ProductList.stories.tsx similarity index 100% rename from frontend/src/components/Product/ProductList/ProductList.stories.tsx rename to src/components/Product/ProductList/ProductList.stories.tsx diff --git a/frontend/src/components/Product/ProductList/ProductList.tsx b/src/components/Product/ProductList/ProductList.tsx similarity index 100% rename from frontend/src/components/Product/ProductList/ProductList.tsx rename to src/components/Product/ProductList/ProductList.tsx diff --git a/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.stories.tsx b/src/components/Product/ProductOverviewItem/ProductOverviewItem.stories.tsx similarity index 100% rename from frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.stories.tsx rename to src/components/Product/ProductOverviewItem/ProductOverviewItem.stories.tsx diff --git a/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.tsx b/src/components/Product/ProductOverviewItem/ProductOverviewItem.tsx similarity index 100% rename from frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.tsx rename to src/components/Product/ProductOverviewItem/ProductOverviewItem.tsx diff --git a/frontend/src/components/Product/ProductRecipeList/ProductRecipeList.tsx b/src/components/Product/ProductRecipeList/ProductRecipeList.tsx similarity index 100% rename from frontend/src/components/Product/ProductRecipeList/ProductRecipeList.tsx rename to src/components/Product/ProductRecipeList/ProductRecipeList.tsx diff --git a/frontend/src/components/Product/ProductTitle/ProductTitle.stories.tsx b/src/components/Product/ProductTitle/ProductTitle.stories.tsx similarity index 100% rename from frontend/src/components/Product/ProductTitle/ProductTitle.stories.tsx rename to src/components/Product/ProductTitle/ProductTitle.stories.tsx diff --git a/frontend/src/components/Product/ProductTitle/ProductTitle.tsx b/src/components/Product/ProductTitle/ProductTitle.tsx similarity index 100% rename from frontend/src/components/Product/ProductTitle/ProductTitle.tsx rename to src/components/Product/ProductTitle/ProductTitle.tsx diff --git a/frontend/src/components/Product/index.ts b/src/components/Product/index.ts similarity index 100% rename from frontend/src/components/Product/index.ts rename to src/components/Product/index.ts diff --git a/frontend/src/components/Rank/ProductRankingList/ProductRankingList.stories.tsx b/src/components/Rank/ProductRankingList/ProductRankingList.stories.tsx similarity index 100% rename from frontend/src/components/Rank/ProductRankingList/ProductRankingList.stories.tsx rename to src/components/Rank/ProductRankingList/ProductRankingList.stories.tsx diff --git a/frontend/src/components/Rank/ProductRankingList/ProductRankingList.tsx b/src/components/Rank/ProductRankingList/ProductRankingList.tsx similarity index 100% rename from frontend/src/components/Rank/ProductRankingList/ProductRankingList.tsx rename to src/components/Rank/ProductRankingList/ProductRankingList.tsx diff --git a/frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.stories.tsx b/src/components/Rank/RecipeRankingItem/RecipeRankingItem.stories.tsx similarity index 100% rename from frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.stories.tsx rename to src/components/Rank/RecipeRankingItem/RecipeRankingItem.stories.tsx diff --git a/frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.tsx b/src/components/Rank/RecipeRankingItem/RecipeRankingItem.tsx similarity index 100% rename from frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.tsx rename to src/components/Rank/RecipeRankingItem/RecipeRankingItem.tsx diff --git a/frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.stories.tsx b/src/components/Rank/RecipeRankingList/RecipeRankingList.stories.tsx similarity index 100% rename from frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.stories.tsx rename to src/components/Rank/RecipeRankingList/RecipeRankingList.stories.tsx diff --git a/frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.tsx b/src/components/Rank/RecipeRankingList/RecipeRankingList.tsx similarity index 100% rename from frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.tsx rename to src/components/Rank/RecipeRankingList/RecipeRankingList.tsx diff --git a/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.stories.tsx b/src/components/Rank/ReviewRankingItem/ReviewRankingItem.stories.tsx similarity index 100% rename from frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.stories.tsx rename to src/components/Rank/ReviewRankingItem/ReviewRankingItem.stories.tsx diff --git a/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx b/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx similarity index 100% rename from frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx rename to src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx diff --git a/frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.stories.tsx b/src/components/Rank/ReviewRankingList/ReviewRankingList.stories.tsx similarity index 100% rename from frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.stories.tsx rename to src/components/Rank/ReviewRankingList/ReviewRankingList.stories.tsx diff --git a/frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.tsx b/src/components/Rank/ReviewRankingList/ReviewRankingList.tsx similarity index 100% rename from frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.tsx rename to src/components/Rank/ReviewRankingList/ReviewRankingList.tsx diff --git a/frontend/src/components/Rank/index.ts b/src/components/Rank/index.ts similarity index 79% rename from frontend/src/components/Rank/index.ts rename to src/components/Rank/index.ts index 1b39a108e..4c2b8262b 100644 --- a/frontend/src/components/Rank/index.ts +++ b/src/components/Rank/index.ts @@ -1,4 +1,4 @@ -export { default as ReviewRankingItem } from '../Rank/ReviewRankingItem/ReviewRankingItem'; +export { default as ReviewRankingItem } from './ReviewRankingItem/ReviewRankingItem'; export { default as ReviewRankingList } from './ReviewRankingList/ReviewRankingList'; export { default as ProductRankingList } from './ProductRankingList/ProductRankingList'; export { default as RecipeRankingItem } from './RecipeRankingItem/RecipeRankingItem'; diff --git a/frontend/src/components/Recipe/CommentForm/CommentForm.stories.tsx b/src/components/Recipe/CommentForm/CommentForm.stories.tsx similarity index 100% rename from frontend/src/components/Recipe/CommentForm/CommentForm.stories.tsx rename to src/components/Recipe/CommentForm/CommentForm.stories.tsx diff --git a/frontend/src/components/Recipe/CommentForm/CommentForm.tsx b/src/components/Recipe/CommentForm/CommentForm.tsx similarity index 100% rename from frontend/src/components/Recipe/CommentForm/CommentForm.tsx rename to src/components/Recipe/CommentForm/CommentForm.tsx diff --git a/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx b/src/components/Recipe/CommentItem/CommentItem.stories.tsx similarity index 100% rename from frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx rename to src/components/Recipe/CommentItem/CommentItem.stories.tsx diff --git a/frontend/src/components/Recipe/CommentItem/CommentItem.tsx b/src/components/Recipe/CommentItem/CommentItem.tsx similarity index 100% rename from frontend/src/components/Recipe/CommentItem/CommentItem.tsx rename to src/components/Recipe/CommentItem/CommentItem.tsx diff --git a/frontend/src/components/Recipe/CommentList/CommentList.stories.tsx b/src/components/Recipe/CommentList/CommentList.stories.tsx similarity index 100% rename from frontend/src/components/Recipe/CommentList/CommentList.stories.tsx rename to src/components/Recipe/CommentList/CommentList.stories.tsx diff --git a/frontend/src/components/Recipe/CommentList/CommentList.tsx b/src/components/Recipe/CommentList/CommentList.tsx similarity index 100% rename from frontend/src/components/Recipe/CommentList/CommentList.tsx rename to src/components/Recipe/CommentList/CommentList.tsx diff --git a/frontend/src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.stories.tsx b/src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.stories.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.stories.tsx rename to src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.stories.tsx diff --git a/frontend/src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.tsx b/src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.tsx rename to src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.tsx diff --git a/frontend/src/components/Recipe/RecipeFavoriteButton/RecipeFavoriteButton.tsx b/src/components/Recipe/RecipeFavoriteButton/RecipeFavoriteButton.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeFavoriteButton/RecipeFavoriteButton.tsx rename to src/components/Recipe/RecipeFavoriteButton/RecipeFavoriteButton.tsx diff --git a/frontend/src/components/Recipe/RecipeItem/RecipeItem.stories.tsx b/src/components/Recipe/RecipeItem/RecipeItem.stories.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeItem/RecipeItem.stories.tsx rename to src/components/Recipe/RecipeItem/RecipeItem.stories.tsx diff --git a/frontend/src/components/Recipe/RecipeItem/RecipeItem.tsx b/src/components/Recipe/RecipeItem/RecipeItem.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeItem/RecipeItem.tsx rename to src/components/Recipe/RecipeItem/RecipeItem.tsx diff --git a/frontend/src/components/Recipe/RecipeList/RecipeList.stories.tsx b/src/components/Recipe/RecipeList/RecipeList.stories.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeList/RecipeList.stories.tsx rename to src/components/Recipe/RecipeList/RecipeList.stories.tsx diff --git a/frontend/src/components/Recipe/RecipeList/RecipeList.tsx b/src/components/Recipe/RecipeList/RecipeList.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeList/RecipeList.tsx rename to src/components/Recipe/RecipeList/RecipeList.tsx diff --git a/frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.stories.tsx b/src/components/Recipe/RecipeNameInput/RecipeNameInput.stories.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.stories.tsx rename to src/components/Recipe/RecipeNameInput/RecipeNameInput.stories.tsx diff --git a/frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.tsx b/src/components/Recipe/RecipeNameInput/RecipeNameInput.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.tsx rename to src/components/Recipe/RecipeNameInput/RecipeNameInput.tsx diff --git a/frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.stories.tsx b/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.stories.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.stories.tsx rename to src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.stories.tsx diff --git a/frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.tsx b/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.tsx rename to src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.tsx diff --git a/frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.stories.tsx b/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.stories.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.stories.tsx rename to src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.stories.tsx diff --git a/frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.tsx b/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.tsx rename to src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.tsx diff --git a/frontend/src/components/Recipe/RecipeUsedProducts/SearchedProductList.tsx b/src/components/Recipe/RecipeUsedProducts/SearchedProductList.tsx similarity index 100% rename from frontend/src/components/Recipe/RecipeUsedProducts/SearchedProductList.tsx rename to src/components/Recipe/RecipeUsedProducts/SearchedProductList.tsx diff --git a/frontend/src/components/Recipe/index.ts b/src/components/Recipe/index.ts similarity index 100% rename from frontend/src/components/Recipe/index.ts rename to src/components/Recipe/index.ts diff --git a/frontend/src/components/Review/BestReviewItem/BestReviewItem.stories.tsx b/src/components/Review/BestReviewItem/BestReviewItem.stories.tsx similarity index 100% rename from frontend/src/components/Review/BestReviewItem/BestReviewItem.stories.tsx rename to src/components/Review/BestReviewItem/BestReviewItem.stories.tsx diff --git a/frontend/src/components/Review/BestReviewItem/BestReviewItem.tsx b/src/components/Review/BestReviewItem/BestReviewItem.tsx similarity index 100% rename from frontend/src/components/Review/BestReviewItem/BestReviewItem.tsx rename to src/components/Review/BestReviewItem/BestReviewItem.tsx diff --git a/frontend/src/components/Review/RebuyCheckbox/RebuyCheckbox.tsx b/src/components/Review/RebuyCheckbox/RebuyCheckbox.tsx similarity index 100% rename from frontend/src/components/Review/RebuyCheckbox/RebuyCheckbox.tsx rename to src/components/Review/RebuyCheckbox/RebuyCheckbox.tsx diff --git a/frontend/src/components/Review/ReviewFavoriteButton/ReviewFavoriteButton.tsx b/src/components/Review/ReviewFavoriteButton/ReviewFavoriteButton.tsx similarity index 100% rename from frontend/src/components/Review/ReviewFavoriteButton/ReviewFavoriteButton.tsx rename to src/components/Review/ReviewFavoriteButton/ReviewFavoriteButton.tsx diff --git a/frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx b/src/components/Review/ReviewItem/ReviewItem.stories.tsx similarity index 100% rename from frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx rename to src/components/Review/ReviewItem/ReviewItem.stories.tsx diff --git a/frontend/src/components/Review/ReviewItem/ReviewItem.tsx b/src/components/Review/ReviewItem/ReviewItem.tsx similarity index 100% rename from frontend/src/components/Review/ReviewItem/ReviewItem.tsx rename to src/components/Review/ReviewItem/ReviewItem.tsx diff --git a/frontend/src/components/Review/ReviewList/ReviewList.tsx b/src/components/Review/ReviewList/ReviewList.tsx similarity index 100% rename from frontend/src/components/Review/ReviewList/ReviewList.tsx rename to src/components/Review/ReviewList/ReviewList.tsx diff --git a/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.stories.tsx b/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.stories.tsx similarity index 100% rename from frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.stories.tsx rename to src/components/Review/ReviewRegisterForm/ReviewRegisterForm.stories.tsx diff --git a/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx b/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx similarity index 100% rename from frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx rename to src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx diff --git a/frontend/src/components/Review/ReviewTagItem/ReviewTagItem.stories.tsx b/src/components/Review/ReviewTagItem/ReviewTagItem.stories.tsx similarity index 100% rename from frontend/src/components/Review/ReviewTagItem/ReviewTagItem.stories.tsx rename to src/components/Review/ReviewTagItem/ReviewTagItem.stories.tsx diff --git a/frontend/src/components/Review/ReviewTagItem/ReviewTagItem.tsx b/src/components/Review/ReviewTagItem/ReviewTagItem.tsx similarity index 100% rename from frontend/src/components/Review/ReviewTagItem/ReviewTagItem.tsx rename to src/components/Review/ReviewTagItem/ReviewTagItem.tsx diff --git a/frontend/src/components/Review/ReviewTagList/ReviewTagList.stories.tsx b/src/components/Review/ReviewTagList/ReviewTagList.stories.tsx similarity index 100% rename from frontend/src/components/Review/ReviewTagList/ReviewTagList.stories.tsx rename to src/components/Review/ReviewTagList/ReviewTagList.stories.tsx diff --git a/frontend/src/components/Review/ReviewTagList/ReviewTagList.tsx b/src/components/Review/ReviewTagList/ReviewTagList.tsx similarity index 100% rename from frontend/src/components/Review/ReviewTagList/ReviewTagList.tsx rename to src/components/Review/ReviewTagList/ReviewTagList.tsx diff --git a/frontend/src/components/Review/ReviewTextarea/ReviewTextarea.stories.tsx b/src/components/Review/ReviewTextarea/ReviewTextarea.stories.tsx similarity index 100% rename from frontend/src/components/Review/ReviewTextarea/ReviewTextarea.stories.tsx rename to src/components/Review/ReviewTextarea/ReviewTextarea.stories.tsx diff --git a/frontend/src/components/Review/ReviewTextarea/ReviewTextarea.tsx b/src/components/Review/ReviewTextarea/ReviewTextarea.tsx similarity index 100% rename from frontend/src/components/Review/ReviewTextarea/ReviewTextarea.tsx rename to src/components/Review/ReviewTextarea/ReviewTextarea.tsx diff --git a/frontend/src/components/Review/StarRate/StarRate.stories.tsx b/src/components/Review/StarRate/StarRate.stories.tsx similarity index 100% rename from frontend/src/components/Review/StarRate/StarRate.stories.tsx rename to src/components/Review/StarRate/StarRate.stories.tsx diff --git a/frontend/src/components/Review/StarRate/StarRate.tsx b/src/components/Review/StarRate/StarRate.tsx similarity index 100% rename from frontend/src/components/Review/StarRate/StarRate.tsx rename to src/components/Review/StarRate/StarRate.tsx diff --git a/frontend/src/components/Review/index.ts b/src/components/Review/index.ts similarity index 100% rename from frontend/src/components/Review/index.ts rename to src/components/Review/index.ts diff --git a/frontend/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx similarity index 100% rename from frontend/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx rename to src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx diff --git a/frontend/src/components/Search/RecipeSearchResultList/RecipeSearchResultList.tsx b/src/components/Search/RecipeSearchResultList/RecipeSearchResultList.tsx similarity index 100% rename from frontend/src/components/Search/RecipeSearchResultList/RecipeSearchResultList.tsx rename to src/components/Search/RecipeSearchResultList/RecipeSearchResultList.tsx diff --git a/frontend/src/components/Search/RecommendList/RecommendList.tsx b/src/components/Search/RecommendList/RecommendList.tsx similarity index 100% rename from frontend/src/components/Search/RecommendList/RecommendList.tsx rename to src/components/Search/RecommendList/RecommendList.tsx diff --git a/frontend/src/components/Search/index.ts b/src/components/Search/index.ts similarity index 100% rename from frontend/src/components/Search/index.ts rename to src/components/Search/index.ts diff --git a/frontend/src/constants/index.ts b/src/constants/index.ts similarity index 100% rename from frontend/src/constants/index.ts rename to src/constants/index.ts diff --git a/frontend/src/constants/path.ts b/src/constants/path.ts similarity index 100% rename from frontend/src/constants/path.ts rename to src/constants/path.ts diff --git a/frontend/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx similarity index 100% rename from frontend/src/contexts/CategoryContext.tsx rename to src/contexts/CategoryContext.tsx diff --git a/frontend/src/contexts/RecipeFormContext.tsx b/src/contexts/RecipeFormContext.tsx similarity index 100% rename from frontend/src/contexts/RecipeFormContext.tsx rename to src/contexts/RecipeFormContext.tsx diff --git a/frontend/src/contexts/ReviewFormContext.tsx b/src/contexts/ReviewFormContext.tsx similarity index 100% rename from frontend/src/contexts/ReviewFormContext.tsx rename to src/contexts/ReviewFormContext.tsx diff --git a/frontend/src/hooks/common/index.ts b/src/hooks/common/index.ts similarity index 100% rename from frontend/src/hooks/common/index.ts rename to src/hooks/common/index.ts diff --git a/frontend/src/hooks/common/useDebounce.ts b/src/hooks/common/useDebounce.ts similarity index 100% rename from frontend/src/hooks/common/useDebounce.ts rename to src/hooks/common/useDebounce.ts diff --git a/frontend/src/hooks/common/useEnterKeyDown.ts b/src/hooks/common/useEnterKeyDown.ts similarity index 100% rename from frontend/src/hooks/common/useEnterKeyDown.ts rename to src/hooks/common/useEnterKeyDown.ts diff --git a/frontend/src/hooks/common/useFormData.ts b/src/hooks/common/useFormData.ts similarity index 100% rename from frontend/src/hooks/common/useFormData.ts rename to src/hooks/common/useFormData.ts diff --git a/frontend/src/hooks/common/useGA.ts b/src/hooks/common/useGA.ts similarity index 100% rename from frontend/src/hooks/common/useGA.ts rename to src/hooks/common/useGA.ts diff --git a/frontend/src/hooks/common/useImageUploader.ts b/src/hooks/common/useImageUploader.ts similarity index 100% rename from frontend/src/hooks/common/useImageUploader.ts rename to src/hooks/common/useImageUploader.ts diff --git a/frontend/src/hooks/common/useIntersectionObserver.ts b/src/hooks/common/useIntersectionObserver.ts similarity index 100% rename from frontend/src/hooks/common/useIntersectionObserver.ts rename to src/hooks/common/useIntersectionObserver.ts diff --git a/frontend/src/hooks/common/useRouteChangeTracker.ts b/src/hooks/common/useRouteChangeTracker.ts similarity index 100% rename from frontend/src/hooks/common/useRouteChangeTracker.ts rename to src/hooks/common/useRouteChangeTracker.ts diff --git a/frontend/src/hooks/common/useRoutePage.ts b/src/hooks/common/useRoutePage.ts similarity index 100% rename from frontend/src/hooks/common/useRoutePage.ts rename to src/hooks/common/useRoutePage.ts diff --git a/frontend/src/hooks/common/useScroll.ts b/src/hooks/common/useScroll.ts similarity index 100% rename from frontend/src/hooks/common/useScroll.ts rename to src/hooks/common/useScroll.ts diff --git a/frontend/src/hooks/common/useScrollRestoration.ts b/src/hooks/common/useScrollRestoration.ts similarity index 100% rename from frontend/src/hooks/common/useScrollRestoration.ts rename to src/hooks/common/useScrollRestoration.ts diff --git a/frontend/src/hooks/common/useSortOption.ts b/src/hooks/common/useSortOption.ts similarity index 100% rename from frontend/src/hooks/common/useSortOption.ts rename to src/hooks/common/useSortOption.ts diff --git a/frontend/src/hooks/common/useTabMenu.ts b/src/hooks/common/useTabMenu.ts similarity index 100% rename from frontend/src/hooks/common/useTabMenu.ts rename to src/hooks/common/useTabMenu.ts diff --git a/frontend/src/hooks/common/useTimeout.ts b/src/hooks/common/useTimeout.ts similarity index 100% rename from frontend/src/hooks/common/useTimeout.ts rename to src/hooks/common/useTimeout.ts diff --git a/frontend/src/hooks/context/index.ts b/src/hooks/context/index.ts similarity index 100% rename from frontend/src/hooks/context/index.ts rename to src/hooks/context/index.ts diff --git a/frontend/src/hooks/context/useCategoryActionContext.ts b/src/hooks/context/useCategoryActionContext.ts similarity index 100% rename from frontend/src/hooks/context/useCategoryActionContext.ts rename to src/hooks/context/useCategoryActionContext.ts diff --git a/frontend/src/hooks/context/useCategoryValueContext.ts b/src/hooks/context/useCategoryValueContext.ts similarity index 100% rename from frontend/src/hooks/context/useCategoryValueContext.ts rename to src/hooks/context/useCategoryValueContext.ts diff --git a/frontend/src/hooks/context/useRecipeFormActionContext.ts b/src/hooks/context/useRecipeFormActionContext.ts similarity index 100% rename from frontend/src/hooks/context/useRecipeFormActionContext.ts rename to src/hooks/context/useRecipeFormActionContext.ts diff --git a/frontend/src/hooks/context/useRecipeFormValueContext.ts b/src/hooks/context/useRecipeFormValueContext.ts similarity index 100% rename from frontend/src/hooks/context/useRecipeFormValueContext.ts rename to src/hooks/context/useRecipeFormValueContext.ts diff --git a/frontend/src/hooks/context/useReviewFormActionContext.ts b/src/hooks/context/useReviewFormActionContext.ts similarity index 100% rename from frontend/src/hooks/context/useReviewFormActionContext.ts rename to src/hooks/context/useReviewFormActionContext.ts diff --git a/frontend/src/hooks/context/useReviewFormValueContext.ts b/src/hooks/context/useReviewFormValueContext.ts similarity index 100% rename from frontend/src/hooks/context/useReviewFormValueContext.ts rename to src/hooks/context/useReviewFormValueContext.ts diff --git a/frontend/src/hooks/queries/banner/index.ts b/src/hooks/queries/banner/index.ts similarity index 100% rename from frontend/src/hooks/queries/banner/index.ts rename to src/hooks/queries/banner/index.ts diff --git a/frontend/src/hooks/queries/banner/useBannerQuery.ts b/src/hooks/queries/banner/useBannerQuery.ts similarity index 100% rename from frontend/src/hooks/queries/banner/useBannerQuery.ts rename to src/hooks/queries/banner/useBannerQuery.ts diff --git a/frontend/src/hooks/queries/index.ts b/src/hooks/queries/index.ts similarity index 100% rename from frontend/src/hooks/queries/index.ts rename to src/hooks/queries/index.ts diff --git a/frontend/src/hooks/queries/members/index.ts b/src/hooks/queries/members/index.ts similarity index 100% rename from frontend/src/hooks/queries/members/index.ts rename to src/hooks/queries/members/index.ts diff --git a/frontend/src/hooks/queries/members/useDeleteReview.ts b/src/hooks/queries/members/useDeleteReview.ts similarity index 100% rename from frontend/src/hooks/queries/members/useDeleteReview.ts rename to src/hooks/queries/members/useDeleteReview.ts diff --git a/frontend/src/hooks/queries/members/useInfiniteMemberRecipeQuery.ts b/src/hooks/queries/members/useInfiniteMemberRecipeQuery.ts similarity index 100% rename from frontend/src/hooks/queries/members/useInfiniteMemberRecipeQuery.ts rename to src/hooks/queries/members/useInfiniteMemberRecipeQuery.ts diff --git a/frontend/src/hooks/queries/members/useInfiniteMemberReviewQuery.ts b/src/hooks/queries/members/useInfiniteMemberReviewQuery.ts similarity index 100% rename from frontend/src/hooks/queries/members/useInfiniteMemberReviewQuery.ts rename to src/hooks/queries/members/useInfiniteMemberReviewQuery.ts diff --git a/frontend/src/hooks/queries/members/useLogoutMutation.ts b/src/hooks/queries/members/useLogoutMutation.ts similarity index 100% rename from frontend/src/hooks/queries/members/useLogoutMutation.ts rename to src/hooks/queries/members/useLogoutMutation.ts diff --git a/frontend/src/hooks/queries/members/useMemberModifyMutation.ts b/src/hooks/queries/members/useMemberModifyMutation.ts similarity index 100% rename from frontend/src/hooks/queries/members/useMemberModifyMutation.ts rename to src/hooks/queries/members/useMemberModifyMutation.ts diff --git a/frontend/src/hooks/queries/members/useMemberQuery.ts b/src/hooks/queries/members/useMemberQuery.ts similarity index 100% rename from frontend/src/hooks/queries/members/useMemberQuery.ts rename to src/hooks/queries/members/useMemberQuery.ts diff --git a/frontend/src/hooks/queries/product/index.ts b/src/hooks/queries/product/index.ts similarity index 100% rename from frontend/src/hooks/queries/product/index.ts rename to src/hooks/queries/product/index.ts diff --git a/frontend/src/hooks/queries/product/useCategoryQuery.ts b/src/hooks/queries/product/useCategoryQuery.ts similarity index 100% rename from frontend/src/hooks/queries/product/useCategoryQuery.ts rename to src/hooks/queries/product/useCategoryQuery.ts diff --git a/frontend/src/hooks/queries/product/useInfiniteProductRecipesQuery.ts b/src/hooks/queries/product/useInfiniteProductRecipesQuery.ts similarity index 100% rename from frontend/src/hooks/queries/product/useInfiniteProductRecipesQuery.ts rename to src/hooks/queries/product/useInfiniteProductRecipesQuery.ts diff --git a/frontend/src/hooks/queries/product/useInfiniteProductReviewsQuery.ts b/src/hooks/queries/product/useInfiniteProductReviewsQuery.ts similarity index 100% rename from frontend/src/hooks/queries/product/useInfiniteProductReviewsQuery.ts rename to src/hooks/queries/product/useInfiniteProductReviewsQuery.ts diff --git a/frontend/src/hooks/queries/product/useInfiniteProductsQuery.ts b/src/hooks/queries/product/useInfiniteProductsQuery.ts similarity index 100% rename from frontend/src/hooks/queries/product/useInfiniteProductsQuery.ts rename to src/hooks/queries/product/useInfiniteProductsQuery.ts diff --git a/frontend/src/hooks/queries/product/useProductDetailQuery.ts b/src/hooks/queries/product/useProductDetailQuery.ts similarity index 100% rename from frontend/src/hooks/queries/product/useProductDetailQuery.ts rename to src/hooks/queries/product/useProductDetailQuery.ts diff --git a/frontend/src/hooks/queries/rank/index.ts b/src/hooks/queries/rank/index.ts similarity index 100% rename from frontend/src/hooks/queries/rank/index.ts rename to src/hooks/queries/rank/index.ts diff --git a/frontend/src/hooks/queries/rank/useBestReviewQuery.ts b/src/hooks/queries/rank/useBestReviewQuery.ts similarity index 100% rename from frontend/src/hooks/queries/rank/useBestReviewQuery.ts rename to src/hooks/queries/rank/useBestReviewQuery.ts diff --git a/frontend/src/hooks/queries/rank/useProductRankingQuery.ts b/src/hooks/queries/rank/useProductRankingQuery.ts similarity index 100% rename from frontend/src/hooks/queries/rank/useProductRankingQuery.ts rename to src/hooks/queries/rank/useProductRankingQuery.ts diff --git a/frontend/src/hooks/queries/rank/useRecipeRankingQuery.ts b/src/hooks/queries/rank/useRecipeRankingQuery.ts similarity index 100% rename from frontend/src/hooks/queries/rank/useRecipeRankingQuery.ts rename to src/hooks/queries/rank/useRecipeRankingQuery.ts diff --git a/frontend/src/hooks/queries/rank/useReviewRankingQuery.ts b/src/hooks/queries/rank/useReviewRankingQuery.ts similarity index 100% rename from frontend/src/hooks/queries/rank/useReviewRankingQuery.ts rename to src/hooks/queries/rank/useReviewRankingQuery.ts diff --git a/frontend/src/hooks/queries/recipe/index.ts b/src/hooks/queries/recipe/index.ts similarity index 100% rename from frontend/src/hooks/queries/recipe/index.ts rename to src/hooks/queries/recipe/index.ts diff --git a/frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts b/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts similarity index 100% rename from frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts rename to src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts diff --git a/frontend/src/hooks/queries/recipe/useInfiniteRecipesQuery.ts b/src/hooks/queries/recipe/useInfiniteRecipesQuery.ts similarity index 100% rename from frontend/src/hooks/queries/recipe/useInfiniteRecipesQuery.ts rename to src/hooks/queries/recipe/useInfiniteRecipesQuery.ts diff --git a/frontend/src/hooks/queries/recipe/useRecipeCommentMutation.ts b/src/hooks/queries/recipe/useRecipeCommentMutation.ts similarity index 100% rename from frontend/src/hooks/queries/recipe/useRecipeCommentMutation.ts rename to src/hooks/queries/recipe/useRecipeCommentMutation.ts diff --git a/frontend/src/hooks/queries/recipe/useRecipeDetailQuery.ts b/src/hooks/queries/recipe/useRecipeDetailQuery.ts similarity index 100% rename from frontend/src/hooks/queries/recipe/useRecipeDetailQuery.ts rename to src/hooks/queries/recipe/useRecipeDetailQuery.ts diff --git a/frontend/src/hooks/queries/recipe/useRecipeFavoriteMutation.ts b/src/hooks/queries/recipe/useRecipeFavoriteMutation.ts similarity index 100% rename from frontend/src/hooks/queries/recipe/useRecipeFavoriteMutation.ts rename to src/hooks/queries/recipe/useRecipeFavoriteMutation.ts diff --git a/frontend/src/hooks/queries/recipe/useRecipeRegisterFormMutation.ts b/src/hooks/queries/recipe/useRecipeRegisterFormMutation.ts similarity index 100% rename from frontend/src/hooks/queries/recipe/useRecipeRegisterFormMutation.ts rename to src/hooks/queries/recipe/useRecipeRegisterFormMutation.ts diff --git a/frontend/src/hooks/queries/review/index.ts b/src/hooks/queries/review/index.ts similarity index 100% rename from frontend/src/hooks/queries/review/index.ts rename to src/hooks/queries/review/index.ts diff --git a/frontend/src/hooks/queries/review/useReviewDetailQuery.ts b/src/hooks/queries/review/useReviewDetailQuery.ts similarity index 100% rename from frontend/src/hooks/queries/review/useReviewDetailQuery.ts rename to src/hooks/queries/review/useReviewDetailQuery.ts diff --git a/frontend/src/hooks/queries/review/useReviewFavoriteMutation.ts b/src/hooks/queries/review/useReviewFavoriteMutation.ts similarity index 100% rename from frontend/src/hooks/queries/review/useReviewFavoriteMutation.ts rename to src/hooks/queries/review/useReviewFavoriteMutation.ts diff --git a/frontend/src/hooks/queries/review/useReviewRegisterFormMutation.ts b/src/hooks/queries/review/useReviewRegisterFormMutation.ts similarity index 100% rename from frontend/src/hooks/queries/review/useReviewRegisterFormMutation.ts rename to src/hooks/queries/review/useReviewRegisterFormMutation.ts diff --git a/frontend/src/hooks/queries/review/useReviewTagsQuery.ts b/src/hooks/queries/review/useReviewTagsQuery.ts similarity index 100% rename from frontend/src/hooks/queries/review/useReviewTagsQuery.ts rename to src/hooks/queries/review/useReviewTagsQuery.ts diff --git a/frontend/src/hooks/queries/search/index.ts b/src/hooks/queries/search/index.ts similarity index 100% rename from frontend/src/hooks/queries/search/index.ts rename to src/hooks/queries/search/index.ts diff --git a/frontend/src/hooks/queries/search/useInfiniteProductSearchAutocompleteQuery.ts b/src/hooks/queries/search/useInfiniteProductSearchAutocompleteQuery.ts similarity index 100% rename from frontend/src/hooks/queries/search/useInfiniteProductSearchAutocompleteQuery.ts rename to src/hooks/queries/search/useInfiniteProductSearchAutocompleteQuery.ts diff --git a/frontend/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts b/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts similarity index 100% rename from frontend/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts rename to src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts diff --git a/frontend/src/hooks/queries/search/useInfiniteRecipeSearchResultsQuery.ts b/src/hooks/queries/search/useInfiniteRecipeSearchResultsQuery.ts similarity index 100% rename from frontend/src/hooks/queries/search/useInfiniteRecipeSearchResultsQuery.ts rename to src/hooks/queries/search/useInfiniteRecipeSearchResultsQuery.ts diff --git a/frontend/src/hooks/queries/useSuspendedInfiniteQuery.ts b/src/hooks/queries/useSuspendedInfiniteQuery.ts similarity index 100% rename from frontend/src/hooks/queries/useSuspendedInfiniteQuery.ts rename to src/hooks/queries/useSuspendedInfiniteQuery.ts diff --git a/frontend/src/hooks/queries/useSuspendedQuery.ts b/src/hooks/queries/useSuspendedQuery.ts similarity index 100% rename from frontend/src/hooks/queries/useSuspendedQuery.ts rename to src/hooks/queries/useSuspendedQuery.ts diff --git a/frontend/src/hooks/review/index.ts b/src/hooks/review/index.ts similarity index 100% rename from frontend/src/hooks/review/index.ts rename to src/hooks/review/index.ts diff --git a/frontend/src/hooks/review/useDisplayTag.ts b/src/hooks/review/useDisplayTag.ts similarity index 100% rename from frontend/src/hooks/review/useDisplayTag.ts rename to src/hooks/review/useDisplayTag.ts diff --git a/frontend/src/hooks/review/useStarRatingHover.ts b/src/hooks/review/useStarRatingHover.ts similarity index 100% rename from frontend/src/hooks/review/useStarRatingHover.ts rename to src/hooks/review/useStarRatingHover.ts diff --git a/frontend/src/hooks/search/index.ts b/src/hooks/search/index.ts similarity index 100% rename from frontend/src/hooks/search/index.ts rename to src/hooks/search/index.ts diff --git a/frontend/src/hooks/search/useSearch.ts b/src/hooks/search/useSearch.ts similarity index 100% rename from frontend/src/hooks/search/useSearch.ts rename to src/hooks/search/useSearch.ts diff --git a/frontend/src/index.tsx b/src/index.tsx similarity index 100% rename from frontend/src/index.tsx rename to src/index.tsx diff --git a/frontend/src/mocks/browser.ts b/src/mocks/browser.ts similarity index 100% rename from frontend/src/mocks/browser.ts rename to src/mocks/browser.ts diff --git a/frontend/src/mocks/data/banners.json b/src/mocks/data/banners.json similarity index 100% rename from frontend/src/mocks/data/banners.json rename to src/mocks/data/banners.json diff --git a/frontend/src/mocks/data/comments.json b/src/mocks/data/comments.json similarity index 100% rename from frontend/src/mocks/data/comments.json rename to src/mocks/data/comments.json diff --git a/frontend/src/mocks/data/foodCategory.json b/src/mocks/data/foodCategory.json similarity index 100% rename from frontend/src/mocks/data/foodCategory.json rename to src/mocks/data/foodCategory.json diff --git a/frontend/src/mocks/data/memberRecipes.json b/src/mocks/data/memberRecipes.json similarity index 100% rename from frontend/src/mocks/data/memberRecipes.json rename to src/mocks/data/memberRecipes.json diff --git a/frontend/src/mocks/data/memberReviews.json b/src/mocks/data/memberReviews.json similarity index 100% rename from frontend/src/mocks/data/memberReviews.json rename to src/mocks/data/memberReviews.json diff --git a/frontend/src/mocks/data/members.json b/src/mocks/data/members.json similarity index 100% rename from frontend/src/mocks/data/members.json rename to src/mocks/data/members.json diff --git a/frontend/src/mocks/data/pbProducts.json b/src/mocks/data/pbProducts.json similarity index 100% rename from frontend/src/mocks/data/pbProducts.json rename to src/mocks/data/pbProducts.json diff --git a/frontend/src/mocks/data/productDetail.json b/src/mocks/data/productDetail.json similarity index 100% rename from frontend/src/mocks/data/productDetail.json rename to src/mocks/data/productDetail.json diff --git a/frontend/src/mocks/data/productDetails.json b/src/mocks/data/productDetails.json similarity index 100% rename from frontend/src/mocks/data/productDetails.json rename to src/mocks/data/productDetails.json diff --git a/frontend/src/mocks/data/productRankingList.json b/src/mocks/data/productRankingList.json similarity index 100% rename from frontend/src/mocks/data/productRankingList.json rename to src/mocks/data/productRankingList.json diff --git a/frontend/src/mocks/data/productSearchResults.json b/src/mocks/data/productSearchResults.json similarity index 100% rename from frontend/src/mocks/data/productSearchResults.json rename to src/mocks/data/productSearchResults.json diff --git a/frontend/src/mocks/data/products.json b/src/mocks/data/products.json similarity index 100% rename from frontend/src/mocks/data/products.json rename to src/mocks/data/products.json diff --git a/frontend/src/mocks/data/recipeDetail.json b/src/mocks/data/recipeDetail.json similarity index 100% rename from frontend/src/mocks/data/recipeDetail.json rename to src/mocks/data/recipeDetail.json diff --git a/frontend/src/mocks/data/recipeRankingList.json b/src/mocks/data/recipeRankingList.json similarity index 100% rename from frontend/src/mocks/data/recipeRankingList.json rename to src/mocks/data/recipeRankingList.json diff --git a/frontend/src/mocks/data/recipes.json b/src/mocks/data/recipes.json similarity index 100% rename from frontend/src/mocks/data/recipes.json rename to src/mocks/data/recipes.json diff --git a/frontend/src/mocks/data/reviewDetail.json b/src/mocks/data/reviewDetail.json similarity index 100% rename from frontend/src/mocks/data/reviewDetail.json rename to src/mocks/data/reviewDetail.json diff --git a/frontend/src/mocks/data/reviewRankingList.json b/src/mocks/data/reviewRankingList.json similarity index 100% rename from frontend/src/mocks/data/reviewRankingList.json rename to src/mocks/data/reviewRankingList.json diff --git a/frontend/src/mocks/data/reviewTagList.json b/src/mocks/data/reviewTagList.json similarity index 100% rename from frontend/src/mocks/data/reviewTagList.json rename to src/mocks/data/reviewTagList.json diff --git a/frontend/src/mocks/data/reviews.json b/src/mocks/data/reviews.json similarity index 100% rename from frontend/src/mocks/data/reviews.json rename to src/mocks/data/reviews.json diff --git a/frontend/src/mocks/data/searchingProducts.json b/src/mocks/data/searchingProducts.json similarity index 100% rename from frontend/src/mocks/data/searchingProducts.json rename to src/mocks/data/searchingProducts.json diff --git a/frontend/src/mocks/data/storeCategory.json b/src/mocks/data/storeCategory.json similarity index 100% rename from frontend/src/mocks/data/storeCategory.json rename to src/mocks/data/storeCategory.json diff --git a/frontend/src/mocks/handlers/bannerHandlers.ts b/src/mocks/handlers/bannerHandlers.ts similarity index 100% rename from frontend/src/mocks/handlers/bannerHandlers.ts rename to src/mocks/handlers/bannerHandlers.ts diff --git a/frontend/src/mocks/handlers/index.ts b/src/mocks/handlers/index.ts similarity index 100% rename from frontend/src/mocks/handlers/index.ts rename to src/mocks/handlers/index.ts diff --git a/frontend/src/mocks/handlers/loginHandlers.ts b/src/mocks/handlers/loginHandlers.ts similarity index 100% rename from frontend/src/mocks/handlers/loginHandlers.ts rename to src/mocks/handlers/loginHandlers.ts diff --git a/frontend/src/mocks/handlers/logoutHandlers.ts b/src/mocks/handlers/logoutHandlers.ts similarity index 100% rename from frontend/src/mocks/handlers/logoutHandlers.ts rename to src/mocks/handlers/logoutHandlers.ts diff --git a/frontend/src/mocks/handlers/memberHandlers.ts b/src/mocks/handlers/memberHandlers.ts similarity index 100% rename from frontend/src/mocks/handlers/memberHandlers.ts rename to src/mocks/handlers/memberHandlers.ts diff --git a/frontend/src/mocks/handlers/productHandlers.ts b/src/mocks/handlers/productHandlers.ts similarity index 100% rename from frontend/src/mocks/handlers/productHandlers.ts rename to src/mocks/handlers/productHandlers.ts diff --git a/frontend/src/mocks/handlers/rankingHandlers.ts b/src/mocks/handlers/rankingHandlers.ts similarity index 100% rename from frontend/src/mocks/handlers/rankingHandlers.ts rename to src/mocks/handlers/rankingHandlers.ts diff --git a/frontend/src/mocks/handlers/recipeHandlers.ts b/src/mocks/handlers/recipeHandlers.ts similarity index 100% rename from frontend/src/mocks/handlers/recipeHandlers.ts rename to src/mocks/handlers/recipeHandlers.ts diff --git a/frontend/src/mocks/handlers/reviewHandlers.ts b/src/mocks/handlers/reviewHandlers.ts similarity index 100% rename from frontend/src/mocks/handlers/reviewHandlers.ts rename to src/mocks/handlers/reviewHandlers.ts diff --git a/frontend/src/mocks/handlers/searchHandlers.ts b/src/mocks/handlers/searchHandlers.ts similarity index 100% rename from frontend/src/mocks/handlers/searchHandlers.ts rename to src/mocks/handlers/searchHandlers.ts diff --git a/frontend/src/mocks/handlers/utils.ts b/src/mocks/handlers/utils.ts similarity index 100% rename from frontend/src/mocks/handlers/utils.ts rename to src/mocks/handlers/utils.ts diff --git a/frontend/src/pages/AuthPage.tsx b/src/pages/AuthPage.tsx similarity index 100% rename from frontend/src/pages/AuthPage.tsx rename to src/pages/AuthPage.tsx diff --git a/frontend/src/pages/HomePage.tsx b/src/pages/HomePage.tsx similarity index 100% rename from frontend/src/pages/HomePage.tsx rename to src/pages/HomePage.tsx diff --git a/frontend/src/pages/IntegratedSearchPage.tsx b/src/pages/IntegratedSearchPage.tsx similarity index 100% rename from frontend/src/pages/IntegratedSearchPage.tsx rename to src/pages/IntegratedSearchPage.tsx diff --git a/frontend/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx similarity index 100% rename from frontend/src/pages/LoginPage.tsx rename to src/pages/LoginPage.tsx diff --git a/frontend/src/pages/MemberModifyPage.tsx b/src/pages/MemberModifyPage.tsx similarity index 100% rename from frontend/src/pages/MemberModifyPage.tsx rename to src/pages/MemberModifyPage.tsx diff --git a/frontend/src/pages/MemberPage.tsx b/src/pages/MemberPage.tsx similarity index 100% rename from frontend/src/pages/MemberPage.tsx rename to src/pages/MemberPage.tsx diff --git a/frontend/src/pages/MemberRecipePage.tsx b/src/pages/MemberRecipePage.tsx similarity index 100% rename from frontend/src/pages/MemberRecipePage.tsx rename to src/pages/MemberRecipePage.tsx diff --git a/frontend/src/pages/MemberReviewPage.tsx b/src/pages/MemberReviewPage.tsx similarity index 100% rename from frontend/src/pages/MemberReviewPage.tsx rename to src/pages/MemberReviewPage.tsx diff --git a/frontend/src/pages/NotFoundPage.tsx b/src/pages/NotFoundPage.tsx similarity index 100% rename from frontend/src/pages/NotFoundPage.tsx rename to src/pages/NotFoundPage.tsx diff --git a/frontend/src/pages/ProductDetailPage.tsx b/src/pages/ProductDetailPage.tsx similarity index 100% rename from frontend/src/pages/ProductDetailPage.tsx rename to src/pages/ProductDetailPage.tsx diff --git a/frontend/src/pages/ProductListPage.tsx b/src/pages/ProductListPage.tsx similarity index 100% rename from frontend/src/pages/ProductListPage.tsx rename to src/pages/ProductListPage.tsx diff --git a/frontend/src/pages/RecipeDetailPage.tsx b/src/pages/RecipeDetailPage.tsx similarity index 100% rename from frontend/src/pages/RecipeDetailPage.tsx rename to src/pages/RecipeDetailPage.tsx diff --git a/frontend/src/pages/RecipePage.tsx b/src/pages/RecipePage.tsx similarity index 100% rename from frontend/src/pages/RecipePage.tsx rename to src/pages/RecipePage.tsx diff --git a/frontend/src/pages/ReviewDetailPage.tsx b/src/pages/ReviewDetailPage.tsx similarity index 100% rename from frontend/src/pages/ReviewDetailPage.tsx rename to src/pages/ReviewDetailPage.tsx diff --git a/frontend/src/pages/SearchPage.tsx b/src/pages/SearchPage.tsx similarity index 100% rename from frontend/src/pages/SearchPage.tsx rename to src/pages/SearchPage.tsx diff --git a/frontend/src/router/App.tsx b/src/router/App.tsx similarity index 100% rename from frontend/src/router/App.tsx rename to src/router/App.tsx diff --git a/frontend/src/router/index.tsx b/src/router/index.tsx similarity index 100% rename from frontend/src/router/index.tsx rename to src/router/index.tsx diff --git a/frontend/src/service/channelTalk.ts b/src/service/channelTalk.ts similarity index 100% rename from frontend/src/service/channelTalk.ts rename to src/service/channelTalk.ts diff --git a/frontend/src/styles/animations.ts b/src/styles/animations.ts similarity index 100% rename from frontend/src/styles/animations.ts rename to src/styles/animations.ts diff --git a/frontend/src/styles/font.ts b/src/styles/font.ts similarity index 100% rename from frontend/src/styles/font.ts rename to src/styles/font.ts diff --git a/frontend/src/styles/globalStyle.ts b/src/styles/globalStyle.ts similarity index 100% rename from frontend/src/styles/globalStyle.ts rename to src/styles/globalStyle.ts diff --git a/frontend/src/types/banner.ts b/src/types/banner.ts similarity index 100% rename from frontend/src/types/banner.ts rename to src/types/banner.ts diff --git a/frontend/src/types/common.ts b/src/types/common.ts similarity index 100% rename from frontend/src/types/common.ts rename to src/types/common.ts diff --git a/frontend/src/types/images.d.ts b/src/types/images.d.ts similarity index 100% rename from frontend/src/types/images.d.ts rename to src/types/images.d.ts diff --git a/frontend/src/types/member.ts b/src/types/member.ts similarity index 100% rename from frontend/src/types/member.ts rename to src/types/member.ts diff --git a/frontend/src/types/product.ts b/src/types/product.ts similarity index 100% rename from frontend/src/types/product.ts rename to src/types/product.ts diff --git a/frontend/src/types/ranking.ts b/src/types/ranking.ts similarity index 100% rename from frontend/src/types/ranking.ts rename to src/types/ranking.ts diff --git a/frontend/src/types/recipe.ts b/src/types/recipe.ts similarity index 100% rename from frontend/src/types/recipe.ts rename to src/types/recipe.ts diff --git a/frontend/src/types/response.ts b/src/types/response.ts similarity index 100% rename from frontend/src/types/response.ts rename to src/types/response.ts diff --git a/frontend/src/types/review.ts b/src/types/review.ts similarity index 100% rename from frontend/src/types/review.ts rename to src/types/review.ts diff --git a/frontend/src/types/search.ts b/src/types/search.ts similarity index 100% rename from frontend/src/types/search.ts rename to src/types/search.ts diff --git a/frontend/src/types/styled.d.ts b/src/types/styled.d.ts similarity index 100% rename from frontend/src/types/styled.d.ts rename to src/types/styled.d.ts diff --git a/frontend/src/utils/category.ts b/src/utils/category.ts similarity index 100% rename from frontend/src/utils/category.ts rename to src/utils/category.ts diff --git a/frontend/src/utils/convertTagColor.ts b/src/utils/convertTagColor.ts similarity index 100% rename from frontend/src/utils/convertTagColor.ts rename to src/utils/convertTagColor.ts diff --git a/frontend/src/utils/date.ts b/src/utils/date.ts similarity index 100% rename from frontend/src/utils/date.ts rename to src/utils/date.ts diff --git a/frontend/src/utils/displaySlice.ts b/src/utils/displaySlice.ts similarity index 100% rename from frontend/src/utils/displaySlice.ts rename to src/utils/displaySlice.ts diff --git a/frontend/src/utils/localStorage.ts b/src/utils/localStorage.ts similarity index 100% rename from frontend/src/utils/localStorage.ts rename to src/utils/localStorage.ts diff --git a/frontend/src/utils/uuid.ts b/src/utils/uuid.ts similarity index 100% rename from frontend/src/utils/uuid.ts rename to src/utils/uuid.ts diff --git a/frontend/tsconfig.json b/tsconfig.json similarity index 100% rename from frontend/tsconfig.json rename to tsconfig.json diff --git a/frontend/webpack.common.js b/webpack.common.js similarity index 100% rename from frontend/webpack.common.js rename to webpack.common.js diff --git a/frontend/webpack.dev.js b/webpack.dev.js similarity index 100% rename from frontend/webpack.dev.js rename to webpack.dev.js diff --git a/frontend/webpack.prod.js b/webpack.prod.js similarity index 100% rename from frontend/webpack.prod.js rename to webpack.prod.js diff --git a/frontend/yarn.lock b/yarn.lock similarity index 100% rename from frontend/yarn.lock rename to yarn.lock