diff --git a/.github/workflows/ci-dev.yml b/.github/workflows/ci-dev.yml new file mode 100644 index 0000000..8f428bb --- /dev/null +++ b/.github/workflows/ci-dev.yml @@ -0,0 +1,113 @@ +name: CI in develop branch + +on: + push: + branches: [ "develop" ] + +permissions: + contents: read + + # PR 코멘트 등록을 위한 write 권한 + checks: write + pull-requests: write + +jobs: + build: + runs-on: ubuntu-latest + + services: + mongodb: + image: mongo:4.4 + options: >- + --health-cmd mongo + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 27017:27017 + + mysql: + image: mysql:8.0 + env: + MYSQL_DATABASE: menjil-test + MYSQL_ROOT_PASSWORD: root + # MYSQL_USER: root + # MYSQL_PASSWORD: root33 + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + steps: + # workflow 실행 전 기본적으로 checkout 필요 + # 최신 버전은 v3 + - uses: actions/checkout@v3 + - name: Install Ubuntu dependencies + run: sudo apt-get update + + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' # jdk를 제공하는 vender사 이름 ex. zulu, adopt, microsoft + + ## Create secret yml files before build + - name: create application-common, local, prod, test.yml + shell: bash + run: | + touch ./src/main/resources/application-common.yml + echo "${{secrets.APPLICATION_COMMON}}" > ./src/main/resources/application-common.yml + touch ./src/main/resources/application-local.yml + echo "${{secrets.APPLICATION_LOCAL}}" > ./src/main/resources/application-local.yml + touch ./src/main/resources/application-prod.yml + echo "${{secrets.APPLICATION_PROD}}" > ./src/main/resources/application-prod.yml + touch ./src/main/resources/application-test.yml + echo "${{secrets.APPLICATION_TEST}}" > ./src/main/resources/application-test.yml + + ## Gradle + - name: Cache gradle packages + 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: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Verify MySQL connection + run: | + sleep 30 + mysql --host 127.0.0.1 --port 3306 --user root --password=root -e "SHOW DATABASES;" + + ## Build + - name: Build with Gradle + run: ./gradlew clean build + + + ## 테스트 결과 PR 코멘트에 등록 + - name: Register the test results as PR comments + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/test/TEST-*.xml' + + ## 테스트 실패시 코드 라인에 대한 체크 추가 + - name: If test fail, add check comment on failed code line + uses: mikepenz/action-junit-report@v3 + if: always() + with: + report_paths: '**/build/test-results/test/TEST-*.html' + + ## 테스트 결과 업로드 + - name: Upload test report + uses: actions/upload-artifact@v3 + with: + name: test-report + path: build/reports/tests/test/ diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/codecov.yml similarity index 100% rename from .github/workflows/code-coverage.yml rename to .github/workflows/codecov.yml diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml deleted file mode 100644 index d1e5e1f..0000000 --- a/.github/workflows/develop.yml +++ /dev/null @@ -1,155 +0,0 @@ -name: Java CI with Gradle, CD with Docker Hub and AWS EC2, using 8081 port in develop branch - -on: - push: - branches: [ "develop" ] - -permissions: - contents: read - -jobs: - build: - runs-on: ubuntu-latest - - services: - mongodb: - image: mongo:4.4 - options: >- - --health-cmd mongo - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 27017:27017 - - mysql: - image: mysql:8.0 - env: - MYSQL_DATABASE: menjil-test - MYSQL_ROOT_PASSWORD: root - # MYSQL_USER: root - # MYSQL_PASSWORD: root33 - ports: - - 3306:3306 - options: >- - --health-cmd="mysqladmin ping" - --health-interval=10s - --health-timeout=5s - --health-retries=3 - - steps: - # workflow 실행 전 기본적으로 checkout 필요 - # 최신 버전은 v3 - - uses: actions/checkout@v3 - - name: Install Ubuntu dependencies - run: sudo apt-get update - - - name: Set up JDK 11 - uses: actions/setup-java@v3 - with: - java-version: '11' - distribution: 'temurin' # jdk를 제공하는 vender사 이름 ex. zulu, adopt, microsoft - - ## Create secret yml files before build - - name: create application-common, local, prod, test.yml - shell: bash - run: | - touch ./src/main/resources/application-common.yml - echo "${{secrets.APPLICATION_COMMON}}" > ./src/main/resources/application-common.yml - touch ./src/main/resources/application-local.yml - echo "${{secrets.APPLICATION_LOCAL}}" > ./src/main/resources/application-local.yml - touch ./src/main/resources/application-prod.yml - echo "${{secrets.APPLICATION_PROD}}" > ./src/main/resources/application-prod.yml - touch ./src/main/resources/application-test.yml - echo "${{secrets.APPLICATION_TEST}}" > ./src/main/resources/application-test.yml - - ## Gradle - - name: Cache gradle packages - 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: Grant execute permission for gradlew - run: chmod +x gradlew - - - name: Verify MySQL connection - run: | - sleep 30 - mysql --host 127.0.0.1 --port 3306 --user root --password=root -e "SHOW DATABASES;" - - ## Build - ## test, build 중 어느 곳에서 오류가 발생하는지 확인을 위해 분리 - - name: Running test code - run: ./gradlew test - - - name: Build with Gradle, without test - run: ./gradlew clean build -x test - # env: - # SPRING_DATASOURCE_URL: jdbc:mysql://127.0.0.1:3306/menjil-test - # SPRING_DATASOURCE_USERNAME: 'root' - # SPRING_DATASOURCE_PASSWORD: 'root' - # SPRING_DATA_MONGODB_URI: mongodb://localhost:27017/menjil-test - - ## Docker - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Build and push - uses: docker/build-push-action@v4 - with: - context: . - file: ./Dockerfile - platforms: linux/amd64 - push: true - tags: ${{ secrets.DOCKERHUB_REPO_DEV }}:latest - - deploy: - needs: build # Run after build - runs-on: ubuntu-latest - steps: - - name: Get Github Action's ip address - id: ip - uses: haythem/public-ip@v1.3 - - ## access-key-id, secret-access-key의 경우 AWS IAM User의 정보 사용 - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ap-northeast-2 - - - name: Add Github Action's ip address to Security Group - run: aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_SECURITY_GROUP_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 - - - name: connect EC2 instance and deploy docker images to develop server - uses: appleboy/ssh-action@v0.1.10 - with: - host: ${{ secrets.AWS_EC2_HOST_IP }} - username: ${{ secrets.AWS_EC2_USERNAME }} - key: ${{ secrets.AWS_SSH_KEY }} - # port: ${{ secrets.AWS_SSH_PORT }} # 생략하면 default 22 - - ## kill container -> remove container -> remove image -> pull new image -> run - script: | - sudo docker kill ${{ secrets.PROJECT_NAME_DEV }} - sudo docker rm ${{ secrets.PROJECT_NAME_DEV }} - sudo docker rmi ${{ secrets.DOCKERHUB_REPO_DEV }} - - sudo docker pull ${{ secrets.DOCKERHUB_REPO_DEV }}:latest - sudo docker run -d --name ${{ secrets.PROJECT_NAME_DEV }} -p 8081:8080 ${{ secrets.DOCKERHUB_REPO_DEV }}:latest - - - name: Remove Github Action's ip address from Security Group - run: aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SECURITY_GROUP_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 - diff --git a/build.gradle b/build.gradle index 612c6ff..2be9123 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,127 @@ plugins { id 'io.spring.dependency-management' version '1.0.15.RELEASE' id 'jacoco' // add jacoco plugin id 'com.google.osdetector' version "1.7.1" // https://github.com/netty/netty/issues/11020 + id "org.asciidoctor.jvm.convert" version "3.3.2" // RestDocs } +jar { + enabled = false +} + +group = 'seoultech.capstone' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '11' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } + asciidoctorExt // RestDocs +} + +repositories { + mavenCentral() +} + +dependencies { + if (osdetector.classifier == "osx-aarch_64") { + runtimeOnly("io.netty:netty-resolver-dns-native-macos:4.1.77.Final:${osdetector.classifier}") + } + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-web' + + // parse json + implementation 'com.google.code.gson:gson:2.9.0' + + // Security and OAuth +// implementation 'org.springframework.boot:spring-boot-starter-security' +// implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + + // JWT + implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5' + runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5' + runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' + + // chat-bot + implementation 'org.springframework.boot:spring-boot-starter-websocket' + implementation 'org.springframework.boot:spring-boot-starter-webflux' + implementation 'org.webjars:sockjs-client:1.1.2' + implementation 'org.webjars:stomp-websocket:2.3.3-1' + + // AWS +// implementation platform('software.amazon.awssdk:bom:2.20.56') // 파일의 종속성 섹션에 BOM (재료 명세서) 을 추가합니다. +// implementation 'software.amazon.awssdk:s3' // 종속성 섹션에 사용할 SDK 모듈을 지정합니다(S3). + implementation group: 'io.awspring.cloud', name: 'spring-cloud-starter-aws', version: '2.4.2' + + // lombok + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + // database + runtimeOnly 'com.mysql:mysql-connector-j' + implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' + + // RestDocs + asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' + testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + // WireMock + testImplementation "com.github.tomakehurst:wiremock-jre8:2.35.0" + testImplementation "org.assertj:assertj-core:3.24.2" +} + +tasks.named('test') { + useJUnitPlatform() + finalizedBy 'jacocoTestReport' +} + +/* start RestDocs */ +ext { // 전역 변수 + snippetsDir = file('build/generated-snippets') +} + +test { + outputs.dir snippetsDir +} + +asciidoctor.doFirst { + delete file('src/main/resources/static/docs') +} + +asciidoctor { + inputs.dir snippetsDir + configurations 'asciidoctorExt' + + sources { // 특정 파일만 html로 만든다. + include("**/index.adoc") + } + baseDirFollowsSourceFile() // 다른 adoc 파일을 include 할 때 경로를 baseDir로 맞춘다. + dependsOn test +} + +bootJar { + dependsOn asciidoctor + from("${asciidoctor.outputDir}") { + into 'static/docs' + } +} + +tasks.register('copyDocument', Copy) { + dependsOn asciidoctor + from file("build/docs/asciidoc") + into file("src/main/resources/static/docs") +} + +build { + dependsOn copyDocument +} +/* end RestDocs */ + +/* jacoco start */ jacoco { toolVersion = '0.8.10' } @@ -35,8 +154,7 @@ jacocoTestReport { "seoultech/capstone/menjil/domain/chat/dto/**", "seoultech/capstone/menjil/global/filter/*CustomCors*", "**/*Config*", - "**/*Exception*", - "**/TokenTestController*" + "**/*Exception*" ]) })) } @@ -50,7 +168,7 @@ jacocoTestCoverageVerification { enabled = true limit { value = 'COVEREDRATIO' - minimum = 0.50 + minimum = 0.05 } excludes = [ "**.*jilApplication*", @@ -61,8 +179,7 @@ jacocoTestCoverageVerification { "**/domain/chat/dto/**", "**.*Config*", "**.*CustomCors*", - "**.*Exception*", - "**.TokenTestController*" + "**.*Exception*" ] } @@ -83,77 +200,6 @@ jacocoTestCoverageVerification { } } - -jar { - enabled = false -} - -group = 'seoultech.capstone' -version = '0.0.1-SNAPSHOT' -sourceCompatibility = '11' - -configurations { - compileOnly { - extendsFrom annotationProcessor - } -} - -repositories { - mavenCentral() -} - -dependencies { - if (osdetector.classifier == "osx-aarch_64") { - runtimeOnly("io.netty:netty-resolver-dns-native-macos:4.1.77.Final:${osdetector.classifier}") - } - - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.boot:spring-boot-starter-web' - - // parse json - implementation 'com.google.code.gson:gson:2.9.0' - - // Security and OAuth -// implementation 'org.springframework.boot:spring-boot-starter-security' -// implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' - - // JWT - implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5' - runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5' - runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' - - // chat-bot - implementation 'org.springframework.boot:spring-boot-starter-websocket' - implementation 'org.springframework.boot:spring-boot-starter-webflux' - implementation 'org.webjars:sockjs-client:1.1.2' - implementation 'org.webjars:stomp-websocket:2.3.3-1' - - // AWS -// implementation platform('software.amazon.awssdk:bom:2.20.56') // 파일의 종속성 섹션에 BOM (재료 명세서) 을 추가합니다. -// implementation 'software.amazon.awssdk:s3' // 종속성 섹션에 사용할 SDK 모듈을 지정합니다(S3). - implementation group: 'io.awspring.cloud', name: 'spring-cloud-starter-aws', version: '2.4.2' - - // lombok - compileOnly 'org.projectlombok:lombok' - annotationProcessor 'org.projectlombok:lombok' - - // database - runtimeOnly 'com.mysql:mysql-connector-j' - implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' -// testImplementation 'de.flapdoodle.embed:de.flapdoodle.embed.mongo' - - testImplementation 'org.springframework.boot:spring-boot-starter-test' -// testImplementation 'com.squareup.okhttp3:mockwebserver:4.10.0' - - // WireMock - testImplementation "com.github.tomakehurst:wiremock-jre8:2.35.0" - testImplementation "org.assertj:assertj-core:3.24.2" -} - -tasks.named('test') { - useJUnitPlatform() - finalizedBy 'jacocoTestReport' -} +/* jacoco end */ mainClassName = 'seoultech.capstone.menjil.MenjilApplication' \ No newline at end of file diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc new file mode 100644 index 0000000..52c931b --- /dev/null +++ b/src/docs/asciidoc/index.adoc @@ -0,0 +1,145 @@ +// ifndef::snippets[] +// = :snippets: ../../build/generated-snippets +// endif::[] += 멘질멘질 REST API 문서 +:doctype: book +:icons: font +:source-highlighter: highlightjs +:toc: left +:toclevels: 2 +:sectlinks: +:sectnums: + +== 회원가입 + +=== 이메일 중복 확인 +==== 성공 +===== 회원가입이 가능한 경우 +요청한 이메일이 가입되어있지 않은 경우 + +operation::api/auth/signup-is-available/true[snippets='http-request,request-parameters'] +operation::api/auth/signup-is-available/true[snippets='http-response,response-fields'] + +==== 실패 +===== 회원가입이 불가능한 경우 +요청한 이메일이 이미 가입되어있는 경우 + +operation::api/auth/signup-is-available/false[snippets='http-request'] +operation::api/auth/signup-is-available/false[snippets='http-response'] + +=== 닉네임 중복 확인 + +==== 성공 +===== 사용할 수 있는 닉네임 형식인 경우 +operation::api/auth/check-nickname/true[snippets='http-request,request-parameters'] +operation::api/auth/check-nickname/true[snippets='http-response,response-fields'] + +==== 실패 +===== 닉네임에 공백이 포함된 경우 +operation::api/auth/check-nickname/false-blank[snippets='http-request'] +operation::api/auth/check-nickname/false-blank[snippets='http-response'] + +===== 닉네임에 특수문자가 포함된 경우 +operation::api/auth/check-nickname/false-special-character[snippets='http-request'] +operation::api/auth/check-nickname/false-special-character[snippets='http-response'] + +===== 닉네임이 이미 사용 중인 경우(존재하는 경우) +operation::api/auth/check-nickname/false-conflict[snippets='http-request'] +operation::api/auth/check-nickname/false-conflict[snippets='http-response'] + + +=== 회원 가입 + +==== 성공 +operation::api/auth/signup/true[snippets='http-request,request-fields'] +operation::api/auth/signup/true[snippets='http-response,response-fields'] + +==== 실패 +===== 닉네임에 공백이 포함되어 있는 경우 +operation::api/auth/signup/false-blank[snippets='http-request'] +operation::api/auth/signup/false-blank[snippets='http-response'] + +===== 닉네임에 특수문자가 포함되어 있는 경우 +operation::api/auth/signup/false-special-character[snippets='http-request'] +operation::api/auth/signup/false-special-character[snippets='http-response'] + +===== 닉네임에 null 값이 들어온 경우 +operation::api/auth/signup/false-null[snippets='http-request'] +operation::api/auth/signup/false-null[snippets='http-response'] + +===== 학점이 4 초과한 값이 들어온 경우 +operation::api/auth/signup/false-score-max[snippets='http-request'] +operation::api/auth/signup/false-score-max[snippets='http-response'] + + + + +== 로그인 +=== 로그인 +==== 성공 +operation::api/auth/signin/success[snippets='http-request,request-fields'] +operation::api/auth/signin/success[snippets='http-response,response-fields'] + +==== 실패 +===== 플랫폼이 google, kakao 가 아닌 경우 +operation::api/auth/signin/fail-provider[snippets='http-request'] +operation::api/auth/signin/fail-provider[snippets='http-response'] + + +// ** 2. 팔로우 ** +== 팔로우 +=== 팔로우 요청 +==== 팔로우가 되어있지 않은 경우 +팔로우가 생성된다. + +operation::api/follow/create/201/true[snippets='http-request,request-fields'] +operation::api/follow/create/201/true[snippets='http-response,response-fields'] + +==== 이미 팔로우가 되어 있는 경우 +팔로우가 해제된다. + +operation::api/follow/create/201/false[snippets='http-request,request-fields'] +operation::api/follow/create/201/false[snippets='http-response,response-fields'] + +==== 서버 오류 + +operation::api/follow/create/500[snippets='http-request,request-fields'] +operation::api/follow/create/500[snippets='http-response,response-fields'] + +=== 팔로우 상태 확인 + +==== 팔로우가 되어있는 경우 +return true + +operation::api/follow/check-status/true[snippets='http-request,request-parameters'] +operation::api/follow/check-status/true[snippets='http-response,response-fields'] + +==== 팔로우가 되어있지 않은 경우 +return false + +operation::api/follow/check-status/false[snippets='http-request,request-parameters'] +operation::api/follow/check-status/false[snippets='http-response,response-fields'] + + + +// ** 3. 팔로잉 페이지 ** +== 팔로잉 +=== 니의 팔로잉 목록 가져오기 + + + +=== 팔로잉된 사용자의 상세 정보 조회 +==== 성공 +===== 사용자의 정보와 질문 답변 내역이 모두 존재하는 경우 +operation::api/following/info/200-ok[snippets='http-request,request-parameters'] +operation::api/following/info/200-ok[snippets='http-response,response-fields'] + +===== 사용자의 정보만 존재하는 경우 +operation::api/following/info/200-only-userinfo[snippets='http-request'] +operation::api/following/info/200-only-userinfo[snippets='http-response'] + +==== 실패 +===== 존재하지 않는 사용자의 닉네임을 요청한 경우 +operation::api/following/info/500-error[snippets='http-request'] +operation::api/following/info/500-error[snippets='http-response,response-fields'] + diff --git a/src/main/java/seoultech/capstone/menjil/MenjilApplication.java b/src/main/java/seoultech/capstone/menjil/MenjilApplication.java index 6b1e88a..63ebd81 100644 --- a/src/main/java/seoultech/capstone/menjil/MenjilApplication.java +++ b/src/main/java/seoultech/capstone/menjil/MenjilApplication.java @@ -2,9 +2,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -@EnableJpaAuditing @SpringBootApplication public class MenjilApplication { diff --git a/src/main/java/seoultech/capstone/menjil/domain/auth/api/AuthController.java b/src/main/java/seoultech/capstone/menjil/domain/auth/api/AuthController.java index 3136703..a937626 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/auth/api/AuthController.java +++ b/src/main/java/seoultech/capstone/menjil/domain/auth/api/AuthController.java @@ -7,10 +7,10 @@ import org.springframework.http.ResponseEntity; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; +import seoultech.capstone.menjil.domain.auth.api.dto.request.SignInRequest; +import seoultech.capstone.menjil.domain.auth.api.dto.request.SignUpRequest; import seoultech.capstone.menjil.domain.auth.application.AuthService; -import seoultech.capstone.menjil.domain.auth.dto.request.SignInRequest; -import seoultech.capstone.menjil.domain.auth.dto.request.SignUpRequest; -import seoultech.capstone.menjil.domain.auth.dto.response.SignInResponse; +import seoultech.capstone.menjil.domain.auth.application.dto.response.SignInResponse; import seoultech.capstone.menjil.global.common.dto.ApiResponse; import seoultech.capstone.menjil.global.exception.CustomException; import seoultech.capstone.menjil.global.exception.SuccessCode; @@ -38,15 +38,14 @@ public class AuthController { */ @GetMapping(value = "/signup", produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - public ResponseEntity> checkSignUpAvailability(@RequestParam("email") String email, + public ResponseEntity> checkSignUpIsAvailable(@RequestParam("email") String email, @RequestParam("provider") String provider) { - log.info(">> 사용자로부터 {} 유저가 {} 회원가입 가능 여부 조회를 요청 받음", email, provider); + log.info("GET] /api/auth/signup 사용자로부터 {} 유저가 {} 회원가입 가능 여부 조회를 요청 받음", email, provider); int httpStatusValue = authService.findUserInDb(email, provider); if (httpStatusValue == SUCCESS.getValue()) { return ResponseEntity.status(HttpStatus.OK).body(success(SIGNUP_AVAILABLE)); -// return new ResponseEntity>(success(SuccessCode.SIGNUP_AVAILABLE), HttpStatus.OK); } else { - throw new CustomException(USER_DUPLICATED); + throw new CustomException(USER_ALREADY_EXISTED); } } @@ -57,11 +56,8 @@ public ResponseEntity> checkSignUpAvailability(@RequestParam("ema public ResponseEntity> isNicknameAvailable(@RequestParam("nickname") String nickname) { String pattern1 = "^[가-힣a-zA-Z0-9]*$"; // 특수문자, 공백 모두 체크 가능 - if (nickname.replaceAll(" ", "").equals("")) { // 먼저 공백 확인 - throw new CustomException(NICKNAME_CONTAINS_BLANK); - } if (!Pattern.matches(pattern1, nickname)) { - throw new CustomException(NICKNAME_CONTAINS_SPECIAL_CHARACTER); + throw new CustomException(NICKNAME_FORMAT_IS_WRONG); } int httpStatusValue = authService.findNicknameInDb(nickname); @@ -69,7 +65,7 @@ public ResponseEntity> isNicknameAvailable(@RequestParam("nicknam return ResponseEntity.status(HttpStatus.OK) .body(success(NICKNAME_AVAILABLE)); } else { - throw new CustomException(NICKNAME_DUPLICATED); + throw new CustomException(NICKNAME_ALREADY_EXISTED); } } @@ -96,8 +92,7 @@ public ResponseEntity> signUp(@Valid @RequestBody final Sign throw exception; } - authService.signUp(signUpRequest); - + authService.signUp(signUpRequest.toServiceRequest()); return ResponseEntity.status(HttpStatus.CREATED) .body(success(SIGNUP_SUCCESS, signUpRequest.getEmail())); } @@ -110,17 +105,9 @@ public ResponseEntity> signUp(@Valid @RequestBody final Sign * 가입된 유저가 없으면, CustomException 처리 */ @PostMapping(value = "/signin", consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity> signIn(@RequestBody SignInRequest dto) { - - String provider = dto.getProvider(); - - if (!provider.equals("google") && !provider.equals("kakao")) { - throw new CustomException(PROVIDER_NOT_ALLOWED); - } - - SignInResponse signInResponse = authService.signIn(dto.getEmail(), provider); - + public ResponseEntity> signIn(@RequestBody SignInRequest request) { return ResponseEntity.status(HttpStatus.CREATED) - .body(ApiResponse.success(SuccessCode.TOKEN_CREATED, signInResponse)); + .body(ApiResponse.success(SuccessCode.TOKEN_CREATED, + authService.signIn(request.toServiceRequest()))); } } diff --git a/src/main/java/seoultech/capstone/menjil/domain/auth/api/dto/request/SignInRequest.java b/src/main/java/seoultech/capstone/menjil/domain/auth/api/dto/request/SignInRequest.java new file mode 100644 index 0000000..7988524 --- /dev/null +++ b/src/main/java/seoultech/capstone/menjil/domain/auth/api/dto/request/SignInRequest.java @@ -0,0 +1,22 @@ +package seoultech.capstone.menjil.domain.auth.api.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import seoultech.capstone.menjil.domain.auth.application.dto.request.SignInServiceRequest; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class SignInRequest { + private String email; + private String provider; + + public SignInServiceRequest toServiceRequest() { + return SignInServiceRequest.builder() + .email(email) + .provider(provider) + .build(); + } + +} diff --git a/src/main/java/seoultech/capstone/menjil/domain/auth/dto/request/SignUpRequest.java b/src/main/java/seoultech/capstone/menjil/domain/auth/api/dto/request/SignUpRequest.java similarity index 82% rename from src/main/java/seoultech/capstone/menjil/domain/auth/dto/request/SignUpRequest.java rename to src/main/java/seoultech/capstone/menjil/domain/auth/api/dto/request/SignUpRequest.java index b049757..7082c40 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/auth/dto/request/SignUpRequest.java +++ b/src/main/java/seoultech/capstone/menjil/domain/auth/api/dto/request/SignUpRequest.java @@ -1,12 +1,9 @@ -package seoultech.capstone.menjil.domain.auth.dto.request; +package seoultech.capstone.menjil.domain.auth.api.dto.request; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import seoultech.capstone.menjil.domain.auth.domain.OptionInfo; -import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; +import seoultech.capstone.menjil.domain.auth.application.dto.request.SignUpServiceRequest; import javax.validation.constraints.*; import java.util.Objects; @@ -29,9 +26,7 @@ public class SignUpRequest { @NotBlank @Pattern(regexp = "^[가-힣a-zA-Z0-9]*$", message = "닉네임은 공백이나 특수문자가 들어갈 수 없습니다") private String nickname; - @NotNull - private UserRole role; - @NotNull(message = "생년은 OOOO형식으로 입력해주세요") + @NotNull(message = "생년은 YYYY 형식으로 입력해주세요") private Integer birthYear; @NotNull(message = "1~12 사이의 값을 입력해주세요") private Integer birthMonth; @@ -53,6 +48,8 @@ public class SignUpRequest { private String subMajor; private String minor; private String company; + private Integer companyYear; + @NotBlank private String field; @NotBlank @@ -66,14 +63,12 @@ public class SignUpRequest { private String awards; private String activity; - @Builder - public User toUserEntity() { - return User.builder() - .id(userId) + public SignUpServiceRequest toServiceRequest() { + return SignUpServiceRequest.builder() + .userId(userId) .email(email) .provider(provider) .nickname(nickname) - .role(role) .birthYear(birthYear) .birthMonth(birthMonth) .school(school) @@ -85,9 +80,13 @@ public User toUserEntity() { .subMajor(subMajor) .minor(minor) .company(company) + .companyYear(companyYear) .field(field) .techStack(techStack) - .optionInfo(new OptionInfo(career, certificate, awards, activity)) // use Embedded type + .career(career) + .certificate(certificate) + .awards(awards) + .activity(activity) .build(); } @@ -100,7 +99,6 @@ public boolean equals(Object o) { && Objects.equals(email, that.email) && Objects.equals(provider, that.provider) && Objects.equals(nickname, that.nickname) - && role == that.role && Objects.equals(birthYear, that.birthYear) && Objects.equals(birthMonth, that.birthMonth) && Objects.equals(school, that.school) @@ -112,6 +110,7 @@ public boolean equals(Object o) { && Objects.equals(subMajor, that.subMajor) && Objects.equals(minor, that.minor) && Objects.equals(company, that.company) + && Objects.equals(companyYear, that.companyYear) && Objects.equals(field, that.field) && Objects.equals(techStack, that.techStack) && Objects.equals(career, that.career) @@ -123,8 +122,9 @@ public boolean equals(Object o) { @Override public int hashCode() { return Objects.hash(userId, email, provider, nickname, - role, birthYear, birthMonth, school, score, + birthYear, birthMonth, school, score, scoreRange, graduateDate, graduateMonth, major, - subMajor, minor, company, field, techStack, career, certificate, awards, activity); + subMajor, minor, company, companyYear, + field, techStack, career, certificate, awards, activity); } } diff --git a/src/main/java/seoultech/capstone/menjil/domain/auth/application/AuthService.java b/src/main/java/seoultech/capstone/menjil/domain/auth/application/AuthService.java index 77178f1..b156e00 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/auth/application/AuthService.java +++ b/src/main/java/seoultech/capstone/menjil/domain/auth/application/AuthService.java @@ -5,12 +5,13 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import seoultech.capstone.menjil.domain.auth.application.dto.request.SignInServiceRequest; +import seoultech.capstone.menjil.domain.auth.application.dto.request.SignUpServiceRequest; +import seoultech.capstone.menjil.domain.auth.application.dto.response.SignInResponse; import seoultech.capstone.menjil.domain.auth.dao.TokenRepository; import seoultech.capstone.menjil.domain.auth.dao.UserRepository; import seoultech.capstone.menjil.domain.auth.domain.RefreshToken; import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.dto.request.SignUpRequest; -import seoultech.capstone.menjil.domain.auth.dto.response.SignInResponse; import seoultech.capstone.menjil.domain.auth.jwt.JwtTokenProvider; import seoultech.capstone.menjil.global.exception.CustomException; import seoultech.capstone.menjil.global.exception.ErrorCode; @@ -21,7 +22,8 @@ import java.time.LocalDateTime; import java.util.Optional; -import static seoultech.capstone.menjil.global.exception.ErrorIntValue.USER_ALREADY_IN_DB; +import static seoultech.capstone.menjil.global.exception.ErrorCode.PROVIDER_NOT_ALLOWED; +import static seoultech.capstone.menjil.global.exception.ErrorIntValue.USER_ALREADY_EXISTED; import static seoultech.capstone.menjil.global.exception.SuccessIntValue.SUCCESS; @Slf4j @@ -46,7 +48,7 @@ public class AuthService { @Transactional(readOnly = true) public int findUserInDb(String email, String provider) { Optional user = userRepository.findUserByEmailAndProvider(email, provider); - return checkIfUserInDb(user) ? USER_ALREADY_IN_DB.getValue() : SUCCESS.getValue(); + return checkIfUserInDb(user) ? USER_ALREADY_EXISTED.getValue() : SUCCESS.getValue(); } /** @@ -55,52 +57,52 @@ public int findUserInDb(String email, String provider) { @Transactional(readOnly = true) public int findNicknameInDb(String nickname) { Optional user = userRepository.findUserByNickname(nickname); - return checkIfUserInDb(user) ? USER_ALREADY_IN_DB.getValue() : SUCCESS.getValue(); + return checkIfUserInDb(user) ? USER_ALREADY_EXISTED.getValue() : SUCCESS.getValue(); } /** * 회원가입 로직 수행 */ @Transactional - public void signUp(SignUpRequest request) { + public void signUp(SignUpServiceRequest request) { // SignUpRequestDto -> User Entity 변환 User user = request.toUserEntity(); // 기존에 중복된 유저가 있는 지 조회 // 이 부분은 의미없다. 처음 가입할 때 유저 확인 후 redirect 처리 하므로. - /* - List userInDb = userRepository.findUserByEmailAndProvider(user.getEmail(), user.getProvider()); + /*List userInDb = userRepository.findUserByEmailAndProvider(user.getEmail(), user.getProvider()); if (userInDb.size() > 0) { throw new CustomException(ErrorCode.USER_DUPLICATED); - } - */ + }*/ // 혹시 클라이언트에서 닉네임 중복 검증을 놓친 경우 확인 User nicknameExistsInDb = userRepository.findUserByNickname(user.getNickname()) .orElse(null); if (nicknameExistsInDb != null) { // 이 부분은, 컨트롤러가 아닌 서비스에서 처리하는 것이 더 바람직할 것으로 보임. - throw new CustomException(ErrorCode.NICKNAME_DUPLICATED); + throw new CustomException(ErrorCode.NICKNAME_ALREADY_EXISTED); } // Set AWS S3 default image url in user user.setImgUrl(defaultImgUrl); // save in db - try { - userRepository.save(user); - } catch (RuntimeException e) { - throw new CustomException(ErrorCode.INTERNAL_SERVER_ERROR); - } + saveUser(user); } /** * 로그인 처리 */ @Transactional - public SignInResponse signIn(String email, String provider) { - Optional userInDb = userRepository.findUserByEmailAndProvider(email, provider); + public SignInResponse signIn(SignInServiceRequest request) { + String email = request.getEmail(); + String provider = request.getProvider(); + + if (!provider.equals("google") && !provider.equals("kakao")) { + throw new CustomException(PROVIDER_NOT_ALLOWED); + } + Optional userInDb = userRepository.findUserByEmailAndProvider(email, provider); if (checkIfUserInDb(userInDb)) { User user = userInDb.get(); @@ -145,4 +147,12 @@ public SignInResponse signIn(String email, String provider) { private boolean checkIfUserInDb(Optional user) { return user.isPresent(); } + + private void saveUser(User user) { + try { + userRepository.save(user); + } catch (RuntimeException e) { + throw new CustomException(ErrorCode.INTERNAL_SERVER_ERROR); + } + } } diff --git a/src/main/java/seoultech/capstone/menjil/domain/auth/application/dto/request/SignInServiceRequest.java b/src/main/java/seoultech/capstone/menjil/domain/auth/application/dto/request/SignInServiceRequest.java new file mode 100644 index 0000000..002ad1a --- /dev/null +++ b/src/main/java/seoultech/capstone/menjil/domain/auth/application/dto/request/SignInServiceRequest.java @@ -0,0 +1,20 @@ +package seoultech.capstone.menjil.domain.auth.application.dto.request; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class SignInServiceRequest { + + private String email; + private String provider; + + @Builder + private SignInServiceRequest(String email, String provider) { + this.email = email; + this.provider = provider; + } + +} diff --git a/src/main/java/seoultech/capstone/menjil/domain/auth/application/dto/request/SignUpServiceRequest.java b/src/main/java/seoultech/capstone/menjil/domain/auth/application/dto/request/SignUpServiceRequest.java new file mode 100644 index 0000000..1188da3 --- /dev/null +++ b/src/main/java/seoultech/capstone/menjil/domain/auth/application/dto/request/SignUpServiceRequest.java @@ -0,0 +1,162 @@ +package seoultech.capstone.menjil.domain.auth.application.dto.request; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import seoultech.capstone.menjil.domain.auth.domain.User; + +import javax.validation.constraints.*; +import java.util.Objects; + +@Getter +@NoArgsConstructor +public class SignUpServiceRequest { + + @NotBlank + private String userId; + @NotBlank + private String email; + @NotBlank + private String provider; // google, kakao + + /** + * 사용자에게 입력받는 필수 정보 + */ + @NotBlank + @Pattern(regexp = "^[가-힣a-zA-Z0-9]*$", message = "닉네임은 공백이나 특수문자가 들어갈 수 없습니다") + private String nickname; + @NotNull(message = "생년은 YYYY 형식으로 입력해주세요") + private Integer birthYear; + @NotNull(message = "1~12 사이의 값을 입력해주세요") + private Integer birthMonth; + @NotBlank + private String school; + @NotNull(message = "학점은 0이상 4이하로 작성해주세요") + @Min(value = 0, message = "학점은 0보다 작을 수 없습니다") + @Max(value = 4, message = "학점은 4보다 클 수 없습니다") + private Integer score; // 학점. 0, 1, 2, 3, 4 + @NotBlank + private String scoreRange; // 초반, 중반, 후반; 학점과 연관됨 + @NotNull(message = "졸업 년도는 정수를 입력해주세요") + private Integer graduateDate; + @NotNull(message = "졸업 월은 정수를 입력해주세요") + private Integer graduateMonth; + + @NotBlank + private String major; + private String subMajor; + private String minor; + private String company; + private Integer companyYear; + + @NotBlank + private String field; + @NotBlank + private String techStack; + + /** + * 사용자에게 입력받는 선택 정보 + */ + private String career; + private String certificate; + private String awards; + private String activity; + + @Builder + private SignUpServiceRequest(String userId, String email, String provider, + String nickname, Integer birthYear, Integer birthMonth, + String school, Integer score, String scoreRange, + Integer graduateDate, Integer graduateMonth, String major, + String subMajor, String minor, String company, + Integer companyYear, String field, String techStack, + String career, String certificate, String awards, + String activity) { + this.userId = userId; + this.email = email; + this.provider = provider; + this.nickname = nickname; + this.birthYear = birthYear; + this.birthMonth = birthMonth; + this.school = school; + this.score = score; + this.scoreRange = scoreRange; + this.graduateDate = graduateDate; + this.graduateMonth = graduateMonth; + this.major = major; + this.subMajor = subMajor; + this.minor = minor; + this.company = company; + this.companyYear = companyYear; + this.field = field; + this.techStack = techStack; + this.career = career; + this.certificate = certificate; + this.awards = awards; + this.activity = activity; + } + + public User toUserEntity() { + return User.builder() + .id(userId) + .email(email) + .provider(provider) + .nickname(nickname) + .birthYear(birthYear) + .birthMonth(birthMonth) + .school(school) + .score(score) + .scoreRange(scoreRange) + .graduateDate(graduateDate) + .graduateMonth(graduateMonth) + .major(major) + .subMajor(subMajor) + .minor(minor) + .company(company) + .companyYear(companyYear) + .field(field) + .techStack(techStack) + .career(career) + .certificate(certificate) + .awards(awards) + .activity(activity) + .build(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SignUpServiceRequest that = (SignUpServiceRequest) o; + return Objects.equals(userId, that.userId) + && Objects.equals(email, that.email) + && Objects.equals(provider, that.provider) + && Objects.equals(nickname, that.nickname) + && Objects.equals(birthYear, that.birthYear) + && Objects.equals(birthMonth, that.birthMonth) + && Objects.equals(school, that.school) + && Objects.equals(score, that.score) + && Objects.equals(scoreRange, that.scoreRange) + && Objects.equals(graduateDate, that.graduateDate) + && Objects.equals(graduateMonth, that.graduateMonth) + && Objects.equals(major, that.major) + && Objects.equals(subMajor, that.subMajor) + && Objects.equals(minor, that.minor) + && Objects.equals(company, that.company) + && Objects.equals(companyYear, that.companyYear) + && Objects.equals(field, that.field) + && Objects.equals(techStack, that.techStack) + && Objects.equals(career, that.career) + && Objects.equals(certificate, that.certificate) + && Objects.equals(awards, that.awards) + && Objects.equals(activity, that.activity); + } + + @Override + public int hashCode() { + return Objects.hash(userId, email, provider, nickname, + birthYear, birthMonth, school, score, + scoreRange, graduateDate, graduateMonth, major, + subMajor, minor, company, companyYear, + field, techStack, career, certificate, awards, activity); + } +} diff --git a/src/main/java/seoultech/capstone/menjil/domain/auth/dto/response/SignInResponse.java b/src/main/java/seoultech/capstone/menjil/domain/auth/application/dto/response/SignInResponse.java similarity index 88% rename from src/main/java/seoultech/capstone/menjil/domain/auth/dto/response/SignInResponse.java rename to src/main/java/seoultech/capstone/menjil/domain/auth/application/dto/response/SignInResponse.java index d515bdd..4b007c6 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/auth/dto/response/SignInResponse.java +++ b/src/main/java/seoultech/capstone/menjil/domain/auth/application/dto/response/SignInResponse.java @@ -1,4 +1,4 @@ -package seoultech.capstone.menjil.domain.auth.dto.response; +package seoultech.capstone.menjil.domain.auth.application.dto.response; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/seoultech/capstone/menjil/domain/auth/dao/UserRepository.java b/src/main/java/seoultech/capstone/menjil/domain/auth/dao/UserRepository.java index 0bde702..0e84683 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/auth/dao/UserRepository.java +++ b/src/main/java/seoultech/capstone/menjil/domain/auth/dao/UserRepository.java @@ -4,9 +4,8 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; -import java.util.List; +import javax.validation.constraints.NotNull; import java.util.Optional; // CRUD 함수를 JpaRepository 가 가지고 있다. @@ -19,6 +18,6 @@ public interface UserRepository extends JpaRepository { Optional findUserById(String id); - Page findUsersByRole(UserRole role, Pageable pageable); + Page findAll(@NotNull Pageable pageable); } diff --git a/src/main/java/seoultech/capstone/menjil/domain/auth/domain/OptionInfo.java b/src/main/java/seoultech/capstone/menjil/domain/auth/domain/OptionInfo.java deleted file mode 100644 index de57533..0000000 --- a/src/main/java/seoultech/capstone/menjil/domain/auth/domain/OptionInfo.java +++ /dev/null @@ -1,32 +0,0 @@ -package seoultech.capstone.menjil.domain.auth.domain; - -import lombok.Getter; -import lombok.NoArgsConstructor; - -import javax.persistence.Column; -import javax.persistence.Embeddable; - -@Getter -@NoArgsConstructor // 기본 생성자 필수 -@Embeddable -public class OptionInfo { - - @Column(columnDefinition = "TEXT") - private String career; - - @Column(columnDefinition = "TEXT") - private String certificate; - - @Column(columnDefinition = "TEXT") - private String awards; - - @Column(columnDefinition = "TEXT") - private String activity; - - public OptionInfo(String career, String certificate, String awards, String activity) { - this.career = career; - this.certificate = certificate; - this.awards = awards; - this.activity = activity; - } -} diff --git a/src/main/java/seoultech/capstone/menjil/domain/auth/domain/User.java b/src/main/java/seoultech/capstone/menjil/domain/auth/domain/User.java index da2153e..51e7474 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/auth/domain/User.java +++ b/src/main/java/seoultech/capstone/menjil/domain/auth/domain/User.java @@ -31,10 +31,6 @@ public class User extends BaseTimeEntity { @Column(nullable = false, length = 100) private String nickname; // 고유 유저를 식별할 정보 - @Enumerated(EnumType.STRING) - @Column(nullable = false, length = 15) - private UserRole role; // Mentor or Mentee - @Column(name = "birth_year", nullable = false) private Integer birthYear; @@ -68,6 +64,9 @@ public class User extends BaseTimeEntity { @Column(length = 100) private String company; // 재직 중인 회사. + @Column(name = "company_year", length = 10) + private Integer companyYear; // 회사 첫 입사 년도 + @Column(nullable = false) private String field; // 관심 분야 @@ -77,8 +76,17 @@ public class User extends BaseTimeEntity { /** * 아래는 선택 입력 정보: 가입 단계에서 굳이 받지 않아도 되는 정보 */ - @Embedded - private OptionInfo optionInfo; + @Column(columnDefinition = "TEXT") + private String career; + + @Column(columnDefinition = "TEXT") + private String certificate; + + @Column(columnDefinition = "TEXT") + private String awards; + + @Column(columnDefinition = "TEXT") + private String activity; @Column(name = "img_url", length = 100) private String imgUrl; @@ -86,15 +94,16 @@ public class User extends BaseTimeEntity { /* Builder 로만 생성할 수 있도록 private 설정 */ @Builder private User(String id, String email, String provider, String nickname, - UserRole role, Integer birthYear, Integer birthMonth, String school, + Integer birthYear, Integer birthMonth, String school, Integer score, String scoreRange, Integer graduateDate, Integer graduateMonth, String major, - String subMajor, String minor, String company, String field, String techStack, - OptionInfo optionInfo, String imgUrl) { + String subMajor, String minor, String company,Integer companyYear, String field, + String techStack, + String career, String certificate, String awards, String activity, + String imgUrl) { this.id = id; this.email = email; this.provider = provider; this.nickname = nickname; - this.role = role; this.birthYear = birthYear; this.birthMonth = birthMonth; this.school = school; @@ -106,9 +115,13 @@ private User(String id, String email, String provider, String nickname, this.subMajor = subMajor; this.minor = minor; this.company = company; + this.companyYear = companyYear; this.field = field; this.techStack = techStack; - this.optionInfo = optionInfo; + this.career = career; + this.certificate = certificate; + this.awards = awards; + this.activity = activity; this.imgUrl = imgUrl; } diff --git a/src/main/java/seoultech/capstone/menjil/domain/auth/domain/UserRole.java b/src/main/java/seoultech/capstone/menjil/domain/auth/domain/UserRole.java deleted file mode 100644 index f83d354..0000000 --- a/src/main/java/seoultech/capstone/menjil/domain/auth/domain/UserRole.java +++ /dev/null @@ -1,15 +0,0 @@ -package seoultech.capstone.menjil.domain.auth.domain; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public enum UserRole { - - MENTOR("ROLE_MENTOR"), - MENTEE("ROLE_MENTEE"); - - private final String role; - -} diff --git a/src/main/java/seoultech/capstone/menjil/domain/auth/dto/request/SignInRequest.java b/src/main/java/seoultech/capstone/menjil/domain/auth/dto/request/SignInRequest.java deleted file mode 100644 index f378aaf..0000000 --- a/src/main/java/seoultech/capstone/menjil/domain/auth/dto/request/SignInRequest.java +++ /dev/null @@ -1,13 +0,0 @@ -package seoultech.capstone.menjil.domain.auth.dto.request; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor -@AllArgsConstructor -public class SignInRequest { - private String email; - private String provider; -} diff --git a/src/main/java/seoultech/capstone/menjil/domain/chat/api/MessageController.java b/src/main/java/seoultech/capstone/menjil/domain/chat/api/MessageController.java index 469d141..7746dbc 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/chat/api/MessageController.java +++ b/src/main/java/seoultech/capstone/menjil/domain/chat/api/MessageController.java @@ -53,6 +53,8 @@ public void enter(@DestinationVariable("roomId") String roomId, case AI_SELECT: case AI_SUMMARY: case AI_SUMMARY_ANSWER: + case AI_SUMMARY_RATING: + case AI_C_RATING: // 2. Send Client's Chat Message sendSuccessResponse(roomId, SuccessCode.MESSAGE_SEND_SUCCESS, (MessageResponse) result); break; diff --git a/src/main/java/seoultech/capstone/menjil/domain/chat/application/MessageRatingService.java b/src/main/java/seoultech/capstone/menjil/domain/chat/application/MessageRatingService.java index a4fe47c..546313f 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/chat/application/MessageRatingService.java +++ b/src/main/java/seoultech/capstone/menjil/domain/chat/application/MessageRatingService.java @@ -3,7 +3,10 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import seoultech.capstone.menjil.domain.chat.dao.MessageRepository; import seoultech.capstone.menjil.domain.chat.dao.QaListRepository; +import seoultech.capstone.menjil.domain.chat.domain.ChatMessage; +import seoultech.capstone.menjil.domain.chat.domain.MessageType; import seoultech.capstone.menjil.domain.chat.domain.QaList; import seoultech.capstone.menjil.domain.chat.dto.request.MessageClickIncViewsAndLikesRequest; import seoultech.capstone.menjil.domain.chat.dto.response.MessageClickIncViewsAndLikesResponse; @@ -15,9 +18,14 @@ @Service public class MessageRatingService { + private final MessageRepository messageRepository; private final QaListRepository qaListRepository; public MessageClickIncViewsAndLikesResponse incrementViewsAndLikes(MessageClickIncViewsAndLikesRequest request) { + MessageType ratingType = MessageType.AI_SUMMARY_RATING; + ChatMessage message = messageRepository.findBy_idAndMessageType(request.getId(), ratingType) + .orElseThrow(() -> new CustomException(ErrorCode.CHAT_MESSAGE_NOT_EXISTED)); + QaList qaList = qaListRepository.findBy_id(request.getQuestionId()) .orElseThrow(() -> new CustomException(ErrorCode.QALIST_NOT_EXISTED)); updateViewsAndLikes(qaList, request.getLikeStatus()); @@ -25,6 +33,9 @@ public MessageClickIncViewsAndLikesResponse incrementViewsAndLikes(MessageClickI // 변경된 내용 저장: Spring Data MongoDB는 dirty checking이 없다. qaListRepository.save(qaList); + // 평가 이후 AI_SUMMARY_RATING 메시지 제거 + messageRepository.delete(message); + return MessageClickIncViewsAndLikesResponse.of(qaList.get_id(), qaList.getViews(), qaList.getLikes()); } diff --git a/src/main/java/seoultech/capstone/menjil/domain/chat/application/MessageService.java b/src/main/java/seoultech/capstone/menjil/domain/chat/application/MessageService.java index 25f6815..21a24cc 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/chat/application/MessageService.java +++ b/src/main/java/seoultech/capstone/menjil/domain/chat/application/MessageService.java @@ -182,20 +182,20 @@ private ChatMessage createLambdaChatMessage(String roomId, String message4th = "AI 챗봇을 종료하고 멘토 답변 기다리기"; LocalDateTime now = getCurrentTimeWithNanos(); - int initialSize = awsLambdaResponses.size(); - int questionMaxSize = 3; - + // TODO: 프론트 요청에 따라 이 부분 사용하지 않기로 결정됨. 추후 이견이 없을 경우 아래 주석 삭제 +// int initialSize = awsLambdaResponses.size(); +// int questionMaxSize = 3; // 첫 번째 메시지 추가 (만약 리스트가 비어 있을 경우) - if (initialSize == 0) { - awsLambdaResponses.add(AwsLambdaResponse.of(null, - messageRequest.getSenderNickname() - + "님의 질문과 유사도가 높은 대화 목록이 존재하지 않습니다", null, null)); - initialSize++; - } - // 필요한 만큼 빈 메시지 추가 - IntStream.range(initialSize, questionMaxSize) - .mapToObj(i -> AwsLambdaResponse.of(null, null, null, null)) - .forEach(awsLambdaResponses::add); +// if (initialSize == 0) { +// awsLambdaResponses.add(AwsLambdaResponse.of(null, +// messageRequest.getSenderNickname() +// + "님의 질문과 유사도가 높은 대화 목록이 존재하지 않습니다", null, null)); +// initialSize++; +// } +// // 필요한 만큼 빈 메시지 추가 +// IntStream.range(initialSize, questionMaxSize) +// .mapToObj(i -> AwsLambdaResponse.of(null, null, null, null)) +// .forEach(awsLambdaResponses::add); // 4번째 응답 추가(공통) awsLambdaResponses.add(AwsLambdaResponse.of(null, message4th, null, null)); diff --git a/src/main/java/seoultech/capstone/menjil/domain/chat/dao/MessageRepository.java b/src/main/java/seoultech/capstone/menjil/domain/chat/dao/MessageRepository.java index 2e6595b..61b4923 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/chat/dao/MessageRepository.java +++ b/src/main/java/seoultech/capstone/menjil/domain/chat/dao/MessageRepository.java @@ -7,11 +7,12 @@ import seoultech.capstone.menjil.domain.chat.domain.MessageType; import java.util.List; +import java.util.Optional; @Repository public interface MessageRepository extends MongoRepository { - ChatMessage findChatMessageByRoomIdAndMessageType(String roomId, MessageType messageType); + Optional findBy_idAndMessageType(String _id, MessageType type); // totalCount 등의 정보는 필요 없고, 단지 ChatMessage 엔티티 데이터만 가져오면 되므로 Page가 아닌 List로 받도록 작성 List findChatMessageByRoomId(String roomId, Pageable pageable); diff --git a/src/main/java/seoultech/capstone/menjil/domain/chat/dao/QaListRepository.java b/src/main/java/seoultech/capstone/menjil/domain/chat/dao/QaListRepository.java index c303801..2b03582 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/chat/dao/QaListRepository.java +++ b/src/main/java/seoultech/capstone/menjil/domain/chat/dao/QaListRepository.java @@ -27,7 +27,7 @@ public interface QaListRepository extends MongoRepository { @Query(value = "{'mentor_nickname' : ?0, 'answer' : { '$ne' : null } }", fields = "{ 'question_origin' : 1, 'question_summary' : 1, " + - "'answer' : 1, 'answer_time': 1 }") + "'answer' : 1, 'answer_time': 1, 'views': 1, 'likes': 1 }") List findQuestionAndAnswerWithMentorNickname(String mentorNickname, Sort sort); Long countByMentorNicknameAndAnswerIsNotNull(String mentorNickname); diff --git a/src/main/java/seoultech/capstone/menjil/domain/chat/domain/MessageType.java b/src/main/java/seoultech/capstone/menjil/domain/chat/domain/MessageType.java index 532ff29..6726f0c 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/chat/domain/MessageType.java +++ b/src/main/java/seoultech/capstone/menjil/domain/chat/domain/MessageType.java @@ -13,7 +13,8 @@ public enum MessageType { */ ENTER, C_QUESTION, - AI_QUESTION_RESPONSE, AI_SUMMARY_LIST, AI_SELECT, AI_SUMMARY, AI_SUMMARY_ANSWER, + AI_QUESTION_RESPONSE, AI_SUMMARY_LIST, AI_SELECT, AI_SUMMARY, AI_SUMMARY_ANSWER, AI_SUMMARY_RATING, + AI_C_RATING, TALK, TYPE_NOT_EXISTS; diff --git a/src/main/java/seoultech/capstone/menjil/domain/chat/dto/request/MessageClickIncViewsAndLikesRequest.java b/src/main/java/seoultech/capstone/menjil/domain/chat/dto/request/MessageClickIncViewsAndLikesRequest.java index 4a0bfda..a047440 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/chat/dto/request/MessageClickIncViewsAndLikesRequest.java +++ b/src/main/java/seoultech/capstone/menjil/domain/chat/dto/request/MessageClickIncViewsAndLikesRequest.java @@ -11,6 +11,9 @@ @AllArgsConstructor public class MessageClickIncViewsAndLikesRequest { + @NotBlank + private String Id; + @NotBlank private String questionId; diff --git a/src/main/java/seoultech/capstone/menjil/domain/follow/api/FollowController.java b/src/main/java/seoultech/capstone/menjil/domain/follow/api/FollowController.java index 5c02b49..ed39bcc 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/follow/api/FollowController.java +++ b/src/main/java/seoultech/capstone/menjil/domain/follow/api/FollowController.java @@ -5,18 +5,15 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import seoultech.capstone.menjil.domain.follow.api.dto.request.FollowCreateRequest; import seoultech.capstone.menjil.domain.follow.application.FollowService; -import seoultech.capstone.menjil.domain.follow.dto.request.FollowRequest; import seoultech.capstone.menjil.global.common.dto.ApiResponse; -import seoultech.capstone.menjil.global.exception.CustomException; -import seoultech.capstone.menjil.global.exception.ErrorCode; import seoultech.capstone.menjil.global.exception.SuccessCode; import javax.validation.Valid; import static seoultech.capstone.menjil.global.common.dto.ApiResponse.success; import static seoultech.capstone.menjil.global.exception.SuccessIntValue.FOLLOW_CREATED; -import static seoultech.capstone.menjil.global.exception.SuccessIntValue.FOLLOW_DELETED; @Slf4j @RequiredArgsConstructor @@ -26,16 +23,14 @@ public class FollowController { private final FollowService followService; - @PostMapping("/request") - public ResponseEntity> followRequest(@Valid @RequestBody FollowRequest followRequest) { + @PostMapping("/create") + public ResponseEntity> createFollow(@Valid @RequestBody FollowCreateRequest followCreateRequest) { - int result = followService.followRequest(followRequest); + int result = followService.createFollow(followCreateRequest.toServiceRequest()); if (result == FOLLOW_CREATED.getValue()) { return ResponseEntity.status(HttpStatus.CREATED).body(success(SuccessCode.FOLLOW_CREATED)); - } else if (result == FOLLOW_DELETED.getValue()) { - return ResponseEntity.status(HttpStatus.CREATED).body(success(SuccessCode.FOLLOW_DELETED)); } else { - throw new CustomException(ErrorCode.INTERNAL_SERVER_ERROR); + return ResponseEntity.status(HttpStatus.CREATED).body(success(SuccessCode.FOLLOW_DELETED)); } } diff --git a/src/main/java/seoultech/capstone/menjil/domain/follow/api/dto/request/FollowCreateRequest.java b/src/main/java/seoultech/capstone/menjil/domain/follow/api/dto/request/FollowCreateRequest.java new file mode 100644 index 0000000..d3b8c26 --- /dev/null +++ b/src/main/java/seoultech/capstone/menjil/domain/follow/api/dto/request/FollowCreateRequest.java @@ -0,0 +1,46 @@ +package seoultech.capstone.menjil.domain.follow.api.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import seoultech.capstone.menjil.domain.follow.application.dto.request.FollowCreateServiceRequest; + +import javax.validation.constraints.NotBlank; +import java.util.Objects; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class FollowCreateRequest { + + @NotBlank + private String userNickname; + + @NotBlank + private String followNickname; + + public static FollowCreateRequest of(String userNickname, String followNickname) { + return new FollowCreateRequest(userNickname, followNickname); + } + + public FollowCreateServiceRequest toServiceRequest() { + return FollowCreateServiceRequest.builder() + .userNickname(userNickname) + .followNickname(followNickname) + .build(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FollowCreateRequest that = (FollowCreateRequest) o; + return Objects.equals(userNickname, that.userNickname) + && Objects.equals(followNickname, that.followNickname); + } + + @Override + public int hashCode() { + return Objects.hash(userNickname, followNickname); + } +} diff --git a/src/main/java/seoultech/capstone/menjil/domain/follow/application/FollowService.java b/src/main/java/seoultech/capstone/menjil/domain/follow/application/FollowService.java index 45f4100..b2abf35 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/follow/application/FollowService.java +++ b/src/main/java/seoultech/capstone/menjil/domain/follow/application/FollowService.java @@ -3,14 +3,15 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import seoultech.capstone.menjil.domain.follow.application.dto.request.FollowCreateServiceRequest; import seoultech.capstone.menjil.domain.follow.dao.FollowRepository; import seoultech.capstone.menjil.domain.follow.domain.Follow; -import seoultech.capstone.menjil.domain.follow.dto.request.FollowRequest; +import seoultech.capstone.menjil.global.exception.CustomException; +import seoultech.capstone.menjil.global.exception.ErrorCode; import java.time.LocalDateTime; import java.util.Optional; -import static seoultech.capstone.menjil.global.exception.ErrorIntValue.INTERNAL_SERVER_ERROR; import static seoultech.capstone.menjil.global.exception.SuccessIntValue.FOLLOW_CREATED; import static seoultech.capstone.menjil.global.exception.SuccessIntValue.FOLLOW_DELETED; @@ -21,10 +22,11 @@ public class FollowService { private final FollowRepository followRepository; - public int followRequest(FollowRequest followRequest) { - String userNickname = followRequest.getUserNickname(); - String followNickname = followRequest.getFollowNickname(); + public int createFollow(FollowCreateServiceRequest request) { + String userNickname = request.getUserNickname(); + String followNickname = request.getFollowNickname(); + // TODO: 이 부분에서, 조회할 때 실제 User가 있는지 확인이 필요하므로, 추후 User & Follow 연관관계 설정 Optional follow = followRepository.findFollowByUserNicknameAndFollowNickname(userNickname, followNickname); if (followIsExist(follow)) { // 팔로우가 존재하는 경우 팔로우 취소 @@ -33,12 +35,7 @@ public int followRequest(FollowRequest followRequest) { } else { // 팔로우가 존재하지 않는 경우 팔로우 등록 Follow newfollow = Follow.of(userNickname, followNickname, LocalDateTime.now()); - try { - followRepository.save(newfollow); - } catch (RuntimeException e) { - log.error(">> save exception occurred in: ", e); - return INTERNAL_SERVER_ERROR.getValue(); - } + saveFollow(newfollow); return FOLLOW_CREATED.getValue(); } } @@ -48,7 +45,16 @@ public boolean checkFollowStatus(String userNickname, String followNickname) { return followIsExist(follow); } - public boolean followIsExist(Optional follow) { + protected boolean followIsExist(Optional follow) { return follow.isPresent(); } + + private void saveFollow(Follow follow) { + try { + followRepository.save(follow); + } catch (RuntimeException e) { + log.error(">> Follow save exception occurred in: ", e); + throw new CustomException(ErrorCode.INTERNAL_SERVER_ERROR); + } + } } diff --git a/src/main/java/seoultech/capstone/menjil/domain/follow/dto/request/FollowRequest.java b/src/main/java/seoultech/capstone/menjil/domain/follow/application/dto/request/FollowCreateServiceRequest.java similarity index 53% rename from src/main/java/seoultech/capstone/menjil/domain/follow/dto/request/FollowRequest.java rename to src/main/java/seoultech/capstone/menjil/domain/follow/application/dto/request/FollowCreateServiceRequest.java index 89a9029..c7a3a86 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/follow/dto/request/FollowRequest.java +++ b/src/main/java/seoultech/capstone/menjil/domain/follow/application/dto/request/FollowCreateServiceRequest.java @@ -1,6 +1,6 @@ -package seoultech.capstone.menjil.domain.follow.dto.request; +package seoultech.capstone.menjil.domain.follow.application.dto.request; -import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -9,8 +9,7 @@ @Getter @NoArgsConstructor -@AllArgsConstructor -public class FollowRequest { +public class FollowCreateServiceRequest { @NotBlank private String userNickname; @@ -18,15 +17,21 @@ public class FollowRequest { @NotBlank private String followNickname; - public static FollowRequest of(String userNickname, String followNickname) { - return new FollowRequest(userNickname, followNickname); + public static FollowCreateServiceRequest of(String userNickname, String followNickname) { + return new FollowCreateServiceRequest(userNickname, followNickname); + } + + @Builder + private FollowCreateServiceRequest(String userNickname, String followNickname) { + this.userNickname = userNickname; + this.followNickname = followNickname; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - FollowRequest that = (FollowRequest) o; + FollowCreateServiceRequest that = (FollowCreateServiceRequest) o; return Objects.equals(userNickname, that.userNickname) && Objects.equals(followNickname, that.followNickname); } diff --git a/src/main/java/seoultech/capstone/menjil/domain/following/api/FollowingController.java b/src/main/java/seoultech/capstone/menjil/domain/following/api/FollowingController.java index 1b38c9a..522772f 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/following/api/FollowingController.java +++ b/src/main/java/seoultech/capstone/menjil/domain/following/api/FollowingController.java @@ -10,8 +10,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import seoultech.capstone.menjil.domain.following.application.FollowingService; -import seoultech.capstone.menjil.domain.following.dto.response.FollowingMentorInfoResponse; -import seoultech.capstone.menjil.domain.following.dto.response.FollowingMentorResponse; +import seoultech.capstone.menjil.domain.following.application.dto.response.FollowingUserInfoResponse; +import seoultech.capstone.menjil.domain.following.application.dto.response.FollowingUserResponse; import seoultech.capstone.menjil.global.common.dto.ApiResponse; import seoultech.capstone.menjil.global.exception.SuccessCode; @@ -25,19 +25,20 @@ public class FollowingController { private final int PAGE_SIZE = 9; @GetMapping() - public ResponseEntity>> getAllFollowMentors(@RequestParam("nickname") String nickname, @PageableDefault(size = PAGE_SIZE, sort = {"createdDate"}, - direction = Sort.Direction.ASC) Pageable pageable) { + public ResponseEntity>> getAllFollowOfUsers( + @RequestParam("nickname") String nickname, + @PageableDefault(size = PAGE_SIZE, sort = {"createdDate"}, + direction = Sort.Direction.ASC) Pageable pageable) { return ResponseEntity.status(HttpStatus.OK) - .body(ApiResponse.success(SuccessCode.GET_ALL_FOLLOW_MENTOR_SUCCESS, - followingService.getAllFollowMentors(nickname, pageable))); + .body(ApiResponse.success(SuccessCode.GET_ALL_FOLLOW_USERS_SUCCESS, + followingService.getAllFollowOfUsers(nickname, pageable))); } @GetMapping("/info") - public ResponseEntity> getFollowMentorInfo( - @RequestParam("nickname") String nickname, + public ResponseEntity> getFollowUserInfo( @RequestParam("followNickname") String followNickname) { return ResponseEntity.status(HttpStatus.OK) - .body(ApiResponse.success(SuccessCode.GET_FOLLOW_MENTOR_INFO_SUCCESS, - followingService.getFollowMentorInfo(nickname, followNickname))); + .body(ApiResponse.success(SuccessCode.GET_FOLLOW_USER_INFO_SUCCESS, + followingService.getFollowUserInfo(followNickname))); } } diff --git a/src/main/java/seoultech/capstone/menjil/domain/following/application/FollowingService.java b/src/main/java/seoultech/capstone/menjil/domain/following/application/FollowingService.java index da2b78c..f59f55d 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/following/application/FollowingService.java +++ b/src/main/java/seoultech/capstone/menjil/domain/following/application/FollowingService.java @@ -15,11 +15,11 @@ import seoultech.capstone.menjil.domain.chat.domain.QaList; import seoultech.capstone.menjil.domain.follow.dao.FollowRepository; import seoultech.capstone.menjil.domain.follow.domain.Follow; -import seoultech.capstone.menjil.domain.following.dto.FollowingQaDto; -import seoultech.capstone.menjil.domain.following.dto.FollowingUserDto; -import seoultech.capstone.menjil.domain.following.dto.FollowingUserInfoDto; -import seoultech.capstone.menjil.domain.following.dto.response.FollowingMentorInfoResponse; -import seoultech.capstone.menjil.domain.following.dto.response.FollowingMentorResponse; +import seoultech.capstone.menjil.domain.following.application.dto.FollowingQaDto; +import seoultech.capstone.menjil.domain.following.application.dto.FollowingUserDto; +import seoultech.capstone.menjil.domain.following.application.dto.FollowingUserInfoDto; +import seoultech.capstone.menjil.domain.following.application.dto.response.FollowingUserInfoResponse; +import seoultech.capstone.menjil.domain.following.application.dto.response.FollowingUserResponse; import seoultech.capstone.menjil.global.exception.CustomException; import seoultech.capstone.menjil.global.exception.ErrorCode; import seoultech.capstone.menjil.global.handler.AwsS3Handler; @@ -44,11 +44,12 @@ public class FollowingService { private String BUCKET_NAME; @Transactional - public Page getAllFollowMentors(String nickname, Pageable pageable) { + public Page getAllFollowOfUsers(String nickname, Pageable pageable) { // get follows + // TODO: 여기도 User랑 Follow 사이에 연관관계 설정해야 하는 거 아닌가? Page page = followRepository.findFollowsByUserNickname(nickname, pageable); - Page followMentorInfoResponse = page.map(follow -> { + Page followMentorInfoResponse = page.map(follow -> { String followNickname = follow.getFollowNickname(); // 1. get follows @@ -70,17 +71,20 @@ public Page getAllFollowMentors(String nickname, Pageab // 4. get answers count Long answersCount = qaListRepository.countByMentorNicknameAndAnswerIsNotNull(followNickname); - return FollowingMentorResponse.of(followingUserDto, lastAnsweredMessages, followersCount, answersCount); + return FollowingUserResponse.of(followingUserDto, lastAnsweredMessages, followersCount, answersCount); }); return followMentorInfoResponse; } @Transactional - public FollowingMentorInfoResponse getFollowMentorInfo(String nickname, String followNickname) { + public FollowingUserInfoResponse getFollowUserInfo(String followNickname) { + // Exception 1: 사용자가 존재하지 않는 경우 User user = userRepository.findUserByNickname(followNickname) .orElseThrow(() -> new CustomException(ErrorCode.INTERNAL_SERVER_ERROR)); + // Exception 2: 팔로우 관계가 존재하지 않는 경우(고려하지 않기로 프론트와 협의됨) + // 1. 사용자 정보 FollowingUserInfoDto followingUserInfoDto = FollowingUserInfoDto.fromUserEntity(user); followingUserInfoDto.setImgUrl(String.valueOf(awsS3Handler.generatePresignedUrl( @@ -90,17 +94,15 @@ public FollowingMentorInfoResponse getFollowMentorInfo(String nickname, String f Long answersCount = qaListRepository.countByMentorNicknameAndAnswerIsNotNull(followNickname); // 3. 작성 질문/답변 목록 리스트 + // TODO: 조회수, 좋아요 추가 Sort sort = Sort.by(Sort.Order.asc("answer_time")); List followingQaDtos = qaListRepository.findQuestionAndAnswerWithMentorNickname(followNickname, sort) .stream() .map(q -> new FollowingQaDto(q.getQuestionOrigin(), q.getQuestionSummary(), - q.getAnswer(), q.getAnswerTime())) + q.getAnswer(), q.getAnswerTime(), q.getViews(), q.getLikes())) .collect(Collectors.toList()); - // TODO: 4. 추천 답변 개수 - // TODO: 5. 멘토링 후기 - - return FollowingMentorInfoResponse.of(followingUserInfoDto, answersCount, followingQaDtos); + return FollowingUserInfoResponse.of(followingUserInfoDto, answersCount, followingQaDtos); } protected List getLastAnsweredMessages(String mentorNickname) { diff --git a/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/FollowingQaDto.java b/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/FollowingQaDto.java new file mode 100644 index 0000000..114fa87 --- /dev/null +++ b/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/FollowingQaDto.java @@ -0,0 +1,34 @@ +package seoultech.capstone.menjil.domain.following.application.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@NoArgsConstructor +public class FollowingQaDto { + + private String questionOrigin; + private String questionSummary; + private String answer; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") + private LocalDateTime answerTime; + + private Long views; + private Long likes; + + @Builder + public FollowingQaDto(String questionOrigin, String questionSummary, String answer, + LocalDateTime answerTime, Long views, Long likes) { + this.questionOrigin = questionOrigin; + this.questionSummary = questionSummary; + this.answer = answer; + this.answerTime = answerTime; + this.views = views; + this.likes = likes; + } +} diff --git a/src/main/java/seoultech/capstone/menjil/domain/following/dto/FollowingUserDto.java b/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/FollowingUserDto.java similarity index 92% rename from src/main/java/seoultech/capstone/menjil/domain/following/dto/FollowingUserDto.java rename to src/main/java/seoultech/capstone/menjil/domain/following/application/dto/FollowingUserDto.java index a4249de..d2993e8 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/following/dto/FollowingUserDto.java +++ b/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/FollowingUserDto.java @@ -1,4 +1,4 @@ -package seoultech.capstone.menjil.domain.following.dto; +package seoultech.capstone.menjil.domain.following.application.dto; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/seoultech/capstone/menjil/domain/following/dto/FollowingUserInfoDto.java b/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/FollowingUserInfoDto.java similarity index 52% rename from src/main/java/seoultech/capstone/menjil/domain/following/dto/FollowingUserInfoDto.java rename to src/main/java/seoultech/capstone/menjil/domain/following/application/dto/FollowingUserInfoDto.java index 01f7e69..14463e9 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/following/dto/FollowingUserInfoDto.java +++ b/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/FollowingUserInfoDto.java @@ -1,13 +1,12 @@ -package seoultech.capstone.menjil.domain.following.dto; +package seoultech.capstone.menjil.domain.following.application.dto; -import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import seoultech.capstone.menjil.domain.auth.domain.User; @Getter @NoArgsConstructor -@AllArgsConstructor public class FollowingUserInfoDto { private String nickname; @@ -35,8 +34,28 @@ public static FollowingUserInfoDto fromUserEntity(User user) { return new FollowingUserInfoDto(user.getNickname(), user.getCompany(), user.getField(), user.getSchool(), user.getMajor(), user.getSubMajor(), user.getMinor(), user.getTechStack(), user.getImgUrl(), - user.getOptionInfo().getCareer(), user.getOptionInfo().getCertificate(), - user.getOptionInfo().getAwards(), user.getOptionInfo().getActivity() + user.getCareer(), user.getCertificate(), + user.getAwards(), user.getActivity() ); } + + @Builder + private FollowingUserInfoDto(String nickname, String company, String field, String school, + String major, String subMajor, String minor, + String techStack, String imgUrl, String career, + String certificate, String awards, String activity) { + this.nickname = nickname; + this.company = company; + this.field = field; + this.school = school; + this.major = major; + this.subMajor = subMajor; + this.minor = minor; + this.techStack = techStack; + this.imgUrl = imgUrl; + this.career = career; + this.certificate = certificate; + this.awards = awards; + this.activity = activity; + } } diff --git a/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/response/FollowingUserInfoResponse.java b/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/response/FollowingUserInfoResponse.java new file mode 100644 index 0000000..1b8c66b --- /dev/null +++ b/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/response/FollowingUserInfoResponse.java @@ -0,0 +1,25 @@ +package seoultech.capstone.menjil.domain.following.application.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import seoultech.capstone.menjil.domain.following.application.dto.FollowingUserInfoDto; +import seoultech.capstone.menjil.domain.following.application.dto.FollowingQaDto; + +import java.util.List; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class FollowingUserInfoResponse { + + private FollowingUserInfoDto followingUserInfoDto; + private Long answersCount; + private List answers; + + public static FollowingUserInfoResponse of(FollowingUserInfoDto userInfo, + Long answersCount, List answers) { + return new FollowingUserInfoResponse(userInfo, answersCount, answers); + } +} + diff --git a/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/response/FollowingUserResponse.java b/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/response/FollowingUserResponse.java new file mode 100644 index 0000000..4a66689 --- /dev/null +++ b/src/main/java/seoultech/capstone/menjil/domain/following/application/dto/response/FollowingUserResponse.java @@ -0,0 +1,24 @@ +package seoultech.capstone.menjil.domain.following.application.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import seoultech.capstone.menjil.domain.following.application.dto.FollowingUserDto; + +import java.util.List; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class FollowingUserResponse { + + private FollowingUserDto followingUserDto; + private List lastAnsweredMessages; // 가장 최근에 답변한 질문(최대 2개) + private Long followersCount; + private Long answersCount; + + public static FollowingUserResponse of(FollowingUserDto userInfo, List lastAnsweredMessages, + Long followersCount, Long answersCount) { + return new FollowingUserResponse(userInfo, lastAnsweredMessages, followersCount, answersCount); + } +} diff --git a/src/main/java/seoultech/capstone/menjil/domain/following/dto/FollowingQaDto.java b/src/main/java/seoultech/capstone/menjil/domain/following/dto/FollowingQaDto.java deleted file mode 100644 index a62d8da..0000000 --- a/src/main/java/seoultech/capstone/menjil/domain/following/dto/FollowingQaDto.java +++ /dev/null @@ -1,21 +0,0 @@ -package seoultech.capstone.menjil.domain.following.dto; - -import com.fasterxml.jackson.annotation.JsonFormat; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Getter -@NoArgsConstructor -@AllArgsConstructor -public class FollowingQaDto { - - private String questionOrigin; - private String questionSummary; - private String answer; - - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") - private LocalDateTime answerTime; -} diff --git a/src/main/java/seoultech/capstone/menjil/domain/following/dto/response/FollowingMentorInfoResponse.java b/src/main/java/seoultech/capstone/menjil/domain/following/dto/response/FollowingMentorInfoResponse.java deleted file mode 100644 index 627d9d3..0000000 --- a/src/main/java/seoultech/capstone/menjil/domain/following/dto/response/FollowingMentorInfoResponse.java +++ /dev/null @@ -1,25 +0,0 @@ -package seoultech.capstone.menjil.domain.following.dto.response; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import seoultech.capstone.menjil.domain.following.dto.FollowingQaDto; -import seoultech.capstone.menjil.domain.following.dto.FollowingUserInfoDto; - -import java.util.List; - -@Getter -@NoArgsConstructor -@AllArgsConstructor -public class FollowingMentorInfoResponse { - - private FollowingUserInfoDto followingUserInfoDto; - private Long answersCount; - private List answers; - - public static FollowingMentorInfoResponse of(FollowingUserInfoDto userInfo, - Long answersCount, List answers) { - return new FollowingMentorInfoResponse(userInfo, answersCount, answers); - } -} - diff --git a/src/main/java/seoultech/capstone/menjil/domain/following/dto/response/FollowingMentorResponse.java b/src/main/java/seoultech/capstone/menjil/domain/following/dto/response/FollowingMentorResponse.java deleted file mode 100644 index ae17ae9..0000000 --- a/src/main/java/seoultech/capstone/menjil/domain/following/dto/response/FollowingMentorResponse.java +++ /dev/null @@ -1,24 +0,0 @@ -package seoultech.capstone.menjil.domain.following.dto.response; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import seoultech.capstone.menjil.domain.following.dto.FollowingUserDto; - -import java.util.List; - -@Getter -@NoArgsConstructor -@AllArgsConstructor -public class FollowingMentorResponse { - - private FollowingUserDto followingUserDto; - private List lastAnsweredMessages; // 가장 최근에 답변한 질문(최대 2개) - private Long followersCount; - private Long answersCount; - - public static FollowingMentorResponse of(FollowingUserDto userInfo, List lastAnsweredMessages, - Long followersCount, Long answersCount) { - return new FollowingMentorResponse(userInfo, lastAnsweredMessages, followersCount, answersCount); - } -} diff --git a/src/main/java/seoultech/capstone/menjil/domain/main/api/MainPageController.java b/src/main/java/seoultech/capstone/menjil/domain/main/api/MainPageController.java index ed67cc7..f8e4aaa 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/main/api/MainPageController.java +++ b/src/main/java/seoultech/capstone/menjil/domain/main/api/MainPageController.java @@ -15,8 +15,8 @@ import seoultech.capstone.menjil.domain.chat.application.RoomService; import seoultech.capstone.menjil.domain.chat.dto.response.RoomInfoResponse; import seoultech.capstone.menjil.domain.main.application.MainPageService; -import seoultech.capstone.menjil.domain.main.dto.response.FollowUserResponse; -import seoultech.capstone.menjil.domain.main.dto.response.MentorInfoResponse; +import seoultech.capstone.menjil.domain.main.application.dto.response.FollowUserResponse; +import seoultech.capstone.menjil.domain.main.application.dto.response.UserInfoResponse; import seoultech.capstone.menjil.global.common.dto.ApiResponse; import seoultech.capstone.menjil.global.exception.SuccessCode; @@ -33,11 +33,11 @@ public class MainPageController { private final int pageSize = 3; @GetMapping("/mentors") - public ResponseEntity>> getMentors(String nickname, @PageableDefault(size = pageSize, sort = {"createdDate", "nickname"}, + public ResponseEntity>> getMentors(String nickname, @PageableDefault(size = pageSize, sort = {"createdDate", "nickname"}, direction = Sort.Direction.ASC) Pageable pageable) { return ResponseEntity.status(HttpStatus.OK) - .body(ApiResponse.success(SuccessCode.GET_MENTOR_LIST_AVAILABLE, mainPageService.getMentors(nickname, pageable))); + .body(ApiResponse.success(SuccessCode.GET_USERS_AVAILABLE, mainPageService.getMentors(nickname, pageable))); } @GetMapping("/rooms") diff --git a/src/main/java/seoultech/capstone/menjil/domain/main/application/MainPageService.java b/src/main/java/seoultech/capstone/menjil/domain/main/application/MainPageService.java index c0a6268..80cb344 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/main/application/MainPageService.java +++ b/src/main/java/seoultech/capstone/menjil/domain/main/application/MainPageService.java @@ -10,13 +10,12 @@ import org.springframework.stereotype.Service; import seoultech.capstone.menjil.domain.auth.dao.UserRepository; import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; import seoultech.capstone.menjil.domain.chat.dao.QaListRepository; import seoultech.capstone.menjil.domain.chat.domain.QaList; import seoultech.capstone.menjil.domain.follow.dao.FollowRepository; import seoultech.capstone.menjil.domain.follow.domain.Follow; -import seoultech.capstone.menjil.domain.main.dto.response.FollowUserResponse; -import seoultech.capstone.menjil.domain.main.dto.response.MentorInfoResponse; +import seoultech.capstone.menjil.domain.main.application.dto.response.FollowUserResponse; +import seoultech.capstone.menjil.domain.main.application.dto.response.UserInfoResponse; import seoultech.capstone.menjil.global.handler.AwsS3Handler; import java.time.Duration; @@ -41,14 +40,13 @@ public class MainPageService { private String BUCKET_NAME; /** - * 멘토들을 가져오는 메서드 * nickname은, 추후 멘토 추천 알고리즘 사용시 필요할 수 있으므로, 우선 받도록 하였으나. * 현재 수행하는 기능은 없다. */ - public Page getMentors(String nickname, Pageable pageable) { - Page page = userRepository.findUsersByRole(UserRole.MENTOR, pageable); - Page mentorInfoResponse = page.map(user -> { - MentorInfoResponse dto = MentorInfoResponse.fromUserEntity(user); + public Page getMentors(String nickname, Pageable pageable) { + Page page = userRepository.findAll(pageable); + Page mentorInfoResponse = page.map(user -> { + UserInfoResponse dto = UserInfoResponse.fromUserEntity(user); // set AWS S3 presigned url dto.setImgUrl(String.valueOf(awsS3Handler.generatePresignedUrl( diff --git a/src/main/java/seoultech/capstone/menjil/domain/main/dto/response/FollowUserResponse.java b/src/main/java/seoultech/capstone/menjil/domain/main/application/dto/response/FollowUserResponse.java similarity index 90% rename from src/main/java/seoultech/capstone/menjil/domain/main/dto/response/FollowUserResponse.java rename to src/main/java/seoultech/capstone/menjil/domain/main/application/dto/response/FollowUserResponse.java index b270f93..2e442c0 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/main/dto/response/FollowUserResponse.java +++ b/src/main/java/seoultech/capstone/menjil/domain/main/application/dto/response/FollowUserResponse.java @@ -1,4 +1,4 @@ -package seoultech.capstone.menjil.domain.main.dto.response; +package seoultech.capstone.menjil.domain.main.application.dto.response; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/seoultech/capstone/menjil/domain/main/dto/response/MentorInfoResponse.java b/src/main/java/seoultech/capstone/menjil/domain/main/application/dto/response/UserInfoResponse.java similarity index 79% rename from src/main/java/seoultech/capstone/menjil/domain/main/dto/response/MentorInfoResponse.java rename to src/main/java/seoultech/capstone/menjil/domain/main/application/dto/response/UserInfoResponse.java index 8ff8ecb..af4d41a 100644 --- a/src/main/java/seoultech/capstone/menjil/domain/main/dto/response/MentorInfoResponse.java +++ b/src/main/java/seoultech/capstone/menjil/domain/main/application/dto/response/UserInfoResponse.java @@ -1,4 +1,4 @@ -package seoultech.capstone.menjil.domain.main.dto.response; +package seoultech.capstone.menjil.domain.main.application.dto.response; import lombok.AllArgsConstructor; import lombok.Getter; @@ -10,7 +10,7 @@ @Getter @NoArgsConstructor @AllArgsConstructor -public class MentorInfoResponse { +public class UserInfoResponse { /** * 메인 화면에 보여질 멘토의 정보를 담는 DTO. * User Entity로부터 정보를 가져온다. @@ -23,8 +23,8 @@ public class MentorInfoResponse { private String imgUrl; private List lastAnsweredMessages; // 가장 최근에 답변한 질문(최대 2개) - public static MentorInfoResponse fromUserEntity(User user) { - return new MentorInfoResponse(user.getNickname(), user.getMajor(), user.getCompany(), + public static UserInfoResponse fromUserEntity(User user) { + return new UserInfoResponse(user.getNickname(), user.getMajor(), user.getCompany(), user.getField(), user.getTechStack(), user.getImgUrl(), null); } diff --git a/src/main/java/seoultech/capstone/menjil/global/config/JpaAuditingConfig.java b/src/main/java/seoultech/capstone/menjil/global/config/JpaAuditingConfig.java new file mode 100644 index 0000000..d54ed73 --- /dev/null +++ b/src/main/java/seoultech/capstone/menjil/global/config/JpaAuditingConfig.java @@ -0,0 +1,9 @@ +package seoultech.capstone.menjil.global.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@EnableJpaAuditing +@Configuration +public class JpaAuditingConfig { +} diff --git a/src/main/java/seoultech/capstone/menjil/global/config/WebConfig.java b/src/main/java/seoultech/capstone/menjil/global/config/WebConfig.java index f969fae..33659fa 100644 --- a/src/main/java/seoultech/capstone/menjil/global/config/WebConfig.java +++ b/src/main/java/seoultech/capstone/menjil/global/config/WebConfig.java @@ -4,6 +4,7 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import seoultech.capstone.menjil.domain.auth.jwt.JwtTokenProvider; import seoultech.capstone.menjil.global.filter.CustomCorsFilter; @@ -34,6 +35,15 @@ public FilterRegistrationBean jwtAuthenticationFilter( return registrationBean; } + /** + * /docs/index.html 접근이 가능하도록 하는 설정이다. + */ + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/docs/**") + .addResourceLocations("classpath:/static/docs/"); + } + /* @Override public void addCorsMappings(CorsRegistry corsRegistry) { diff --git a/src/main/java/seoultech/capstone/menjil/global/exception/ErrorCode.java b/src/main/java/seoultech/capstone/menjil/global/exception/ErrorCode.java index 753c2ff..4279079 100644 --- a/src/main/java/seoultech/capstone/menjil/global/exception/ErrorCode.java +++ b/src/main/java/seoultech/capstone/menjil/global/exception/ErrorCode.java @@ -14,12 +14,11 @@ public enum ErrorCode { INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "C003", "서버 내부 오류"), // auth - NICKNAME_DUPLICATED(HttpStatus.CONFLICT, "A001", "이미 존재하는 닉네임입니다"), - USER_DUPLICATED(HttpStatus.CONFLICT, "A002", "이미 가입된 사용자입니다"), - NICKNAME_CONTAINS_BLANK(HttpStatus.BAD_REQUEST, "A003", "닉네임에 공백을 사용할 수 없습니다"), - NICKNAME_CONTAINS_SPECIAL_CHARACTER(HttpStatus.BAD_REQUEST, "A004", "닉네임에 특수 문자를 사용할 수 없습니다"), + NICKNAME_ALREADY_EXISTED(HttpStatus.CONFLICT, "A001", "이미 존재하는 닉네임입니다"), + USER_ALREADY_EXISTED(HttpStatus.CONFLICT, "A002", "이미 가입된 사용자입니다"), + NICKNAME_FORMAT_IS_WRONG(HttpStatus.BAD_REQUEST, "A004", "닉네임은 알파벳, 한글, 숫자만 포함해야 하며 공백을 포함할 수 없습니다."), SIGNUP_INPUT_INVALID(HttpStatus.BAD_REQUEST, "A005", "Input type is invalid"), - PROVIDER_NOT_ALLOWED(HttpStatus.BAD_REQUEST, "A006", "가입 양식이 잘못되었습니다. 구글이나 카카오로 가입 요청을 해주세요"), + PROVIDER_NOT_ALLOWED(HttpStatus.BAD_REQUEST, "A006", "플랫폼 양식이 잘못되었습니다. 구글이나 카카오 플랫폼으로 요청을 해주세요"), USER_NOT_EXISTED(HttpStatus.BAD_REQUEST, "A007", "가입된 사용자가 존재하지 않습니다"), // main @@ -32,7 +31,8 @@ public enum ErrorCode { MESSAGE_TYPE_INPUT_INVALID(HttpStatus.CONFLICT, "CH04", "지정된 Message Type이 입력되지 않았습니다"), MENTEE_NICKNAME_NOT_EXISTED(HttpStatus.BAD_REQUEST, "CH05", "멘티의 닉네임 정보가 존재하지 않습니다"), MENTOR_NICKNAME_NOT_EXISTED(HttpStatus.BAD_REQUEST, "CH06", "멘토의 닉네임 정보가 존재하지 않습니다"), - QALIST_NOT_EXISTED(HttpStatus.BAD_REQUEST, "CH07", "채팅 메시지 id가 유효하지 않습니다"); + CHAT_MESSAGE_NOT_EXISTED(HttpStatus.BAD_REQUEST, "CH07", "채팅 메시지 id가 유효하지 않습니다"), + QALIST_NOT_EXISTED(HttpStatus.BAD_REQUEST, "CH07", "질문답변 메시지 객체 id가 유효하지 않습니다"); private final HttpStatus httpStatus; private final String type; diff --git a/src/main/java/seoultech/capstone/menjil/global/exception/ErrorIntValue.java b/src/main/java/seoultech/capstone/menjil/global/exception/ErrorIntValue.java index e13bdba..2f6bbf2 100644 --- a/src/main/java/seoultech/capstone/menjil/global/exception/ErrorIntValue.java +++ b/src/main/java/seoultech/capstone/menjil/global/exception/ErrorIntValue.java @@ -8,7 +8,7 @@ public enum ErrorIntValue { // auth - USER_ALREADY_IN_DB(-1), + USER_ALREADY_EXISTED(-1), // chat TIME_INPUT_INVALID(-11), diff --git a/src/main/java/seoultech/capstone/menjil/global/exception/SuccessCode.java b/src/main/java/seoultech/capstone/menjil/global/exception/SuccessCode.java index e990742..2ea5d73 100644 --- a/src/main/java/seoultech/capstone/menjil/global/exception/SuccessCode.java +++ b/src/main/java/seoultech/capstone/menjil/global/exception/SuccessCode.java @@ -26,7 +26,7 @@ public enum SuccessCode { // main GET_USER_ROOMS_AVAILABLE(HttpStatus.OK.value(), "사용자의 채팅방 목록을 불러오는데 성공하였습니다"), - GET_MENTOR_LIST_AVAILABLE(HttpStatus.OK.value(), "멘토 리스트를 불러오는데 성공하였습니다"), + GET_USERS_AVAILABLE(HttpStatus.OK.value(), "멘토 리스트를 불러오는데 성공하였습니다"), FOLLOWS_NOT_EXISTS(HttpStatus.OK.value(), "관심 멘토 목록이 존재하지 않습니다"), FOLLOWS_EXISTS(HttpStatus.OK.value(), "관심 멘토 목록을 불러오는데 성공하였습니다"), @@ -34,8 +34,8 @@ public enum SuccessCode { FOLLOW_CHECK_SUCCESS(HttpStatus.OK.value(), "팔로우 조회에 성공하셨습니다"), // following - GET_ALL_FOLLOW_MENTOR_SUCCESS(HttpStatus.OK.value(), "팔로우 멘토 리스트를 불러오는데 성공하였습니다"), - GET_FOLLOW_MENTOR_INFO_SUCCESS(HttpStatus.OK.value(), "팔로우 멘토 정보를 불러오는데 성공하였습니다"), + GET_ALL_FOLLOW_USERS_SUCCESS(HttpStatus.OK.value(), "팔로우 목록을 불러오는데 성공하였습니다"), + GET_FOLLOW_USER_INFO_SUCCESS(HttpStatus.OK.value(), "팔로우한 사용자 정보를 불러오는데 성공하였습니다"), /** * 201 CREATED diff --git a/src/test/java/seoultech/capstone/menjil/docs/RestDocsSupport.java b/src/test/java/seoultech/capstone/menjil/docs/RestDocsSupport.java new file mode 100644 index 0000000..d29e90d --- /dev/null +++ b/src/test/java/seoultech/capstone/menjil/docs/RestDocsSupport.java @@ -0,0 +1,36 @@ +package seoultech.capstone.menjil.docs; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.filter.CharacterEncodingFilter; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; + + +@ExtendWith(RestDocumentationExtension.class) +public abstract class RestDocsSupport { + + protected MockMvc mockMvc; + + @BeforeEach + void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) { + this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) + .apply(documentationConfiguration(restDocumentation) + .snippets() + .withEncoding("UTF-8") + .and() + .operationPreprocessors() + .withRequestDefaults(prettyPrint()) // JSON 예쁘게 출력prettyPrint()) // show json pretty in asciidoc + .withResponseDefaults(prettyPrint())) // show json pretty in asciidoc + .addFilters(new CharacterEncodingFilter("UTF-8", true)) // 한글 깨짐 방지 + .build(); + } +} + + diff --git a/src/test/java/seoultech/capstone/menjil/docs/auth/AuthControllerDocsTest.java b/src/test/java/seoultech/capstone/menjil/docs/auth/AuthControllerDocsTest.java new file mode 100644 index 0000000..8ee0af2 --- /dev/null +++ b/src/test/java/seoultech/capstone/menjil/docs/auth/AuthControllerDocsTest.java @@ -0,0 +1,650 @@ +package seoultech.capstone.menjil.docs.auth; + +import com.google.gson.Gson; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.payload.JsonFieldType; +import seoultech.capstone.menjil.docs.RestDocsSupport; +import seoultech.capstone.menjil.domain.auth.api.AuthController; +import seoultech.capstone.menjil.domain.auth.api.dto.request.SignInRequest; +import seoultech.capstone.menjil.domain.auth.api.dto.request.SignUpRequest; +import seoultech.capstone.menjil.domain.auth.application.AuthService; +import seoultech.capstone.menjil.domain.auth.application.dto.request.SignInServiceRequest; +import seoultech.capstone.menjil.domain.auth.application.dto.response.SignInResponse; +import seoultech.capstone.menjil.global.config.WebConfig; +import seoultech.capstone.menjil.global.exception.*; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.payload.JsonFieldType.STRING; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(controllers = AuthController.class, + excludeFilters = { + @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = WebConfig.class) + }) +public class AuthControllerDocsTest extends RestDocsSupport { + + @MockBean + private AuthService authService; + + @Autowired + private Gson gson; + + @Test + @DisplayName("case 1: 사용자가 존재하지 않는 경우") + void checkSignupIsAvailable() throws Exception { + // given + String email = "testUser1@gmail.com"; + String provider = "google"; + + // when + Mockito.when(authService.findUserInDb(email, provider)) + .thenReturn(SuccessIntValue.SUCCESS.getValue()); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/auth/signup") + .queryParam("email", email) + .queryParam("provider", provider)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code", is(SuccessCode.SIGNUP_AVAILABLE.getCode()))) + .andDo(print()) + .andDo(document("api/auth/signup-is-available/true", + requestParameters( + parameterWithName("email").description("사용자 이메일"), + parameterWithName("provider").description("이메일 플랫폼") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("case 2: 사용자가 이미 존재하는 경우, 회원가입이 불가능하다") + void checkSignupIsAvailable_user_already_existed() throws Exception { + // given + String email = "testUser2@gmail.com"; + String provider = "google"; + + // when + Mockito.when(authService.findUserInDb(email, provider)).thenReturn(ErrorIntValue.USER_ALREADY_EXISTED.getValue()); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/auth/signup") + .queryParam("email", email) + .queryParam("provider", provider)) + .andExpect(status().isConflict()) + .andExpect(jsonPath("$.code", is(ErrorCode.USER_ALREADY_EXISTED.getHttpStatus().value()))) + .andDo(print()) + .andDo(document("api/auth/signup-is-available/false", + requestParameters( + parameterWithName("email").description("사용자 이메일"), + parameterWithName("provider").description("이메일 플랫폼") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("닉네임 검증; 공백과 특수문자가 없는 경우 정상 로직 수행") + void isNicknameAvailable() throws Exception { + // given + String nickname = "test33AA"; + + // when + Mockito.when(authService.findNicknameInDb(nickname)).thenReturn(SuccessIntValue.SUCCESS.getValue()); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/auth/check-nickname") + .queryParam("nickname", nickname)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code", is(SuccessCode.NICKNAME_AVAILABLE.getCode()))) + .andDo(print()) + .andDo(document("api/auth/check-nickname/true", + requestParameters( + parameterWithName("nickname").description("닉네임") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("닉네임 검증; 공백이 들어오면 CustomException 을 발생시킨다") + void isNicknameAvailable_nickname_is_blank() throws Exception { + // given + String nickname = " "; + + // when // then + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/auth/check-nickname") + .queryParam("nickname", nickname)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code", + is(ErrorCode.NICKNAME_FORMAT_IS_WRONG.getHttpStatus().value()))) + .andDo(print()) + .andDo(document("api/auth/check-nickname/false-blank", + requestParameters( + parameterWithName("nickname").description("닉네임") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("닉네임 검증; 특수문자가 들어오면 CustomException 을 발생시킨다.") + void isNicknameAvailable_nickname_has_special_char() throws Exception { + String nickname = "easd##!@@@@asdf"; + + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/auth/check-nickname") + .queryParam("nickname", nickname)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code", + is(ErrorCode.NICKNAME_FORMAT_IS_WRONG.getHttpStatus().value()))) + .andDo(print()) + .andDo(document("api/auth/check-nickname/false-special-character", + requestParameters( + parameterWithName("nickname").description("닉네임") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("닉네임 검증; 닉네임이 db에 이미 존재하는 경우 ErrorCode.NICKNAME_DUPLICATED 리턴") + void isNicknameAvailable_nickname_already_existed() throws Exception { + String nickname = "NicknameExistsInDB"; + + Mockito.when(authService.findNicknameInDb(nickname)).thenReturn(ErrorIntValue.USER_ALREADY_EXISTED.getValue()); + + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/auth/check-nickname") + .queryParam("nickname", nickname)) + .andExpect(status().isConflict()) + .andExpect(jsonPath("$.code", is(ErrorCode.NICKNAME_ALREADY_EXISTED.getHttpStatus().value()))) + .andDo(print()) + .andDo(document("api/auth/check-nickname/false-conflict", + requestParameters( + parameterWithName("nickname").description("닉네임") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("회원가입 요청이 정상적으로 된 경우 SuccessCode.SIGNUP_SUCCESS 리턴") + void signUp() throws Exception { + // given + SignUpRequest signUpReqDto = createSignUpReqDto("google_1122334455", "test@google.com", "google", + "user11", 1999, 3, "서울과학기술대학교", 3); + String content = gson.toJson(signUpReqDto); + + // when + Mockito.doNothing().when(authService).signUp(signUpReqDto.toServiceRequest()); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/auth/signup") + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.code", is(SuccessCode.SIGNUP_SUCCESS.getCode()))) + .andDo(print()) + .andDo(document("api/auth/signup/true", + requestFields( + fieldWithPath("userId").type(JsonFieldType.STRING) + .description("provider + '_' + random int(10자리). 무작위로 생성. e.g) google_1122334455"), + fieldWithPath("email").type(JsonFieldType.STRING) + .description("이메일 주소"), + fieldWithPath("provider").type(JsonFieldType.STRING) + .description("이메일 플랫폼"), + fieldWithPath("nickname").type(JsonFieldType.STRING) + .description("닉네임"), + fieldWithPath("birthYear").type(JsonFieldType.NUMBER) + .description("생년. YYYY"), + fieldWithPath("birthMonth").type(JsonFieldType.NUMBER) + .description("생월. MM"), + fieldWithPath("school").type(JsonFieldType.STRING) + .description("대학교"), + fieldWithPath("score").type(JsonFieldType.NUMBER) + .description("학점: 0, 1, 2, 3, 4"), + fieldWithPath("scoreRange").type(JsonFieldType.STRING) + .description("초반, 중반, 후반"), + fieldWithPath("graduateDate").type(JsonFieldType.NUMBER) + .description("졸업년도. YYYY"), + fieldWithPath("graduateMonth").type(JsonFieldType.NUMBER) + .description("졸업월. MM"), + fieldWithPath("major").type(JsonFieldType.STRING) + .description("본전공"), + fieldWithPath("subMajor").type(JsonFieldType.STRING) + .description("복수전공. null 값을 허용합니다"), + fieldWithPath("minor").type(JsonFieldType.STRING) + .description("부전공. null 값을 허용합니다"), + fieldWithPath("company").type(JsonFieldType.STRING) + .description("재직 중인 회사. 회사 정보가 없는 경우 null이 될 수 있습니다."), + fieldWithPath("companyYear").type(JsonFieldType.NUMBER) + .description("첫 입사 년도. 정보가 없는 경우 0값을 보내주세요"), + fieldWithPath("field").type(JsonFieldType.STRING) + .description("관심 분아. 여러 개면 ','로 구분] 프론트엔드, 백엔드, ..."), + fieldWithPath("techStack").type(JsonFieldType.STRING) + .description("기술 스택. 여러 개면 ','로 구분"), + fieldWithPath("career").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("certificate").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("awards").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("activity").type(JsonFieldType.STRING) + .description("null 값을 허용합니다") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(STRING).description("응답 데이터: 가입이 처리된 이메일") + ) + )); + } + + @Test + @DisplayName("회원가입 요청 시 닉네임 검증: 닉네임에 공백이 포함된 경우 CustomException 발생") + void signUp_nickname_contains_blank() throws Exception { + SignUpRequest signUpReqDto = createSignUpReqDto("google_1122334455", "test@google.com", "google", + "user One", 1999, 3, "서울과학기술대학교", 3); + String content = gson.toJson(signUpReqDto); + + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/auth/signup") + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code", is(400))) + .andDo(print()) + .andDo(document("api/auth/signup/false-blank", + requestFields( + fieldWithPath("userId").type(JsonFieldType.STRING) + .description("provider + '_' + random int(10자리). 무작위로 생성. e.g) google_1122334455"), + fieldWithPath("email").type(JsonFieldType.STRING) + .description("이메일 주소"), + fieldWithPath("provider").type(JsonFieldType.STRING) + .description("이메일 플랫폼"), + fieldWithPath("nickname").type(JsonFieldType.STRING) + .description("닉네임"), + fieldWithPath("birthYear").type(JsonFieldType.NUMBER) + .description("생년. YYYY"), + fieldWithPath("birthMonth").type(JsonFieldType.NUMBER) + .description("생월. MM"), + fieldWithPath("school").type(JsonFieldType.STRING) + .description("대학교"), + fieldWithPath("score").type(JsonFieldType.NUMBER) + .description("학점: 0, 1, 2, 3, 4"), + fieldWithPath("scoreRange").type(JsonFieldType.STRING) + .description("초반, 중반, 후반"), + fieldWithPath("graduateDate").type(JsonFieldType.NUMBER) + .description("졸업년도. YYYY"), + fieldWithPath("graduateMonth").type(JsonFieldType.NUMBER) + .description("졸업월. MM"), + fieldWithPath("major").type(JsonFieldType.STRING) + .description("본전공"), + fieldWithPath("subMajor").type(JsonFieldType.STRING) + .description("복수전공. null 값을 허용합니다"), + fieldWithPath("minor").type(JsonFieldType.STRING) + .description("부전공. null 값을 허용합니다"), + fieldWithPath("company").type(JsonFieldType.STRING) + .description("재직 중인 회사. 회사 정보가 없는 경우 null이 될 수 있습니다."), + fieldWithPath("companyYear").type(JsonFieldType.NUMBER) + .description("첫 입사 년도. 정보가 없는 경우 0값을 보내주세요"), + fieldWithPath("field").type(JsonFieldType.STRING) + .description("관심 분아. 여러 개면 ','로 구분] 프론트엔드, 백엔드, ..."), + fieldWithPath("techStack").type(JsonFieldType.STRING) + .description("기술 스택. 여러 개면 ','로 구분"), + fieldWithPath("career").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("certificate").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("awards").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("activity").type(JsonFieldType.STRING) + .description("null 값을 허용합니다") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("회원가입 요청 시 닉네임 검증: 닉네임에 특수문자가 포함된 경우 CustomException 발생") + void signUp_nickname_contains_character() throws Exception { + SignUpRequest signUpReqDto = createSignUpReqDto("google_1122334455", "test@google.com", "google", + "user@@!!!One", 1999, 3, "서울과학기술대학교", 3); + String content = gson.toJson(signUpReqDto); + + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/auth/signup") + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code", is(400))) + .andDo(print()) + .andDo(document("api/auth/signup/false-special-character", + requestFields( + fieldWithPath("userId").type(JsonFieldType.STRING) + .description("provider + '_' + random int(10자리). 무작위로 생성. e.g) google_1122334455"), + fieldWithPath("email").type(JsonFieldType.STRING) + .description("이메일 주소"), + fieldWithPath("provider").type(JsonFieldType.STRING) + .description("이메일 플랫폼"), + fieldWithPath("nickname").type(JsonFieldType.STRING) + .description("닉네임"), + fieldWithPath("birthYear").type(JsonFieldType.NUMBER) + .description("생년. YYYY"), + fieldWithPath("birthMonth").type(JsonFieldType.NUMBER) + .description("생월. MM"), + fieldWithPath("school").type(JsonFieldType.STRING) + .description("대학교"), + fieldWithPath("score").type(JsonFieldType.NUMBER) + .description("학점: 0, 1, 2, 3, 4"), + fieldWithPath("scoreRange").type(JsonFieldType.STRING) + .description("초반, 중반, 후반"), + fieldWithPath("graduateDate").type(JsonFieldType.NUMBER) + .description("졸업년도. YYYY"), + fieldWithPath("graduateMonth").type(JsonFieldType.NUMBER) + .description("졸업월. MM"), + fieldWithPath("major").type(JsonFieldType.STRING) + .description("본전공"), + fieldWithPath("subMajor").type(JsonFieldType.STRING) + .description("복수전공. null 값을 허용합니다"), + fieldWithPath("minor").type(JsonFieldType.STRING) + .description("부전공. null 값을 허용합니다"), + fieldWithPath("company").type(JsonFieldType.STRING) + .description("재직 중인 회사. 회사 정보가 없는 경우 null이 될 수 있습니다."), + fieldWithPath("companyYear").type(JsonFieldType.NUMBER) + .description("첫 입사 년도. 정보가 없는 경우 0값을 보내주세요"), + fieldWithPath("field").type(JsonFieldType.STRING) + .description("관심 분아. 여러 개면 ','로 구분] 프론트엔드, 백엔드, ..."), + fieldWithPath("techStack").type(JsonFieldType.STRING) + .description("기술 스택. 여러 개면 ','로 구분"), + fieldWithPath("career").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("certificate").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("awards").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("activity").type(JsonFieldType.STRING) + .description("null 값을 허용합니다") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("회원가입 요청 시 닉네임 검증: 값이 null 인 경우 @NotBlank") + void signUp_nickname_is_Null() throws Exception { + SignUpRequest signUpReqDto = createSignUpReqDto("google_1122334455", "test@google.com", "google", + null, 1999, 3, "서울과학기술대학교", 3); + String content = gson.toJson(signUpReqDto); + + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/auth/signup") + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code", is(400))) + .andDo(print()) + .andDo(document("api/auth/signup/false-null", + requestFields( + fieldWithPath("userId").type(JsonFieldType.STRING) + .description("provider + '_' + random int(10자리). 무작위로 생성. e.g) google_1122334455"), + fieldWithPath("email").type(JsonFieldType.STRING) + .description("이메일 주소"), + fieldWithPath("provider").type(JsonFieldType.STRING) + .description("이메일 플랫폼"), + // NULL을 넣었으므로, 이 부분 주석 처리 +// fieldWithPath("nickname").type(JsonFieldType.NULL) +// .description("닉네임"), + fieldWithPath("birthYear").type(JsonFieldType.NUMBER) + .description("생년. YYYY"), + fieldWithPath("birthMonth").type(JsonFieldType.NUMBER) + .description("생월. MM"), + fieldWithPath("school").type(JsonFieldType.STRING) + .description("대학교"), + fieldWithPath("score").type(JsonFieldType.NUMBER) + .description("학점: 0, 1, 2, 3, 4"), + fieldWithPath("scoreRange").type(JsonFieldType.STRING) + .description("초반, 중반, 후반"), + fieldWithPath("graduateDate").type(JsonFieldType.NUMBER) + .description("졸업년도. YYYY"), + fieldWithPath("graduateMonth").type(JsonFieldType.NUMBER) + .description("졸업월. MM"), + fieldWithPath("major").type(JsonFieldType.STRING) + .description("본전공"), + fieldWithPath("subMajor").type(JsonFieldType.STRING) + .description("복수전공. null 값을 허용합니다"), + fieldWithPath("minor").type(JsonFieldType.STRING) + .description("부전공. null 값을 허용합니다"), + fieldWithPath("company").type(JsonFieldType.STRING) + .description("재직 중인 회사. 회사 정보가 없는 경우 null이 될 수 있습니다."), + fieldWithPath("companyYear").type(JsonFieldType.NUMBER) + .description("첫 입사 년도. 정보가 없는 경우 0값을 보내주세요"), + fieldWithPath("field").type(JsonFieldType.STRING) + .description("관심 분아. 여러 개면 ','로 구분] 프론트엔드, 백엔드, ..."), + fieldWithPath("techStack").type(JsonFieldType.STRING) + .description("기술 스택. 여러 개면 ','로 구분"), + fieldWithPath("career").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("certificate").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("awards").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("activity").type(JsonFieldType.STRING) + .description("null 값을 허용합니다") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("회원가입 요청 시 @Max 검증: 학점이 4 이상인 경우") + void signUp_if_score_is_more_than_4() throws Exception { + SignUpRequest signUpReqDto = createSignUpReqDto("google_213", "tes@google.com", "google", + "hi", 1999, 3, "서울시립대", 6); + + String content = gson.toJson(signUpReqDto); + + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/auth/signup") + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code", is(400))) + .andDo(print()) + .andDo(document("api/auth/signup/false-score-max", + requestFields( + fieldWithPath("userId").type(JsonFieldType.STRING) + .description("provider + '_' + random int(10자리). 무작위로 생성. e.g) google_1122334455"), + fieldWithPath("email").type(JsonFieldType.STRING) + .description("이메일 주소"), + fieldWithPath("provider").type(JsonFieldType.STRING) + .description("이메일 플랫폼"), + fieldWithPath("nickname").type(JsonFieldType.STRING) + .description("닉네임"), + fieldWithPath("birthYear").type(JsonFieldType.NUMBER) + .description("생년. YYYY"), + fieldWithPath("birthMonth").type(JsonFieldType.NUMBER) + .description("생월. MM"), + fieldWithPath("school").type(JsonFieldType.STRING) + .description("대학교"), + fieldWithPath("score").type(JsonFieldType.NUMBER) + .description("학점: 0, 1, 2, 3, 4"), + fieldWithPath("scoreRange").type(JsonFieldType.STRING) + .description("초반, 중반, 후반"), + fieldWithPath("graduateDate").type(JsonFieldType.NUMBER) + .description("졸업년도. YYYY"), + fieldWithPath("graduateMonth").type(JsonFieldType.NUMBER) + .description("졸업월. MM"), + fieldWithPath("major").type(JsonFieldType.STRING) + .description("본전공"), + fieldWithPath("subMajor").type(JsonFieldType.STRING) + .description("복수전공. null 값을 허용합니다"), + fieldWithPath("minor").type(JsonFieldType.STRING) + .description("부전공. null 값을 허용합니다"), + fieldWithPath("company").type(JsonFieldType.STRING) + .description("재직 중인 회사. 회사 정보가 없는 경우 null이 될 수 있습니다."), + fieldWithPath("companyYear").type(JsonFieldType.NUMBER) + .description("첫 입사 년도. 정보가 없는 경우 0값을 보내주세요"), + fieldWithPath("field").type(JsonFieldType.STRING) + .description("관심 분아. 여러 개면 ','로 구분] 프론트엔드, 백엔드, ..."), + fieldWithPath("techStack").type(JsonFieldType.STRING) + .description("기술 스택. 여러 개면 ','로 구분"), + fieldWithPath("career").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("certificate").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("awards").type(JsonFieldType.STRING) + .description("null 값을 허용합니다"), + fieldWithPath("activity").type(JsonFieldType.STRING) + .description("null 값을 허용합니다") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("로그인 시 google, kakao 외에 다른 플랫폼이 온 경우 ErrorCode.PROVIDER_NOT_ALLOWED 리턴") + void signIn_provider_type_mismatch() throws Exception { + // given + String providerMisMatch = "naver"; + SignInRequest request = new SignInRequest("k337kk@kakao.com", providerMisMatch); + String content = gson.toJson(request); + + // when + Mockito.when(authService.signIn(Mockito.any(SignInServiceRequest.class))) + .thenThrow(new CustomException(ErrorCode.PROVIDER_NOT_ALLOWED)); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/auth/signin") + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code", + is(ErrorCode.PROVIDER_NOT_ALLOWED.getHttpStatus().value()))) + .andDo(print()) + .andDo(document("api/auth/signin/fail-provider", + requestFields( + fieldWithPath("email").type(JsonFieldType.STRING) + .description("이메일 주소"), + fieldWithPath("provider").type(JsonFieldType.STRING) + .description("이메일 플랫폼") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + /** + * 로그인 성공 로직. 테스트 코드와는 달리, kakao 경우 1개만 작성 + */ + @Test + @DisplayName("kakao 로그인이 잘 된경우, SuccessCode.TOKEN_CREATED 리턴") + void signIn_kakao() throws Exception { + // given + SignInRequest request = new SignInRequest("k337kk@kakao.com", "kakao"); + String content = gson.toJson(request); + + SignInResponse signInResponse = SignInResponse.of("access_token", "refresh_token", + "사용자1", "서울과학기술대학교", "컴퓨터공학과", "[img url]"); + + // when + Mockito.when(authService.signIn(Mockito.any(SignInServiceRequest.class))).thenReturn(signInResponse); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/auth/signin") + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.code", + is(SuccessCode.TOKEN_CREATED.getCode()))) + .andDo(print()) + .andDo(document("api/auth/signin/success", + requestFields( + fieldWithPath("email").type(JsonFieldType.STRING) + .description("이메일 주소"), + fieldWithPath("provider").type(JsonFieldType.STRING) + .description("이메일 플랫폼") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data.accessToken").description("Access token"), + fieldWithPath("data.refreshToken").description("Refresh token"), + fieldWithPath("data.nickname").description("사용자의 닉네임"), + fieldWithPath("data.school").description("사용자의 학교"), + fieldWithPath("data.major").description("사용자의 전공"), + fieldWithPath("data.imgUrl").description("사용자의 프로필 이미지 url") + ) + )); + + } + + + private SignUpRequest createSignUpReqDto(String id, String email, String provider, String nickname, + Integer birthYear, Integer birthMonth, + String school, Integer score) { + String major = "컴퓨터공학과"; + String subMajor = "경영학과"; + String minor = "인공지능응용학과"; + String company = "NAVER"; + Integer companyYear = 0; + String field = "백엔드"; + String techStack = "AWS"; + return new SignUpRequest(id, email, provider, nickname, + birthYear, birthMonth, school, + score, "중반", 2021, 3, major, subMajor, minor, company, companyYear, + field, techStack,"NAVER 2023.02~", "정보처리기사", "공모전 입상", "SOPT"); + } +} diff --git a/src/test/java/seoultech/capstone/menjil/docs/follow/FollowControllerDocsTest.java b/src/test/java/seoultech/capstone/menjil/docs/follow/FollowControllerDocsTest.java new file mode 100644 index 0000000..8c459dd --- /dev/null +++ b/src/test/java/seoultech/capstone/menjil/docs/follow/FollowControllerDocsTest.java @@ -0,0 +1,205 @@ +package seoultech.capstone.menjil.docs.follow; + +import com.google.gson.Gson; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.payload.JsonFieldType; +import seoultech.capstone.menjil.docs.RestDocsSupport; +import seoultech.capstone.menjil.domain.follow.api.FollowController; +import seoultech.capstone.menjil.domain.follow.api.dto.request.FollowCreateRequest; +import seoultech.capstone.menjil.domain.follow.application.FollowService; +import seoultech.capstone.menjil.global.config.WebConfig; +import seoultech.capstone.menjil.global.exception.CustomException; +import seoultech.capstone.menjil.global.exception.ErrorCode; +import seoultech.capstone.menjil.global.exception.SuccessCode; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.payload.JsonFieldType.STRING; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static seoultech.capstone.menjil.global.exception.SuccessIntValue.FOLLOW_CREATED; +import static seoultech.capstone.menjil.global.exception.SuccessIntValue.FOLLOW_DELETED; + +@WebMvcTest(controllers = FollowController.class, + excludeFilters = { + @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = WebConfig.class) + }) +public class FollowControllerDocsTest extends RestDocsSupport { + + @MockBean + private FollowService followService; + + @Autowired + private Gson gson; + + private final String TEST_USER_NICKNAME = "user_nickname33"; + private final String TEST_FOLLOW_NICKNAME = "follow_nickname33"; + + @Test + @DisplayName("case 1: 팔로우가 생성된 경우") + void followRequest_follow_created() throws Exception { + // given + FollowCreateRequest followCreateRequest = FollowCreateRequest.of(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME); + String content = gson.toJson(followCreateRequest); + + // when + Mockito.when(followService.createFollow(followCreateRequest.toServiceRequest())).thenReturn(FOLLOW_CREATED.getValue()); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/follow/create") + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.code", is(SuccessCode.FOLLOW_CREATED.getCode()))) + .andDo(print()) + .andDo(document("api/follow/create/201/true", + requestFields( + fieldWithPath("userNickname").type(JsonFieldType.STRING) + .description("사용자 닉네임"), + fieldWithPath("followNickname").type(JsonFieldType.STRING) + .description("사용자가 팔로우 요청을 보내는 닉네임") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("case 2: 팔로우가 제거된 경우") + void followRequest_follow_deleted() throws Exception { + // given + FollowCreateRequest followCreateRequest = FollowCreateRequest.of(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME); + String content = gson.toJson(followCreateRequest); + + // when + Mockito.when(followService.createFollow(followCreateRequest.toServiceRequest())).thenReturn(FOLLOW_DELETED.getValue()); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/follow/create") + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.code", is(SuccessCode.FOLLOW_DELETED.getCode()))) + .andDo(print()) + .andDo(document("api/follow/create/201/false", + requestFields( + fieldWithPath("userNickname").type(JsonFieldType.STRING) + .description("사용자 닉네임"), + fieldWithPath("followNickname").type(JsonFieldType.STRING) + .description("사용자가 팔로우 요청을 보내는 닉네임") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("case 3: 서버 오류") + void followRequest_INTERNAL_SERVER_ERROR() throws Exception { + // given + FollowCreateRequest followCreateRequest = FollowCreateRequest.of(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME); + String content = gson.toJson(followCreateRequest); + + // when + Mockito.when(followService.createFollow(followCreateRequest.toServiceRequest())) + .thenThrow(new CustomException(ErrorCode.INTERNAL_SERVER_ERROR)); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/follow/create") + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().is5xxServerError()) + .andExpect(jsonPath("$.code", is(ErrorCode.INTERNAL_SERVER_ERROR.getHttpStatus().value()))) + .andDo(print()) + .andDo(document("api/follow/create/500", + requestFields( + fieldWithPath("userNickname").type(JsonFieldType.STRING) + .description("사용자 닉네임"), + fieldWithPath("followNickname").type(JsonFieldType.STRING) + .description("사용자가 팔로우 요청을 보내는 닉네임") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("case 1: follow가 이미 존재하는 경우") + void checkFollowStatus_return_true() throws Exception { + // given + boolean FOLLOW_EXISTS = true; + + // when + Mockito.when(followService.checkFollowStatus(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME)).thenReturn(FOLLOW_EXISTS); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/follow/check-status/") + .queryParam("userNickname", TEST_USER_NICKNAME) + .queryParam("followNickname", TEST_FOLLOW_NICKNAME)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code", is(SuccessCode.FOLLOW_CHECK_SUCCESS.getCode()))) + .andDo(print()) + .andDo(document("api/follow/check-status/true", + requestParameters( + parameterWithName("userNickname").description("사용자 닉네임"), + parameterWithName("followNickname").description("사용자가 팔로우 요청을 보내는 닉네임") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.BOOLEAN).description("응답 데이터") + ) + )); + } + + @Test + @DisplayName("case 2: follow가 존재하지 않는 경우") + void checkFollowStatus_return_false() throws Exception { + // given + boolean FOLLOW_NOT_EXISTS = false; + + // when + Mockito.when(followService.checkFollowStatus(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME)).thenReturn(FOLLOW_NOT_EXISTS); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/follow/check-status/") + .queryParam("userNickname", TEST_USER_NICKNAME) + .queryParam("followNickname", TEST_FOLLOW_NICKNAME)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code", is(SuccessCode.FOLLOW_CHECK_SUCCESS.getCode()))) + .andDo(print()) + .andDo(document("api/follow/check-status/false", + requestParameters( + parameterWithName("userNickname").description("사용자 닉네임"), + parameterWithName("followNickname").description("사용자가 팔로우 요청을 보내는 닉네임") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.BOOLEAN).description("응답 데이터") + ) + )); + } +} diff --git a/src/test/java/seoultech/capstone/menjil/docs/following/FollowingControllerDocsTest.java b/src/test/java/seoultech/capstone/menjil/docs/following/FollowingControllerDocsTest.java new file mode 100644 index 0000000..156b80a --- /dev/null +++ b/src/test/java/seoultech/capstone/menjil/docs/following/FollowingControllerDocsTest.java @@ -0,0 +1,208 @@ +package seoultech.capstone.menjil.docs.following; + +import com.google.gson.Gson; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.payload.JsonFieldType; +import seoultech.capstone.menjil.docs.RestDocsSupport; +import seoultech.capstone.menjil.domain.following.api.FollowingController; +import seoultech.capstone.menjil.domain.following.application.FollowingService; +import seoultech.capstone.menjil.domain.following.application.dto.FollowingQaDto; +import seoultech.capstone.menjil.domain.following.application.dto.FollowingUserInfoDto; +import seoultech.capstone.menjil.domain.following.application.dto.response.FollowingUserInfoResponse; +import seoultech.capstone.menjil.global.config.WebConfig; +import seoultech.capstone.menjil.global.exception.CustomException; +import seoultech.capstone.menjil.global.exception.ErrorCode; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.payload.JsonFieldType.STRING; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(controllers = FollowingController.class, + excludeFilters = { + @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = WebConfig.class) + }) +public class FollowingControllerDocsTest extends RestDocsSupport { + + @MockBean + private FollowingService followingService; + + @Autowired + private Gson gson; + + @Test + @DisplayName("case 1: 정상 로직] 사용자의 정보와 질문답변 내용이 모두 존재한다") + void getFollowUserInfo() throws Exception { + // given + String followNickname = "userA"; + Long answersCount = 2L; + List answers = new ArrayList<>(); + answers.add(FollowingQaDto.builder() + .questionOrigin("원본 질문1") + .questionSummary("원본 요약1") + .answer("답변1") + .answerTime(LocalDateTime.now()) + .likes(2L) + .views(11L) + .build()); + answers.add(FollowingQaDto.builder() + .questionOrigin("원본 질문2") + .questionSummary("원본 요약2") + .answer("답변2") + .answerTime(LocalDateTime.now().plusSeconds(5000)) + .likes(4L) + .views(10L) + .build()); + + FollowingUserInfoResponse response = createTestFollowingUserInfoResponse(followNickname, + answersCount, answers); + + // when + Mockito.when(followingService.getFollowUserInfo(followNickname)).thenReturn(response); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/following/info") + .queryParam("followNickname", followNickname)) + .andExpect(status().isOk()) + .andDo(document("api/following/info/200-ok", + requestParameters( + parameterWithName("followNickname").description("조회 요청을 보내는 닉네임") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data.followingUserInfoDto.nickname").description("닉네임"), + fieldWithPath("data.followingUserInfoDto.company").description("재직 중인 회사. 회사 정보가 없는 경우 null이 될 수 있습니다."), + fieldWithPath("data.followingUserInfoDto.field").description("관심 분아. 여러 개면 ','로 구분] 프론트엔드, 백엔드, ..."), + fieldWithPath("data.followingUserInfoDto.school").description("The school the user attended."), + fieldWithPath("data.followingUserInfoDto.major").description("The major of the user."), + fieldWithPath("data.followingUserInfoDto.subMajor").optional().description("복수전공. null 값을 허용합니다"), + fieldWithPath("data.followingUserInfoDto.minor").optional().description("부전공. null 값을 허용합니다"), + fieldWithPath("data.followingUserInfoDto.techStack").description("기술 스택. 여러 개면 ','로 구분"), + fieldWithPath("data.followingUserInfoDto.imgUrl").description("프로필 img url"), + fieldWithPath("data.followingUserInfoDto.career").optional().description("null 값을 허용합니다"), + fieldWithPath("data.followingUserInfoDto.certificate").optional().description("null 값을 허용합니다"), + fieldWithPath("data.followingUserInfoDto.awards").optional().description("null 값을 허용합니다"), + fieldWithPath("data.followingUserInfoDto.activity").optional().description("null 값을 허용합니다"), + fieldWithPath("data.answersCount").description("멘토의 답변 수"), + fieldWithPath("data.answers[].questionOrigin").description("원본 질문"), + fieldWithPath("data.answers[].questionSummary").description("ChatGPT로 원본 질문을 3줄 요약한 결과"), + fieldWithPath("data.answers[].answer").description("요약된 질문에 대한 답변"), + fieldWithPath("data.answers[].answerTime").optional().description("답변 생성 시간"), + fieldWithPath("data.answers[].views").description("조회수"), + fieldWithPath("data.answers[].likes").description("좋아요수") + ) + )); + } + + + @Test + @DisplayName("case 1-1: 정상 로직] 사용자의 정보만 존재하며, 질문답변 내역은 존재하지 않는다.") + void getFollowUserInfo_qaObject_is_not_existed() throws Exception { + // given + String followNickname = "userA"; + Long answersCount = 0L; + List answers = new ArrayList<>(); + + FollowingUserInfoResponse response = createTestFollowingUserInfoResponse(followNickname, + answersCount, answers); + + // when + Mockito.when(followingService.getFollowUserInfo(followNickname)).thenReturn(response); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/following/info") + .queryParam("followNickname", followNickname)) + .andExpect(status().isOk()) + .andDo(print()) + .andDo(document("api/following/info/200-only-userinfo", + requestParameters( + parameterWithName("followNickname").description("조회 요청을 보내는 닉네임") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data.followingUserInfoDto.nickname").description("닉네임"), + fieldWithPath("data.followingUserInfoDto.company").description("재직 중인 회사. 회사 정보가 없는 경우 null이 될 수 있습니다."), + fieldWithPath("data.followingUserInfoDto.field").description("관심 분아. 여러 개면 ','로 구분] 프론트엔드, 백엔드, ..."), + fieldWithPath("data.followingUserInfoDto.school").description("The school the user attended."), + fieldWithPath("data.followingUserInfoDto.major").description("The major of the user."), + fieldWithPath("data.followingUserInfoDto.subMajor").optional().description("복수전공. null 값을 허용합니다"), + fieldWithPath("data.followingUserInfoDto.minor").optional().description("부전공. null 값을 허용합니다"), + fieldWithPath("data.followingUserInfoDto.techStack").description("기술 스택. 여러 개면 ','로 구분"), + fieldWithPath("data.followingUserInfoDto.imgUrl").description("프로필 img url"), + fieldWithPath("data.followingUserInfoDto.career").optional().description("null 값을 허용합니다"), + fieldWithPath("data.followingUserInfoDto.certificate").optional().description("null 값을 허용합니다"), + fieldWithPath("data.followingUserInfoDto.awards").optional().description("null 값을 허용합니다"), + fieldWithPath("data.followingUserInfoDto.activity").optional().description("null 값을 허용합니다"), + fieldWithPath("data.answersCount").description("멘토의 답변 수"), + fieldWithPath("data.answers").description("질문 답변 객체 Array") + ) + )); + } + + @Test + @DisplayName("case 2: 요청한 사용자의 닉네임이 데이터베이스에 없는 경우") + void getFollowUserInfo_user_not_in_db() throws Exception { + // given + String followNickname = "user333_not_in_db"; + + // when + Mockito.when(followingService.getFollowUserInfo(followNickname)) + .thenThrow(new CustomException(ErrorCode.INTERNAL_SERVER_ERROR)); + + // then + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/following/info") + .queryParam("followNickname", followNickname)) + .andExpect(status().is5xxServerError()) + .andDo(print()) + .andDo(document("api/following/info/500-error", + requestParameters( + parameterWithName("followNickname").description("조회 요청을 보내는 닉네임") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"), + fieldWithPath("message").type(STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NULL).description("응답 데이터") + ) + )); + } + + private FollowingUserInfoResponse createTestFollowingUserInfoResponse(String followNickname, + Long answersCount, + List answers) { + FollowingUserInfoDto followingUserInfoDto = FollowingUserInfoDto.builder() + .nickname(followNickname) + .company("Google") + .field("백엔드") + .school("서울과학기술대학교") + .major("컴퓨터공학과") + .subMajor(null) + .minor(null) + .techStack("Spring Boot, AWS") + .imgUrl("https://...") + .career(null) + .certificate(null) + .awards(null) + .activity(null) + .build(); + + return FollowingUserInfoResponse.of(followingUserInfoDto, answersCount, answers); + } +} diff --git a/src/test/java/seoultech/capstone/menjil/domain/auth/api/AuthControllerTest.java b/src/test/java/seoultech/capstone/menjil/domain/auth/api/AuthControllerTest.java index c464da3..040f690 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/auth/api/AuthControllerTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/auth/api/AuthControllerTest.java @@ -3,27 +3,23 @@ import com.google.gson.Gson; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.mockito.BDDMockito; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; -import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import seoultech.capstone.menjil.domain.auth.api.dto.request.SignInRequest; +import seoultech.capstone.menjil.domain.auth.api.dto.request.SignUpRequest; import seoultech.capstone.menjil.domain.auth.application.AuthService; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; -import seoultech.capstone.menjil.domain.auth.dto.request.SignInRequest; -import seoultech.capstone.menjil.domain.auth.dto.request.SignUpRequest; -import seoultech.capstone.menjil.domain.auth.dto.response.SignInResponse; +import seoultech.capstone.menjil.domain.auth.application.dto.request.SignInServiceRequest; +import seoultech.capstone.menjil.domain.auth.application.dto.response.SignInResponse; import seoultech.capstone.menjil.global.config.WebConfig; -import seoultech.capstone.menjil.global.exception.ErrorCode; -import seoultech.capstone.menjil.global.exception.ErrorIntValue; -import seoultech.capstone.menjil.global.exception.SuccessCode; -import seoultech.capstone.menjil.global.exception.SuccessIntValue; +import seoultech.capstone.menjil.global.exception.*; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.times; @@ -33,16 +29,14 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@MockBean(JpaMetamodelMappingContext.class) @WebMvcTest(controllers = AuthController.class, - excludeAutoConfiguration = {SecurityAutoConfiguration.class}, excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = WebConfig.class) }) class AuthControllerTest { @Autowired - private MockMvc mvc; + private MockMvc mockMvc; @Autowired private Gson gson; @@ -50,28 +44,22 @@ class AuthControllerTest { @MockBean private AuthService authService; - /* - * @WebMvcTest 에서는, db 조회는 힘들 것으로 생각하였으나, - * Mockito.when().thenReturn() 을 통해 예상되는 동작을 미리 지정해준 뒤, - * 그에 해당하는 응답값이 반환되는지 검증할 수 있다. - */ - /** * checkSignupIsAvailable * 회원가입 하기 전에, 먼저 기존에 가입된 사용자인지 확인한다 */ @Test - @DisplayName("case 1: 사용자가 존재하지 않는 경우") + @DisplayName("case 1: 사용자가 존재하지 않는 경우, 회원가입이 가능하다") void checkSignupIsAvailable() throws Exception { // given - String email = "Junit-test@gmail.com"; + String email = "testUser1@gmail.com"; String provider = "google"; // when Mockito.when(authService.findUserInDb(email, provider)).thenReturn(SuccessIntValue.SUCCESS.getValue()); // then - mvc.perform(get("/api/auth/signup") + mockMvc.perform(MockMvcRequestBuilders.get("/api/auth/signup") .queryParam("email", email) .queryParam("provider", provider)) .andExpect(status().isOk()) @@ -83,37 +71,43 @@ void checkSignupIsAvailable() throws Exception { } @Test - @DisplayName("case 2: 사용자가 이미 존재하는 경우 경우") + @DisplayName("case 2: 사용자가 이미 존재하는 경우, 회원가입이 불가능하다") void checkSignupIsAvailable_user_already_existed() throws Exception { - String email = "Junit-test@gmail.com"; + // given + String email = "testUser1@gmail.com"; String provider = "google"; - Mockito.when(authService.findUserInDb(email, provider)).thenReturn(ErrorIntValue.USER_ALREADY_IN_DB.getValue()); + BDDMockito.given(authService.findUserInDb(email, provider)) + .willReturn(ErrorIntValue.USER_ALREADY_EXISTED.getValue()); - mvc.perform(get("/api/auth/signup") + // when // then + mockMvc.perform(get("/api/auth/signup") .queryParam("email", email) .queryParam("provider", provider)) .andExpect(status().isConflict()) .andExpect(jsonPath("$.code", - is(ErrorCode.USER_DUPLICATED.getHttpStatus().value()))) + is(ErrorCode.USER_ALREADY_EXISTED.getHttpStatus().value()))) .andExpect(jsonPath("$.message", - is(ErrorCode.USER_DUPLICATED.getMessage()))) + is(ErrorCode.USER_ALREADY_EXISTED.getMessage()))) .andDo(print()); verify(authService).findUserInDb(email, provider); } /** - * checkNicknameDuplicate + * isNicknameAvailable */ @Test - @DisplayName("닉네임 검증; 공백과 특수문자가 없는경우 SuccessCode.NICKNAME_AVAILABLE 리턴") - void checkNicknameDuplicate() throws Exception { - String nickname = "test33AA가나마"; + @DisplayName("닉네임 검증; 공백과 특수문자가 없는 경우 정상 로직 수행") + void isNicknameAvailable() throws Exception { + // given + String nickname = "test33AA"; + // when Mockito.when(authService.findNicknameInDb(nickname)).thenReturn(SuccessIntValue.SUCCESS.getValue()); - mvc.perform(get("/api/auth/check-nickname") + // then + mockMvc.perform(get("/api/auth/check-nickname") .queryParam("nickname", nickname)) .andExpect(status().isOk()) .andExpect(jsonPath("$.code", is(SuccessCode.NICKNAME_AVAILABLE.getCode()))) @@ -125,42 +119,46 @@ void checkNicknameDuplicate() throws Exception { @Test @DisplayName("닉네임 검증; 공백이 들어오면 CustomException 을 발생시킨다") - void checkNicknameDuplicate_nickname_is_blank() throws Exception { - mvc.perform(get("/api/auth/check-nickname") - .queryParam("nickname", " ")) + void isNicknameAvailable_nickname_is_blank() throws Exception { + String nickname = " "; + + mockMvc.perform(get("/api/auth/check-nickname") + .queryParam("nickname", nickname)) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.code", - is(ErrorCode.NICKNAME_CONTAINS_BLANK.getHttpStatus().value()))) + is(ErrorCode.NICKNAME_FORMAT_IS_WRONG.getHttpStatus().value()))) .andExpect(jsonPath("$.message", - is(ErrorCode.NICKNAME_CONTAINS_BLANK.getMessage()))) + is(ErrorCode.NICKNAME_FORMAT_IS_WRONG.getMessage()))) .andDo(print()); } @Test @DisplayName("닉네임 검증; 특수문자가 들어오면 CustomException 을 발생시킨다.") - void checkNicknameDuplicate_nickname_has_special_char() throws Exception { - mvc.perform(get("/api/auth/check-nickname") - .queryParam("nickname", "*ea3sf")) + void isNicknameAvailable_nickname_has_special_char() throws Exception { + String nickname = "easd##!@@@@asdf"; + + mockMvc.perform(get("/api/auth/check-nickname") + .queryParam("nickname", nickname)) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.code", - is(ErrorCode.NICKNAME_CONTAINS_SPECIAL_CHARACTER.getHttpStatus().value()))) + is(ErrorCode.NICKNAME_FORMAT_IS_WRONG.getHttpStatus().value()))) .andExpect(jsonPath("$.message", - is(ErrorCode.NICKNAME_CONTAINS_SPECIAL_CHARACTER.getMessage()))) + is(ErrorCode.NICKNAME_FORMAT_IS_WRONG.getMessage()))) .andDo(print()); } @Test @DisplayName("닉네임 검증; 닉네임이 db에 이미 존재하는 경우 ErrorCode.NICKNAME_DUPLICATED 리턴") - void checkNicknameDuplicate_nickname_already_existed() throws Exception { + void isNicknameAvailable_nickname_already_existed() throws Exception { String nickname = "NicknameExistsInDB"; - Mockito.when(authService.findNicknameInDb(nickname)).thenReturn(ErrorIntValue.USER_ALREADY_IN_DB.getValue()); + Mockito.when(authService.findNicknameInDb(nickname)).thenReturn(ErrorIntValue.USER_ALREADY_EXISTED.getValue()); - mvc.perform(get("/api/auth/check-nickname") + mockMvc.perform(get("/api/auth/check-nickname") .queryParam("nickname", nickname)) .andExpect(status().isConflict()) - .andExpect(jsonPath("$.code", is(ErrorCode.NICKNAME_DUPLICATED.getHttpStatus().value()))) - .andExpect(jsonPath("$.message", is(ErrorCode.NICKNAME_DUPLICATED.getMessage()))) + .andExpect(jsonPath("$.code", is(ErrorCode.NICKNAME_ALREADY_EXISTED.getHttpStatus().value()))) + .andExpect(jsonPath("$.message", is(ErrorCode.NICKNAME_ALREADY_EXISTED.getMessage()))) .andDo(print()); verify(authService).findNicknameInDb(nickname); @@ -172,14 +170,14 @@ void checkNicknameDuplicate_nickname_already_existed() throws Exception { @Test @DisplayName("회원가입 요청이 정상적으로 된 경우 SuccessCode.SIGNUP_SUCCESS 리턴") void signUp() throws Exception { - SignUpRequest signUpReqDto = createSignUpReqDto("google_213", "tes@google.com", "google", - "hi", 1999, 3, "서울시립대", 3); + SignUpRequest signUpReqDto = createSignUpReqDto("google_1122334455", "test@google.com", "google", + "user11", 1999, 3, "서울과학기술대학교", 3); String content = gson.toJson(signUpReqDto); // Return type of authService.signUp method is void - Mockito.doNothing().when(authService).signUp(signUpReqDto); + Mockito.doNothing().when(authService).signUp(signUpReqDto.toServiceRequest()); - mvc.perform(MockMvcRequestBuilders.post("/api/auth/signup") + mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().isCreated()) @@ -187,18 +185,17 @@ void signUp() throws Exception { .andExpect(jsonPath("$.message", is(SuccessCode.SIGNUP_SUCCESS.getMessage()))) .andDo(print()); - verify(authService).signUp(signUpReqDto); + verify(authService).signUp(signUpReqDto.toServiceRequest()); } @Test @DisplayName("회원가입 요청 시 닉네임 검증: 닉네임에 공백이 포함된 경우 CustomException 발생") void signUp_nickname_contains_blank() throws Exception { - SignUpRequest signUpReqDto = createSignUpReqDto("google_2134", "test@kakao.com", "kakao", - "가나 다라마", 1999, 3, "서울과기대", 3); - + SignUpRequest signUpReqDto = createSignUpReqDto("google_1122334455", "test@google.com", "google", + "user One", 1999, 3, "서울과학기술대학교", 3); String content = gson.toJson(signUpReqDto); - mvc.perform(MockMvcRequestBuilders.post("/api/auth/signup") + mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().isBadRequest()) @@ -210,12 +207,11 @@ void signUp_nickname_contains_blank() throws Exception { @Test @DisplayName("회원가입 요청 시 닉네임 검증: 닉네임에 특수문자가 포함된 경우 CustomException 발생") void signUp_nickname_contains_character() throws Exception { - SignUpRequest signUpReqDto = createSignUpReqDto("google_213", "tes@google.com", "google", - "가나@다라마", 1999, 3, "서울과기대", 3); - + SignUpRequest signUpReqDto = createSignUpReqDto("google_1122334455", "test@google.com", "google", + "user@@!!!One", 1999, 3, "서울과학기술대학교", 3); String content = gson.toJson(signUpReqDto); - mvc.perform(MockMvcRequestBuilders.post("/api/auth/signup") + mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().isBadRequest()) @@ -227,17 +223,15 @@ void signUp_nickname_contains_character() throws Exception { @Test @DisplayName("회원가입 요청 시 닉네임 검증: 값이 null 인 경우 @NotBlank") void signUp_nickname_is_Null() throws Exception { - SignUpRequest signUpReqDto = createSignUpReqDto("google_213", "tes@google.com", "google", - null, 1999, 3, "서울과기대", 3); - + SignUpRequest signUpReqDto = createSignUpReqDto("google_1122334455", "test@google.com", "google", + null, 1999, 3, "서울과학기술대학교", 3); String content = gson.toJson(signUpReqDto); - mvc.perform(MockMvcRequestBuilders.post("/api/auth/signup") + mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.code", is(400))) - .andExpect(jsonPath("$.code", is(400))) .andExpect(jsonPath("$.message", is("1. must not be blank "))) .andDo(print()); } @@ -250,7 +244,7 @@ void signUp_Null_is_more_than_two() throws Exception { String content = gson.toJson(signUpReqDto); - mvc.perform(MockMvcRequestBuilders.post("/api/auth/signup") + mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().isBadRequest()) @@ -268,7 +262,7 @@ void signUp_if_score_is_more_than_4() throws Exception { String content = gson.toJson(signUpReqDto); - mvc.perform(MockMvcRequestBuilders.post("/api/auth/signup") + mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().isBadRequest()) @@ -277,7 +271,6 @@ void signUp_if_score_is_more_than_4() throws Exception { .andDo(print()); } - /** * 로그인(signIn) 검증 */ @@ -285,11 +278,16 @@ void signUp_if_score_is_more_than_4() throws Exception { @DisplayName("로그인 시 google, kakao 외에 다른 플랫폼이 온 경우 ErrorCode.PROVIDER_NOT_ALLOWED 리턴") void signIn_provider_type_mismatch() throws Exception { // given - String typeMismatch = "naver"; - SignInRequest requestDto = new SignInRequest("k337kk@kakao.com", typeMismatch); - String content = gson.toJson(requestDto); + String providerMisMatch = "naver"; + SignInRequest request = new SignInRequest("k337kk@kakao.com", providerMisMatch); + String content = gson.toJson(request); - mvc.perform(MockMvcRequestBuilders.post("/api/auth/signin") + // when + Mockito.when(authService.signIn(Mockito.any(SignInServiceRequest.class))) + .thenThrow(new CustomException(ErrorCode.PROVIDER_NOT_ALLOWED)); + + // then + mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/signin") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().isBadRequest()) @@ -298,21 +296,25 @@ void signIn_provider_type_mismatch() throws Exception { .andExpect(jsonPath("$.message", is(ErrorCode.PROVIDER_NOT_ALLOWED.getMessage()))) .andDo(print()); + + verify(authService, times(1)).signIn(Mockito.any(SignInServiceRequest.class)); } @Test @DisplayName("kakao 로그인이 잘 된경우, SuccessCode.TOKEN_CREATED 리턴") void signIn_kakao() throws Exception { // given - SignInRequest requestDto = new SignInRequest("k337kk@kakao.com", "kakao"); - String content = gson.toJson(requestDto); + SignInRequest request = new SignInRequest("k337kk@kakao.com", "kakao"); + String content = gson.toJson(request); - SignInResponse signInResponse = SignInResponse.of("test_access_token", "test_refresh_token", - "test", "test", "test", "test"); + SignInResponse signInResponse = SignInResponse.of("access_token", "refresh_token", + "사용자1", "서울과학기술대학교", "컴퓨터공학과", "[img url]"); - Mockito.when(authService.signIn(requestDto.getEmail(), requestDto.getProvider())).thenReturn(signInResponse); + // when + Mockito.when(authService.signIn(Mockito.any(SignInServiceRequest.class))).thenReturn(signInResponse); - mvc.perform(MockMvcRequestBuilders.post("/api/auth/signin") + // then + mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/signin") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().isCreated()) @@ -326,22 +328,22 @@ void signIn_kakao() throws Exception { is(signInResponse.getRefreshToken()))) .andDo(print()); - verify(authService, times(1)).signIn(requestDto.getEmail(), requestDto.getProvider()); + verify(authService, times(1)).signIn(Mockito.any(SignInServiceRequest.class)); } @Test @DisplayName("google 로그인이 잘 된경우, SuccessCode.TOKEN_CREATED 리턴") void signIn_google() throws Exception { // given - SignInRequest requestDto = new SignInRequest("testUser@google.com", "google"); - String content = gson.toJson(requestDto); + SignInRequest request = new SignInRequest("testUser@google.com", "google"); + String content = gson.toJson(request); - SignInResponse signInResponse = SignInResponse.of("test_access_token", "test_refresh_token", - "test", "test", "test", "test"); + SignInResponse signInResponse = SignInResponse.of("access_token", "refresh_token", + "사용자1", "서울과학기술대학교", "컴퓨터공학과", "[img url]"); - Mockito.when(authService.signIn(requestDto.getEmail(), requestDto.getProvider())).thenReturn(signInResponse); + Mockito.when(authService.signIn(Mockito.any(SignInServiceRequest.class))).thenReturn(signInResponse); - mvc.perform(MockMvcRequestBuilders.post("/api/auth/signin") + mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/signin") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().isCreated()) @@ -355,16 +357,23 @@ void signIn_google() throws Exception { is(signInResponse.getRefreshToken()))) .andDo(print()); - verify(authService, times(1)).signIn(requestDto.getEmail(), requestDto.getProvider()); + verify(authService, times(1)).signIn(Mockito.any(SignInServiceRequest.class)); } private SignUpRequest createSignUpReqDto(String id, String email, String provider, String nickname, Integer birthYear, Integer birthMonth, String school, Integer score) { + String major = "컴퓨터공학과"; + String subMajor = null; + String minor = null; + String company = null; + Integer companyYear = 3; + String field = "백엔드"; + String techStack = "AWS"; return new SignUpRequest(id, email, provider, nickname, - UserRole.MENTEE, birthYear, birthMonth, school, - score, "중반", 2021, 3, "경제학과", null, null, null, - "Devops", "AWS", null, null, null, null); + birthYear, birthMonth, school, + score, "중반", 2021, 3, major, subMajor, minor, company, companyYear, + field, techStack,null, null, null, null); } } \ No newline at end of file diff --git a/src/test/java/seoultech/capstone/menjil/domain/auth/application/AuthServiceExceptionTest.java b/src/test/java/seoultech/capstone/menjil/domain/auth/application/AuthServiceExceptionTest.java deleted file mode 100644 index b5ebf5e..0000000 --- a/src/test/java/seoultech/capstone/menjil/domain/auth/application/AuthServiceExceptionTest.java +++ /dev/null @@ -1,97 +0,0 @@ -package seoultech.capstone.menjil.domain.auth.application; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.dao.DataIntegrityViolationException; -import seoultech.capstone.menjil.domain.auth.dao.TokenRepository; -import seoultech.capstone.menjil.domain.auth.dao.UserRepository; -import seoultech.capstone.menjil.domain.auth.domain.RefreshToken; -import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; -import seoultech.capstone.menjil.domain.auth.dto.request.SignUpRequest; -import seoultech.capstone.menjil.domain.auth.jwt.JwtTokenProvider; -import seoultech.capstone.menjil.global.exception.CustomException; - -import java.time.LocalDateTime; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -public class AuthServiceExceptionTest { - - @InjectMocks - private AuthService authService; - - @Mock - private UserRepository userRepository; - @Mock - private TokenRepository tokenRepository; - @Mock - private JwtTokenProvider jwtTokenProvider; - - @Test - void signUp_Should_Throw_CustomException_WhenSaveFails() { - // Arrange - SignUpRequest SignUpRequestA = createSignUpReqDto("google_123", "tes33t@kakao.com", - "kakao", "userA"); - - // DataIntegrityViolationException - doThrow(DataIntegrityViolationException.class).when(userRepository).save(any(User.class)); - - // Act and Assert - assertThrows(CustomException.class, () -> authService.signUp(SignUpRequestA)); - } - - @Test - void signIn_Should_Throw_CustomException_When_Token_SaveFails() { - // Arrange - String testUserId = "google_1234123455"; - String testEmail = "testAA@google.com"; - String testProvider = "google"; - - User mockUser = createUser(testUserId, testEmail, testProvider, "testUserAA"); - String mockAccessToken = "mockAccessToken"; - String mockRefreshToken = "mockRefreshToken"; - - when(userRepository.findUserByEmailAndProvider(testEmail, testProvider)) - .thenReturn(Optional.ofNullable(mockUser)); - when(jwtTokenProvider.generateAccessToken(anyString(), any(LocalDateTime.class))).thenReturn(mockAccessToken); - when(jwtTokenProvider.generateRefreshToken(anyString(), any(LocalDateTime.class))).thenReturn(mockRefreshToken); - when(tokenRepository.findRefreshTokenByUserId(mockUser)).thenReturn(Optional.empty()); - - // DataIntegrityViolationException - doThrow(DataIntegrityViolationException.class).when(tokenRepository).save(any(RefreshToken.class)); - - // Act and Assert - assertThrows(CustomException.class, () -> authService.signIn(testEmail, testProvider)); - } - - - private SignUpRequest createSignUpReqDto(String id, String email, String provider, String nickname) { - return new SignUpRequest(id, email, provider, nickname, - UserRole.MENTEE, 2000, 3, "고려대학교", - 3, "중반", 2021, 3, "경제학과", null, null, null, - "Devops", "AWS", null, null, null, null); - } - - private User createUser(String id, String email, String provider, String nickname) { - return User.builder() - .id(id).email(email).provider(provider).nickname(nickname) - .role(UserRole.MENTEE).birthYear(2000).birthMonth(3) - .school("고려대학교").score(3).scoreRange("중반") - .graduateDate(2021).graduateMonth(3) - .major("경제학과").subMajor(null) - .minor(null).field("백엔드").techStack("AWS") - .optionInfo(null) - .build(); - } - -} diff --git a/src/test/java/seoultech/capstone/menjil/domain/auth/application/AuthServiceTest.java b/src/test/java/seoultech/capstone/menjil/domain/auth/application/AuthServiceTest.java index d4e2cb9..3d1a7c2 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/auth/application/AuthServiceTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/auth/application/AuthServiceTest.java @@ -7,20 +7,20 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.transaction.annotation.Transactional; +import seoultech.capstone.menjil.domain.auth.application.dto.request.SignInServiceRequest; +import seoultech.capstone.menjil.domain.auth.application.dto.request.SignUpServiceRequest; import seoultech.capstone.menjil.domain.auth.dao.TokenRepository; import seoultech.capstone.menjil.domain.auth.dao.UserRepository; import seoultech.capstone.menjil.domain.auth.domain.RefreshToken; import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; -import seoultech.capstone.menjil.domain.auth.dto.request.SignUpRequest; -import seoultech.capstone.menjil.domain.auth.dto.response.SignInResponse; +import seoultech.capstone.menjil.domain.auth.application.dto.response.SignInResponse; import seoultech.capstone.menjil.global.exception.CustomException; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; -import static seoultech.capstone.menjil.global.exception.ErrorIntValue.USER_ALREADY_IN_DB; +import static seoultech.capstone.menjil.global.exception.ErrorIntValue.USER_ALREADY_EXISTED; import static seoultech.capstone.menjil.global.exception.SuccessIntValue.SUCCESS; @SpringBootTest @@ -53,7 +53,7 @@ void setUp() { void checkUserExistsInDb_user_already_exists_in_db() { // email, provider 가 같은 유저가 db에 이미 존재하는 경우 int result = authService.findUserInDb(TEST_USER_EMAIL, TEST_USER_PROVIDER); - assertThat(result).isEqualTo(USER_ALREADY_IN_DB.getValue()); + assertThat(result).isEqualTo(USER_ALREADY_EXISTED.getValue()); // email, provider 가 같은 유저가 db에 존재하지 않는 경우 int result2 = authService.findUserInDb("userA@gmail.com", "kakao"); @@ -68,7 +68,7 @@ void checkUserExistsInDb_user_already_exists_in_db() { void findNicknameInDb() { // 닉네임 중복 int result = authService.findNicknameInDb(TEST_USER_NICKNAME); - assertThat(result).isEqualTo(USER_ALREADY_IN_DB.getValue()); + assertThat(result).isEqualTo(USER_ALREADY_EXISTED.getValue()); // 닉네임 중복 X int result2 = authService.findNicknameInDb("testB"); @@ -81,13 +81,13 @@ void findNicknameInDb() { @Test @DisplayName("정상적으로 회원가입이 동작하는지 확인: img_url 세팅 및 데이터 저장") void signUp() { - SignUpRequest SignUpRequestA = createSignUpReqDto("google_123", "tes33t@kakao.com", + SignUpServiceRequest request = createSignUpServiceReqDto("google_123", "tes33t@kakao.com", "kakao", "userA"); // @BeforeEach 에서 저장된 데이터 제거 userRepository.deleteAll(); - authService.signUp(SignUpRequestA); + authService.signUp(request); List userList = userRepository.findAll(); assertThat(userList.size()).isEqualTo(1); @@ -99,10 +99,10 @@ void signUp() { @DisplayName("회원가입 시 닉네임이 db에 이미 존재하는 경우 CustomException 리턴") void signUp_Nickname_already_existed() { // given - SignUpRequest SignUpRequestA = createSignUpReqDto("google_123", "test@kakao.com", + SignUpServiceRequest request = createSignUpServiceReqDto("google_123", "test@kakao.com", "kakao", TEST_USER_NICKNAME); - assertThrows(CustomException.class, () -> authService.signUp(SignUpRequestA)); + assertThrows(CustomException.class, () -> authService.signUp(request)); } /** @@ -111,43 +111,52 @@ void signUp_Nickname_already_existed() { @Test @DisplayName("로그인 시 db에 사용자 정보가 있는 경우 Access Token, Refresh Token, 그 외 사용자 정보를 응답으로 보낸다") void signIn() { - // dto 검증 - SignInResponse responseDto = authService.signIn(TEST_USER_EMAIL, "google"); - - assertThat(responseDto.getAccessToken()).isNotNull(); - assertThat(responseDto.getRefreshToken()).isNotNull(); - - assertThat(responseDto.getNickname()).isEqualTo(TEST_USER_NICKNAME); - assertThat(responseDto.getMajor()).isEqualTo("경제학과"); - assertThat(responseDto.getSchool()).isEqualTo("서울과학기술대학교"); - assertThat(responseDto.getNickname()).isNotBlank(); - assertThat(responseDto.getSchool()).isNotBlank(); - assertThat(responseDto.getMajor()).isNotBlank(); - assertThat(responseDto.getImgUrl()).isNotBlank(); + // given + SignInServiceRequest request = SignInServiceRequest.builder() + .email(TEST_USER_EMAIL).provider("google").build(); + // when + SignInResponse response = authService.signIn(request); + + // then + assertThat(response.getAccessToken()).isNotNull(); + assertThat(response.getRefreshToken()).isNotNull(); + + assertThat(response.getNickname()).isEqualTo(TEST_USER_NICKNAME); + assertThat(response.getMajor()).isEqualTo("컴퓨터공학과"); + assertThat(response.getSchool()).isEqualTo("서울과학기술대학교"); + assertThat(response.getNickname()).isNotBlank(); + assertThat(response.getSchool()).isNotBlank(); + assertThat(response.getMajor()).isNotBlank(); + assertThat(response.getImgUrl()).isNotBlank(); } @Test @DisplayName("로그인 시 db에 사용자 정보가 없는 경우 CustomException 리턴") void signIn_User_Not_Existed() { // given - User userB = createUser("kakao_4237", "userA@kakao.com", "kakao", "testA"); + SignInServiceRequest request = SignInServiceRequest.builder() + .email("userA@kakao.com").provider("kakao").build(); - // when - assertThrows(CustomException.class, () -> authService.signIn(userB.getEmail(), userB.getProvider())); + // when // then + assertThrows(CustomException.class, () -> authService.signIn(request)); } @Test @DisplayName("로그인 시 TokenRepository 에 이미 RefreshToken 정보가 있는 경우, Token 값이 update 된다") void signIn_update_RefreshToken() throws InterruptedException { + // given + SignInServiceRequest request = SignInServiceRequest.builder() + .email(TEST_USER_EMAIL).provider(TEST_USER_PROVIDER).build(); + // First Login - SignInResponse responseDto = authService.signIn(TEST_USER_EMAIL, TEST_USER_PROVIDER); + SignInResponse responseDto = authService.signIn(request); String firstRT = responseDto.getRefreshToken(); Thread.sleep(3000); // Second Login: Update RefreshToken in TokenRepository: firstRT -> secondRT - SignInResponse responseDto2 = authService.signIn(TEST_USER_EMAIL, TEST_USER_PROVIDER); + SignInResponse responseDto2 = authService.signIn(request); String secondRT = responseDto2.getRefreshToken(); assertThat(firstRT).isNotEqualTo(secondRT); @@ -163,23 +172,60 @@ void signIn_update_RefreshToken() throws InterruptedException { } private User createUser(String id, String email, String provider, String nickname) { + String major = "컴퓨터공학과"; + String subMajor = null; + String minor = null; + String company = null; + Integer companyYear = 0; return User.builder() .id(id).email(email).provider(provider).nickname(nickname) - .role(UserRole.MENTEE).birthYear(2000).birthMonth(3) + .birthYear(2000).birthMonth(3) .school("서울과학기술대학교").score(3).scoreRange("중반") .graduateDate(2021).graduateMonth(3) - .major("경제학과").subMajor(null) - .minor(null).field("백엔드").techStack("AWS") - .optionInfo(null) + .major(major).subMajor(subMajor).minor(minor) + .field("백엔드").techStack("AWS") + .career(null) + .certificate(null) + .awards(null) + .activity(null) + .company(company) + .companyYear(companyYear) .imgUrl(defaultImgUrl) // set img url .build(); } - private SignUpRequest createSignUpReqDto(String id, String email, String provider, String nickname) { - return new SignUpRequest(id, email, provider, nickname, - UserRole.MENTEE, 2000, 3, "고려대학교", - 3, "중반", 2021, 3, "경제학과", null, null, null, - "Devops", "AWS", null, null, null, null); + private SignUpServiceRequest createSignUpServiceReqDto(String id, String email, String provider, String nickname) { + String major = "컴퓨터공학과"; + String subMajor = null; + String minor = null; + String company = null; + Integer companyYear = 3; + String field = "백엔드"; + String techStack = "AWS"; + return SignUpServiceRequest.builder() + .userId(id) + .email(email) + .provider(provider) + .nickname(nickname) + .birthYear(2000) + .birthMonth(3) + .school("서울과학기술대학교") + .score(3) + .scoreRange("중반") + .graduateDate(2021) + .graduateMonth(3) + .major(major) + .subMajor(subMajor) + .minor(minor) + .field(field) + .techStack(techStack) + .career(null) + .certificate(null) + .awards(null) + .activity(null) + .company(company) + .companyYear(companyYear) + .build(); } } \ No newline at end of file diff --git a/src/test/java/seoultech/capstone/menjil/domain/auth/dao/TokenRepositoryTest.java b/src/test/java/seoultech/capstone/menjil/domain/auth/dao/TokenRepositoryTest.java index a480144..9592eb1 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/auth/dao/TokenRepositoryTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/auth/dao/TokenRepositoryTest.java @@ -7,10 +7,8 @@ import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.test.context.ActiveProfiles; -import org.springframework.transaction.annotation.Transactional; import seoultech.capstone.menjil.domain.auth.domain.RefreshToken; import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; import java.sql.Timestamp; import java.time.LocalDateTime; @@ -21,7 +19,6 @@ @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // MySQL 등의 db 를 사용할 경우 추가설정 @DataJpaTest @ActiveProfiles("test") -@Transactional class TokenRepositoryTest { @Autowired @@ -110,12 +107,15 @@ void update_RefreshToken_In_Db() { private User createUser(String id, String email, String provider, String nickname) { return User.builder() .id(id).email(email).provider(provider).nickname(nickname) - .role(UserRole.MENTEE).birthYear(2000).birthMonth(3) + .birthYear(2000).birthMonth(3) .school("고려대학교").score(3).scoreRange("중반") .graduateDate(2021).graduateMonth(3) .major("경제학과").subMajor(null) .minor(null).field("백엔드").techStack("AWS") - .optionInfo(null) + .career(null) + .certificate(null) + .awards(null) + .activity(null) .build(); } diff --git a/src/test/java/seoultech/capstone/menjil/domain/auth/dao/UserRepositoryTest.java b/src/test/java/seoultech/capstone/menjil/domain/auth/dao/UserRepositoryTest.java index 998b2ed..8706f8a 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/auth/dao/UserRepositoryTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/auth/dao/UserRepositoryTest.java @@ -6,19 +6,14 @@ import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.test.context.ActiveProfiles; -import org.springframework.transaction.annotation.Transactional; import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; import java.util.List; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.tuple; -import static org.assertj.core.api.InstanceOfAssertFactories.OPTIONAL; -@Transactional -@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // To use MySQL @DataJpaTest @ActiveProfiles("test") class UserRepositoryTest { @@ -27,70 +22,65 @@ class UserRepositoryTest { private UserRepository userRepository; @Test - @DisplayName("Email, Provider 두 가지를 모두 만족하는 User 가 조회되는지를 검증한다.") + @DisplayName("Email, Provider 두 가지를 모두 만족하는 User가 조회되는지를 검증한다.") void findUserByEmailAndProvider() { // given - User userA = createUser("google_1", "userA@gmail.com", "google", "g1"); + String email = "userA@gmail.com"; + String provider = "google"; + User userA = createTestUser("google_1", email, provider, "g1"); // UserA 와 email 동일 - User userB = createUser("google_2", "userA@gmail.com", "kakao", "g2"); + User userB = createTestUser("google_2", "userA@gmail.com", "kakao", "g2"); // UserA 와 provider 동일 - User userC = createUser("google_2", "userAA@gmail.com", "google", "g3"); + User userC = createTestUser("google_2", "userAA@gmail.com", "google", "g3"); userRepository.saveAll(List.of(userA, userB, userC)); // when - Optional user = userRepository.findUserByEmailAndProvider("userA@gmail.com", "google"); + Optional user = userRepository.findUserByEmailAndProvider(email, provider); // then assertThat(user.isPresent()).isTrue(); - assertThat(user.get().getEmail()).isEqualTo("userA@gmail.com"); - assertThat(user.get().getProvider()).isEqualTo("google"); + assertThat(user.get().getEmail()).isEqualTo(email); + assertThat(user.get().getProvider()).isEqualTo(provider); } @Test - @DisplayName("Nickname 을 통해 유저가 조회되는지 검증") + @DisplayName("Nickname을 통해 유저가 조회되는지 검증") void findUserByNickname() { // given - User userA = createUser("google_1", "userA@gmail.com", "google", "g1"); - User userB = createUser("google_11", "userA1@gmail.com", "google", "g2"); + String email = "userA@gmail.com"; + String nickname = "g1"; + User userA = createTestUser("google_1", email, "google", nickname); + User userB = createTestUser("google_11", "userA1@gmail.com", "google", "g2"); userRepository.saveAll(List.of(userA, userB)); // when - Optional nicknameExistsInDb = userRepository.findUserByNickname("g1"); - + Optional nicknameExistsInDb = userRepository.findUserByNickname(nickname); // then assertThat(nicknameExistsInDb.isPresent()).isTrue(); - assertThat(nicknameExistsInDb.get().getNickname()).isEqualTo("g1"); + assertThat(nicknameExistsInDb.get().getNickname()).isEqualTo(nickname); + assertThat(nicknameExistsInDb.get().getEmail()).isEqualTo(email); + assertThat(nicknameExistsInDb.get().getCareer()).isNull(); // NULL 검증 } - @Test - @DisplayName("중복된 닉네임이 db에 여러 개인 경우, 모두 불러오는지 검증") - void findAllByNickname() { - // given - User userA = createUser("google_1", "userA@gmail.com", "google", "g1"); - User userB = createUser("google_11", "userA1@gmail.com", "google", "g2"); - - - // when - - - // then - - } - private User createUser(String id, String email, String provider, String nickname) { + private User createTestUser(String id, String email, String provider, String nickname) { return User.builder() .id(id).email(email).provider(provider).nickname(nickname) - .role(UserRole.MENTEE).birthYear(2000).birthMonth(3) + .birthYear(2000).birthMonth(3) .school("고려대학교").score(3).scoreRange("중반") .graduateDate(2021).graduateMonth(3) - .major("경제학과").subMajor(null) - .minor(null).field("백엔드").techStack("AWS") - .optionInfo(null) + .major("경제학과").subMajor(null).minor(null) + .company(null).companyYear(null) + .field("백엔드").techStack("AWS") + .career(null) + .certificate(null) + .awards(null) + .activity(null) .build(); } } \ No newline at end of file diff --git a/src/test/java/seoultech/capstone/menjil/domain/chat/application/MessageRatingServiceTest.java b/src/test/java/seoultech/capstone/menjil/domain/chat/application/MessageRatingServiceTest.java index b1d00e6..f993113 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/chat/application/MessageRatingServiceTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/chat/application/MessageRatingServiceTest.java @@ -8,13 +8,18 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.transaction.annotation.Transactional; +import seoultech.capstone.menjil.domain.chat.dao.MessageRepository; import seoultech.capstone.menjil.domain.chat.dao.QaListRepository; +import seoultech.capstone.menjil.domain.chat.domain.ChatMessage; +import seoultech.capstone.menjil.domain.chat.domain.MessageType; import seoultech.capstone.menjil.domain.chat.domain.QaList; +import seoultech.capstone.menjil.domain.chat.domain.SenderType; import seoultech.capstone.menjil.domain.chat.dto.request.MessageClickIncViewsAndLikesRequest; import seoultech.capstone.menjil.domain.chat.dto.response.MessageClickIncViewsAndLikesResponse; import seoultech.capstone.menjil.global.exception.CustomException; import java.time.LocalDateTime; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -27,15 +32,33 @@ class MessageRatingServiceTest { @Autowired private MessageRatingService messageRatingService; + @Autowired + private MessageRepository messageRepository; + @Autowired private QaListRepository qaListRepository; private final Long DEFAULT_VALUE = 0L; - private String DOCUMENT_ID = ""; + private String CHAT_MESSAGE_DOCUMENT_ID = ""; + private String QA_LIST_DOCUMENT_ID = ""; + private final MessageType RATING_TYPE = MessageType.AI_SUMMARY_RATING; + + // TODO: 추후 테스트 코드 수정 예정 @BeforeEach void setUp() { LocalDateTime now = LocalDateTime.now(); + ChatMessage chatMessage1 = ChatMessage.builder() + .roomId("test_room_id") + .senderType(SenderType.MENTOR) + .senderNickname("mentor_") + .message("삭제되어야 할 메시지") + .messageList(null) + .messageType(MessageType.AI_SUMMARY_RATING) + .time(now.minusHours(1000)) + .build(); + messageRepository.save(chatMessage1); + QaList qaList1 = QaList.builder() .menteeNickname("mentee") .mentorNickname("mentor_") @@ -52,61 +75,98 @@ void setUp() { qaList1.setLikes(DEFAULT_VALUE); qaListRepository.save(qaList1); - // MessageClickIncViewsAndLikesRequest에서 _id 값이 필요하므로 미리 가져옴 - DOCUMENT_ID = qaList1.get_id(); + // _id 값이 필요하므로 미리 가져옴 + CHAT_MESSAGE_DOCUMENT_ID = chatMessage1.get_id(); + QA_LIST_DOCUMENT_ID = qaList1.get_id(); } @AfterEach void tearDown() { // delete mongodb manually + messageRepository.deleteAll(); qaListRepository.deleteAll(); } @Test - @DisplayName("case 1: QaList id가 db에 존재하지 않는 경우, 예외처리") - void incrementViewsAndLikes_id_not_inDB() { + @DisplayName("case 1: ChatMessage id가 db에 존재하지 않는 경우, 예외처리") + void incrementViewsAndLikes_chat_message_not_inDB() { // given - String _id = "not2fadsf13vzv"; // random value - MessageClickIncViewsAndLikesRequest request = new MessageClickIncViewsAndLikesRequest(_id, true); + String Id = "test1"; // 1st check. + String questionId = "not3344by2277"; // random value, 2nd check + MessageClickIncViewsAndLikesRequest request = new MessageClickIncViewsAndLikesRequest(Id, questionId, true); // then assertThrows(CustomException.class, () -> messageRatingService.incrementViewsAndLikes(request)); } + @Test - @DisplayName("case 2: 정상 로직, 좋아요를 누른 경우") + @DisplayName("case 2: QaList id가 db에 존재하지 않는 경우, 예외처리") + void incrementViewsAndLikes_qaList_id_not_inDB() { + // given + String Id = CHAT_MESSAGE_DOCUMENT_ID; + String questionId = "not3344by2277"; // random value + MessageClickIncViewsAndLikesRequest request = new MessageClickIncViewsAndLikesRequest(Id, questionId, true); + + // then + assertThrows(CustomException.class, () -> messageRatingService.incrementViewsAndLikes(request)); + } + + @Test + @DisplayName("case 3: 정상 로직, 좋아요를 누른 경우") void incrementViewsAndLikes_add_likes() { // given - String _id = DOCUMENT_ID; // saved value - Boolean likes = true; - MessageClickIncViewsAndLikesRequest request = new MessageClickIncViewsAndLikesRequest(_id, likes); + String Id = CHAT_MESSAGE_DOCUMENT_ID; + String questionId = QA_LIST_DOCUMENT_ID; + MessageClickIncViewsAndLikesRequest request = new MessageClickIncViewsAndLikesRequest(Id, questionId, true); + + // 먼저 db에 데이터가 존재하는지 검증 + Optional savedMessage = messageRepository.findBy_idAndMessageType(Id, RATING_TYPE); + assertThat(savedMessage.isPresent()).isTrue(); // when MessageClickIncViewsAndLikesResponse response = messageRatingService.incrementViewsAndLikes(request); // then int updateNum = 1; - assertThat(response.getQuestionId()).isEqualTo(_id); + assertThat(response.getQuestionId()).isEqualTo(questionId); assertThat(response.getViews()).isEqualTo(DEFAULT_VALUE + updateNum); assertThat(response.getLikes()).isEqualTo(DEFAULT_VALUE + updateNum); + + // 메서드 실행 이후 db에 데이터가 지워졌는지 검증 + Optional findMessage = messageRepository.findBy_idAndMessageType(Id, RATING_TYPE); + assertThat(findMessage.isEmpty()).isTrue(); + assertThat(messageRepository.findAll().size()).isZero(); } @Test - @DisplayName("case 2-1: 정상 로직, 좋아요를 누르지 않은 경우") + @DisplayName("case 3-1: 정상 로직, 좋아요를 누르지 않은 경우") void incrementViewsAndLikes_no_likes() { // given - String _id = DOCUMENT_ID; // saved value - Boolean likes = false; - MessageClickIncViewsAndLikesRequest request = new MessageClickIncViewsAndLikesRequest(_id, likes); + String Id = CHAT_MESSAGE_DOCUMENT_ID; + String questionId = QA_LIST_DOCUMENT_ID; + + // here is false + MessageClickIncViewsAndLikesRequest request = new MessageClickIncViewsAndLikesRequest(Id, questionId, false); + + // 먼저 db에 데이터가 존재하는지 검증 + Optional savedMessage = messageRepository.findBy_idAndMessageType(Id, RATING_TYPE); + assertThat(savedMessage.isPresent()).isTrue(); // when MessageClickIncViewsAndLikesResponse response = messageRatingService.incrementViewsAndLikes(request); // then int updateNum = 1; - assertThat(response.getQuestionId()).isEqualTo(_id); + assertThat(response.getQuestionId()).isEqualTo(questionId); assertThat(response.getViews()).isEqualTo(DEFAULT_VALUE + updateNum); assertThat(response.getLikes()).isEqualTo(DEFAULT_VALUE); + + // 메서드 실행 이후 db에 데이터가 지워졌는지 검증 + Optional findMessage = messageRepository.findBy_idAndMessageType(Id, RATING_TYPE); + assertThat(findMessage.isEmpty()).isTrue(); + assertThat(messageRepository.findAll().size()).isZero(); } + } \ No newline at end of file diff --git a/src/test/java/seoultech/capstone/menjil/domain/chat/application/MessageServiceTest.java b/src/test/java/seoultech/capstone/menjil/domain/chat/application/MessageServiceTest.java index 5692575..60161fb 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/chat/application/MessageServiceTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/chat/application/MessageServiceTest.java @@ -10,7 +10,6 @@ import org.springframework.transaction.annotation.Transactional; import seoultech.capstone.menjil.domain.auth.dao.UserRepository; import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; import seoultech.capstone.menjil.domain.chat.dao.MessageRepository; import seoultech.capstone.menjil.domain.chat.dao.RoomRepository; import seoultech.capstone.menjil.domain.chat.domain.MessageType; @@ -27,8 +26,6 @@ import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; -import static seoultech.capstone.menjil.global.exception.ErrorIntValue.TIME_INPUT_INVALID; -import static seoultech.capstone.menjil.global.exception.SuccessIntValue.SUCCESS; @SpringBootTest @Transactional @@ -59,10 +56,8 @@ void setUp() { roomRepository.save(room); // Save Mentee and Mentor - User mentee = createUser("google_123123", "mentee@mentee.com", TEST_MENTEE_NICKNAME, - UserRole.MENTEE); - User mentor = createUser("google_1231234", "mentor@mentor.com", TEST_MENTOR_NICKNAME, - UserRole.MENTOR); + User mentee = createUser("google_123123", "mentee@mentee.com", TEST_MENTEE_NICKNAME ); + User mentor = createUser("google_1231234", "mentor@mentor.com", TEST_MENTOR_NICKNAME ); userRepository.saveAll(List.of(mentee, mentor)); } @@ -285,15 +280,18 @@ private String createTimeFormatOfMessageResponse(LocalDateTime time) { return time.format(formatter); } - private User createUser(String id, String email, String nickname, UserRole role) { + private User createUser(String id, String email, String nickname) { return User.builder() .id(id).email(email).provider("google").nickname(nickname) - .role(role).birthYear(2000).birthMonth(3) + .birthYear(2000).birthMonth(3) .school("서울과학기술대학교").score(3).scoreRange("중반") .graduateDate(2021).graduateMonth(3) .major("경제학과").subMajor(null) .minor(null).field("백엔드").techStack("AWS") - .optionInfo(null) + .career(null) + .certificate(null) + .awards(null) + .activity(null) .imgUrl("default/profile.png") // set img url .build(); } diff --git a/src/test/java/seoultech/capstone/menjil/domain/chat/application/RoomServiceTest.java b/src/test/java/seoultech/capstone/menjil/domain/chat/application/RoomServiceTest.java index 964e2c6..493d8c5 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/chat/application/RoomServiceTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/chat/application/RoomServiceTest.java @@ -10,7 +10,6 @@ import org.springframework.transaction.annotation.Transactional; import seoultech.capstone.menjil.domain.auth.dao.UserRepository; import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; import seoultech.capstone.menjil.domain.chat.dao.MessageRepository; import seoultech.capstone.menjil.domain.chat.dao.RoomRepository; import seoultech.capstone.menjil.domain.chat.domain.ChatMessage; @@ -64,10 +63,8 @@ void setUp() { roomRepository.save(room); // Save Mentee and Mentor - User mentee = createUser("google_123123", "mentee@mentee.com", TEST_MENTEE_NICKNAME, - UserRole.MENTEE); - User mentor = createUser("google_1231234", "mentor@mentor.com", TEST_MENTOR_NICKNAME, - UserRole.MENTOR); + User mentee = createUser("google_123123", "mentee@mentee.com", TEST_MENTEE_NICKNAME); + User mentor = createUser("google_1231234", "mentor@mentor.com", TEST_MENTOR_NICKNAME); userRepository.saveAll(List.of(mentee, mentor)); } @@ -339,8 +336,8 @@ void getAllRoomsOfUser_By_MENTEE() { List mentors = Arrays.asList( // @BeforeEach에서 TEST_MENTOR_NICKNAME 유저를 저장하므로, 여기서 저장하면 DataIntegrityViolationException 발생함 // createUser("test_1", "testmentor1@google.com", TEST_MENTOR_NICKNAME, UserRole.MENTOR), - createUser("test_2", "testmentor2@google.com", room2MentorNickname, UserRole.MENTOR), - createUser("test_3", "testmentor3@google.com", room3MentorNickname, UserRole.MENTOR) + createUser("test_2", "testmentor2@google.com", room2MentorNickname), + createUser("test_3", "testmentor3@google.com", room3MentorNickname) ); userRepository.saveAll(mentors); @@ -439,8 +436,8 @@ void getAllRoomsOfUser_By_MENTOR() { List mentors = Arrays.asList( // @BeforeEach에서 TEST_MENTOR_NICKNAME 유저를 저장하므로, 여기서 저장하면 DataIntegrityViolationException 발생함 // createUser("test_1", "testmentee1@google.com", TEST_MENTEE_NICKNAME, UserRole.MENTEE), - createUser("test_2", "testmentee2@google.com", room2MenteeNickname, UserRole.MENTEE), - createUser("test_3", "testmentee3@google.com", room3MenteeNickname, UserRole.MENTEE) + createUser("test_2", "testmentee2@google.com", room2MenteeNickname), + createUser("test_3", "testmentee3@google.com", room3MenteeNickname) ); userRepository.saveAll(mentors); @@ -654,15 +651,18 @@ void findLastChatMessageByRoomId() { assertThat(lastMessage.getTime()).isAfterOrEqualTo(now.plusSeconds(FIXED_NUM * 1000).withNano(0)); } - private User createUser(String id, String email, String nickname, UserRole role) { + private User createUser(String id, String email, String nickname) { return User.builder() .id(id).email(email).provider("google").nickname(nickname) - .role(role).birthYear(2000).birthMonth(3) + .birthYear(2000).birthMonth(3) .school("서울과학기술대학교").score(3).scoreRange("중반") .graduateDate(2021).graduateMonth(3) .major("경제학과").subMajor(null) .minor(null).field("백엔드").techStack("AWS") - .optionInfo(null) + .career(null) + .certificate(null) + .awards(null) + .activity(null) .imgUrl("default/profile.png") // set img url .build(); } diff --git a/src/test/java/seoultech/capstone/menjil/domain/chat/dao/RoomRepositoryTest.java b/src/test/java/seoultech/capstone/menjil/domain/chat/dao/RoomRepositoryTest.java index 2597456..bf2edd2 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/chat/dao/RoomRepositoryTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/chat/dao/RoomRepositoryTest.java @@ -7,14 +7,12 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ActiveProfiles; -import org.springframework.transaction.annotation.Transactional; import seoultech.capstone.menjil.domain.chat.domain.Room; import java.time.LocalDateTime; import static org.assertj.core.api.Assertions.assertThat; -@Transactional @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @DataJpaTest @ActiveProfiles("test") diff --git a/src/test/java/seoultech/capstone/menjil/domain/follow/api/FollowControllerTest.java b/src/test/java/seoultech/capstone/menjil/domain/follow/api/FollowControllerTest.java index 155d79a..74b40a4 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/follow/api/FollowControllerTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/follow/api/FollowControllerTest.java @@ -6,20 +6,18 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; -import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import seoultech.capstone.menjil.domain.follow.application.FollowService; -import seoultech.capstone.menjil.domain.follow.dto.request.FollowRequest; +import seoultech.capstone.menjil.domain.follow.api.dto.request.FollowCreateRequest; import seoultech.capstone.menjil.global.config.WebConfig; +import seoultech.capstone.menjil.global.exception.CustomException; import seoultech.capstone.menjil.global.exception.ErrorCode; -import seoultech.capstone.menjil.global.exception.ErrorIntValue; import seoultech.capstone.menjil.global.exception.SuccessCode; import static org.hamcrest.Matchers.is; @@ -31,9 +29,7 @@ import static seoultech.capstone.menjil.global.exception.SuccessIntValue.*; -@MockBean(JpaMetamodelMappingContext.class) @WebMvcTest(controllers = FollowController.class, - excludeAutoConfiguration = {SecurityAutoConfiguration.class}, excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = WebConfig.class) }) @@ -42,28 +38,30 @@ class FollowControllerTest { @Autowired private MockMvc mvc; + @Autowired + private Gson gson; + @MockBean private FollowService followService; - private final String TEST_USER_NICKNAME = "test_user_nickname"; - private final String TEST_FOLLOW_NICKNAME = "test_follow_nickname"; + private final String TEST_USER_NICKNAME = "user_nickname33"; + private final String TEST_FOLLOW_NICKNAME = "follow_nickname33"; /** - * followRequest + * createFollow */ @Test @DisplayName("case 1: 팔로우가 생성된 경우") - void followRequest_follow_created() throws Exception { + void createFollow_follow_created() throws Exception { // given - FollowRequest followRequest = FollowRequest.of(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME); - Gson gson = new Gson(); - String content = gson.toJson(followRequest); + FollowCreateRequest followCreateRequest = FollowCreateRequest.of(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME); + String content = gson.toJson(followCreateRequest); // when - Mockito.when(followService.followRequest(followRequest)).thenReturn(FOLLOW_CREATED.getValue()); + Mockito.when(followService.createFollow(followCreateRequest.toServiceRequest())).thenReturn(FOLLOW_CREATED.getValue()); // then - mvc.perform(MockMvcRequestBuilders.post("/api/follow/request") + mvc.perform(MockMvcRequestBuilders.post("/api/follow/create") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().isCreated()) @@ -72,22 +70,21 @@ void followRequest_follow_created() throws Exception { .andExpect(jsonPath("$.data", is(Matchers.nullValue()))) // Check for null .andDo(print()); - verify(followService, times(1)).followRequest(followRequest); + verify(followService, times(1)).createFollow(followCreateRequest.toServiceRequest()); } @Test @DisplayName("case 2: 팔로우가 제거된 경우") - void followRequest_follow_deleted() throws Exception { + void createFollow_follow_deleted() throws Exception { // given - FollowRequest followRequest = FollowRequest.of(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME); - Gson gson = new Gson(); - String content = gson.toJson(followRequest); + FollowCreateRequest followCreateRequest = FollowCreateRequest.of(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME); + String content = gson.toJson(followCreateRequest); // when - Mockito.when(followService.followRequest(followRequest)).thenReturn(FOLLOW_DELETED.getValue()); + Mockito.when(followService.createFollow(followCreateRequest.toServiceRequest())).thenReturn(FOLLOW_DELETED.getValue()); // then - mvc.perform(MockMvcRequestBuilders.post("/api/follow/request") + mvc.perform(MockMvcRequestBuilders.post("/api/follow/create") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().isCreated()) @@ -96,22 +93,22 @@ void followRequest_follow_deleted() throws Exception { .andExpect(jsonPath("$.data", is(Matchers.nullValue()))) // Check for null .andDo(print()); - verify(followService, times(1)).followRequest(followRequest); + verify(followService, times(1)).createFollow(followCreateRequest.toServiceRequest()); } @Test @DisplayName("case 3: 서버 오류") - void followRequest_INTERNAL_SERVER_ERROR() throws Exception { + void createFollow_INTERNAL_SERVER_ERROR() throws Exception { // given - FollowRequest followRequest = FollowRequest.of(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME); - Gson gson = new Gson(); - String content = gson.toJson(followRequest); + FollowCreateRequest followCreateRequest = FollowCreateRequest.of(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME); + String content = gson.toJson(followCreateRequest); // when - Mockito.when(followService.followRequest(followRequest)).thenReturn(ErrorIntValue.INTERNAL_SERVER_ERROR.getValue()); + Mockito.when(followService.createFollow(followCreateRequest.toServiceRequest())) + .thenThrow(new CustomException(ErrorCode.INTERNAL_SERVER_ERROR)); // then - mvc.perform(MockMvcRequestBuilders.post("/api/follow/request") + mvc.perform(MockMvcRequestBuilders.post("/api/follow/create") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().is5xxServerError()) @@ -120,7 +117,7 @@ void followRequest_INTERNAL_SERVER_ERROR() throws Exception { .andExpect(jsonPath("$.data", is(Matchers.nullValue()))) // Check for null .andDo(print()); - verify(followService, times(1)).followRequest(followRequest); + verify(followService, times(1)).createFollow(followCreateRequest.toServiceRequest()); } /** diff --git a/src/test/java/seoultech/capstone/menjil/domain/follow/application/FollowServiceTest.java b/src/test/java/seoultech/capstone/menjil/domain/follow/application/FollowServiceTest.java index 46bae10..0422c79 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/follow/application/FollowServiceTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/follow/application/FollowServiceTest.java @@ -7,9 +7,9 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.transaction.annotation.Transactional; +import seoultech.capstone.menjil.domain.follow.application.dto.request.FollowCreateServiceRequest; import seoultech.capstone.menjil.domain.follow.dao.FollowRepository; import seoultech.capstone.menjil.domain.follow.domain.Follow; -import seoultech.capstone.menjil.domain.follow.dto.request.FollowRequest; import java.time.LocalDateTime; import java.util.Optional; @@ -40,39 +40,36 @@ void setUp() { } /** - * followRequest + * createFollow */ @Test - @DisplayName("db에 이미 팔로우 내용이 존재하는 경우, FOLLOW_DELETED=1 을 리턴한다") - void followRequest_return_FOLLOW_DELETED() { + @DisplayName("case 1: db에 이미 팔로우 내용이 존재하는 경우, FOLLOW_DELETED=1 을 리턴한다") + void createFollow_return_FOLLOW_DELETED() { // given - FollowRequest followRequest = FollowRequest.of(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME); + FollowCreateServiceRequest request = FollowCreateServiceRequest.of(TEST_USER_NICKNAME, TEST_FOLLOW_NICKNAME); // when - // 실행하기 전 db에 데이터가 존재하는지 검증 - assertThat(followRepository.findAll().size()).isOne(); + assertThat(followRepository.findAll().size()).isOne(); // 실행하기 전 db에 데이터가 존재하는지 검증 - int result = followService.followRequest(followRequest); + int result = followService.createFollow(request); // then assertThat(result).isEqualTo(FOLLOW_DELETED.getValue()); - - // db에서 지워졌는지 검증 - assertThat(followRepository.findAll().size()).isZero(); + assertThat(followRepository.findAll().size()).isZero(); // db에서 지워졌는지 검증 } @Test - @DisplayName("팔로우 내용이 db에 존재하지 않는 경우, FOLLOW_CRETAED=0 을 리턴한다") - void followRequest_return_FOLLOW_CRETAED() { + @DisplayName("case 2: db에 팔로우 내용이 존재하지 않는 경우, FOLLOW_CRETAED=0 을 리턴한다") + void createFollow_return_FOLLOW_CRETAED() { // given String testUser = "testUser"; - FollowRequest followRequest = FollowRequest.of(testUser, TEST_FOLLOW_NICKNAME); + FollowCreateServiceRequest request = FollowCreateServiceRequest.of(testUser, TEST_FOLLOW_NICKNAME); // when // 실행하기 전 db에 데이터가 존재하는지 검증 assertThat(followRepository.findAll().size()).isOne(); - int result = followService.followRequest(followRequest); + int result = followService.createFollow(request); // then assertThat(result).isEqualTo(FOLLOW_CREATED.getValue()); diff --git a/src/test/java/seoultech/capstone/menjil/domain/following/api/FollowingControllerTest.java b/src/test/java/seoultech/capstone/menjil/domain/following/api/FollowingControllerTest.java new file mode 100644 index 0000000..cd47bac --- /dev/null +++ b/src/test/java/seoultech/capstone/menjil/domain/following/api/FollowingControllerTest.java @@ -0,0 +1,162 @@ +package seoultech.capstone.menjil.domain.following.api; + +import com.google.gson.Gson; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import seoultech.capstone.menjil.domain.following.application.FollowingService; +import seoultech.capstone.menjil.domain.following.application.dto.FollowingQaDto; +import seoultech.capstone.menjil.domain.following.application.dto.FollowingUserInfoDto; +import seoultech.capstone.menjil.domain.following.application.dto.response.FollowingUserInfoResponse; +import seoultech.capstone.menjil.global.config.WebConfig; +import seoultech.capstone.menjil.global.exception.CustomException; +import seoultech.capstone.menjil.global.exception.ErrorCode; +import seoultech.capstone.menjil.global.exception.SuccessCode; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(controllers = FollowingController.class, + excludeFilters = { + @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = WebConfig.class) + }) +class FollowingControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private Gson gson; + + @MockBean + private FollowingService followingService; + + @Test + void getAllFollowOfUsers() { + } + + @Test + @DisplayName("case 1: 정상 로직] 사용자의 정보와 질문답변 내용이 모두 존재한다") + void getFollowUserInfo() throws Exception { + // given + String followNickname = "userA"; + Long answersCount = 2L; + List answers = new ArrayList<>(); + answers.add(FollowingQaDto.builder() + .questionOrigin("원본 질문1") + .questionSummary("원본 요약1") + .answer("답변1") + .answerTime(LocalDateTime.now()) + .likes(2L) + .views(11L) + .build()); + answers.add(FollowingQaDto.builder() + .questionOrigin("원본 질문2") + .questionSummary("원본 요약2") + .answer("답변2") + .answerTime(LocalDateTime.now().plusSeconds(5000)) + .likes(4L) + .views(10L) + .build()); + + FollowingUserInfoResponse response = createTestFollowingUserInfoResponse(followNickname, + answersCount, answers); + + // when + Mockito.when(followingService.getFollowUserInfo(followNickname)).thenReturn(response); + + // then + mockMvc.perform(MockMvcRequestBuilders.get("/api/following/info") + .queryParam("followNickname", followNickname)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code", is(SuccessCode.GET_FOLLOW_USER_INFO_SUCCESS.getCode()))) + .andExpect(jsonPath("$.message", is(SuccessCode.GET_FOLLOW_USER_INFO_SUCCESS.getMessage()))) + .andDo(print()); + + verify(followingService, times(1)).getFollowUserInfo(followNickname); + } + + + @Test + @DisplayName("case 1-1: 정상 로직] 사용자의 정보만 존재하며, 질문답변 내역은 존재하지 않는다.") + void getFollowUserInfo_qaObject_is_not_existed() throws Exception { + // given + String followNickname = "userA"; + Long answersCount = 0L; + List answers = new ArrayList<>(); + + FollowingUserInfoResponse response = createTestFollowingUserInfoResponse(followNickname, + answersCount, answers); + + // when + Mockito.when(followingService.getFollowUserInfo(followNickname)).thenReturn(response); + + // then + mockMvc.perform(MockMvcRequestBuilders.get("/api/following/info") + .queryParam("followNickname", followNickname)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code", is(SuccessCode.GET_FOLLOW_USER_INFO_SUCCESS.getCode()))) + .andExpect(jsonPath("$.message", is(SuccessCode.GET_FOLLOW_USER_INFO_SUCCESS.getMessage()))) + .andDo(print()); + + verify(followingService, times(1)).getFollowUserInfo(followNickname); + } + + @Test + @DisplayName("case 2: 요청한 사용자의 닉네임이 데이터베이스에 없는 경우") + void getFollowUserInfo_user_not_in_db() throws Exception { + // given + String followNickname = "user333_not_in_db"; + + // when + Mockito.when(followingService.getFollowUserInfo(followNickname)) + .thenThrow(new CustomException(ErrorCode.INTERNAL_SERVER_ERROR)); + + // then + mockMvc.perform(MockMvcRequestBuilders.get("/api/following/info") + .queryParam("followNickname", followNickname)) + .andExpect(status().is5xxServerError()) + .andExpect(jsonPath("$.code", is(ErrorCode.INTERNAL_SERVER_ERROR.getHttpStatus().value()))) + .andExpect(jsonPath("$.message", is(ErrorCode.INTERNAL_SERVER_ERROR.getMessage()))) + .andDo(print()); + + verify(followingService, times(1)).getFollowUserInfo(followNickname); + } + + private FollowingUserInfoResponse createTestFollowingUserInfoResponse(String followNickname, + Long answersCount, + List answers) { + FollowingUserInfoDto followingUserInfoDto = FollowingUserInfoDto.builder() + .nickname(followNickname) + .company("Google") + .field("백엔드") + .school("서울과학기술대학교") + .major("컴퓨터공학과") + .subMajor(null) + .minor(null) + .techStack("Spring Boot, AWS") + .imgUrl("https://...") + .career(null) + .certificate(null) + .awards(null) + .activity(null) + .build(); + + return FollowingUserInfoResponse.of(followingUserInfoDto, answersCount, answers); + } +} \ No newline at end of file diff --git a/src/test/java/seoultech/capstone/menjil/domain/following/application/FollowingServiceTest.java b/src/test/java/seoultech/capstone/menjil/domain/following/application/FollowingServiceTest.java index 37a6d9b..aa8d470 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/following/application/FollowingServiceTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/following/application/FollowingServiceTest.java @@ -9,15 +9,13 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.transaction.annotation.Transactional; import seoultech.capstone.menjil.domain.auth.dao.UserRepository; -import seoultech.capstone.menjil.domain.auth.domain.OptionInfo; import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; import seoultech.capstone.menjil.domain.chat.dao.QaListRepository; import seoultech.capstone.menjil.domain.chat.domain.QaList; import seoultech.capstone.menjil.domain.follow.dao.FollowRepository; import seoultech.capstone.menjil.domain.follow.domain.Follow; -import seoultech.capstone.menjil.domain.following.dto.FollowingQaDto; -import seoultech.capstone.menjil.domain.following.dto.response.FollowingMentorInfoResponse; +import seoultech.capstone.menjil.domain.following.application.dto.FollowingQaDto; +import seoultech.capstone.menjil.domain.following.application.dto.response.FollowingUserInfoResponse; import java.time.LocalDateTime; import java.util.List; @@ -43,24 +41,24 @@ class FollowingServiceTest { @Autowired private QaListRepository qaListRepository; - private final String TEST_MENTEE_NICKNAME = "test_mentee_1"; - private final String TEST_MENTOR_NICKNAME_1 = "test_mentor_1"; - private final int qaNumWithAnswers = 11; + private final String USER_NICKNAME = "test_user_1"; + private final String FOLLOWER_NICKNAME = "test_follower_1"; + private final String FOLLOWER_NICKNAME_FORMAT = "test_follower_"; + private final int QA_NUM_WITH_ANSWERS = 11; @BeforeEach void setUp() { - // Save Mentee and Mentors - User mentee = createTestUser("google_123123", "mentee@mentee.com", TEST_MENTEE_NICKNAME, - UserRole.MENTEE); - userRepository.save(mentee); + // Save Users + User user = createTestUser("google_1112223344", "userA@google.com", USER_NICKNAME); + userRepository.save(user); - int mentorNum = 30; - List users = IntStream.rangeClosed(1, mentorNum) + int followerNum = 30; + List users = IntStream.rangeClosed(1, followerNum) .mapToObj(i -> { - String id = "google_" + i; - String email = "mentor" + i + "@mentor.com"; - String nickname = "test_mentor_" + i; - return createTestUser(id, email, nickname, UserRole.MENTOR); + String id = "google_" + (1234567899 + i); + String email = "testFollower" + i + "@google.com"; + String nickname = FOLLOWER_NICKNAME_FORMAT + i; + return createTestUser(id, email, nickname); }) .collect(Collectors.toList()); userRepository.saveAll(users); @@ -70,17 +68,17 @@ void setUp() { int followNum = 6; List follows = IntStream.rangeClosed(1, followNum) .mapToObj(i -> { - String menteeNickname = TEST_MENTEE_NICKNAME; - String mentorNickname = "test_mentor_" + i; - return Follow.of(menteeNickname, mentorNickname, now.plusMinutes(i)); + String userNickname = USER_NICKNAME; + String followerNickname = FOLLOWER_NICKNAME_FORMAT + i; + return Follow.of(userNickname, followerNickname, now.plusMinutes(i)); }) .collect(Collectors.toList()); followRepository.saveAll(follows); // Save QaList with answer is not null - // mentor is only TEST_MENTOR_NICKNAME_1 - List qaLists = IntStream.rangeClosed(1, qaNumWithAnswers) - .mapToObj(i -> createTestQaListAndAnswerIsNotNull(TEST_MENTOR_NICKNAME_1, i)) + // mentor is only TEST_FOLLOW_NICKNAME_1 + List qaLists = IntStream.rangeClosed(1, QA_NUM_WITH_ANSWERS) + .mapToObj(i -> createTestQaListAndAnswerIsNotNull(FOLLOWER_NICKNAME, i)) .collect(Collectors.toList()); qaListRepository.saveAll(qaLists); @@ -88,7 +86,7 @@ void setUp() { // mentor is only TEST_MENTOR_NICKNAME_1 int qaNumWithAnswerIsNull = 8; List qaListsWithAnswerIsNull = IntStream.rangeClosed(1, qaNumWithAnswerIsNull) - .mapToObj(i -> createTestQaListAndAnswerIsNull(TEST_MENTOR_NICKNAME_1, i)) + .mapToObj(i -> createTestQaListAndAnswerIsNull(FOLLOWER_NICKNAME, i)) .collect(Collectors.toList()); qaListRepository.saveAll(qaListsWithAnswerIsNull); } @@ -100,22 +98,23 @@ void tearDown() { } /** - * getAllFollowMentors + * getAllFollowOfUsers */ @Test - void getAllFollowMentors() { + void getAllFollowOfUsers() { } /** - * getFollowMentorInfo + * getFollowUserInfo */ @Test - @DisplayName("case 1: 멘토가 팔로우 되어 있고, 질문답변 데이터도 존재하는 경우") - void getFollowMentorInfo() { + @DisplayName("case 1: 팔로우 된 사용자가 존재하고, 질문답변 데이터도 존재하는 경우") + void getFollowUserInfo() { // given + String follower = FOLLOWER_NICKNAME; // when - FollowingMentorInfoResponse followMentorInfo = followingService.getFollowMentorInfo(TEST_MENTEE_NICKNAME, TEST_MENTOR_NICKNAME_1); + FollowingUserInfoResponse followMentorInfo = followingService.getFollowUserInfo(follower); // then assertThat(followMentorInfo).isNotNull(); @@ -123,8 +122,8 @@ void getFollowMentorInfo() { .getMajor()).isEqualTo("컴퓨터공학과"); assertThat(followMentorInfo.getFollowingUserInfoDto() .getCareer()).isNull(); - assertThat(followMentorInfo.getAnswersCount()).isEqualTo(qaNumWithAnswers); - assertThat(followMentorInfo.getAnswers().size()).isEqualTo(qaNumWithAnswers); + assertThat(followMentorInfo.getAnswersCount()).isEqualTo(QA_NUM_WITH_ANSWERS); + assertThat(followMentorInfo.getAnswers().size()).isEqualTo(QA_NUM_WITH_ANSWERS); // check if answer_time Order by ASC List qaDtos = followMentorInfo.getAnswers(); @@ -147,14 +146,14 @@ void getFollowMentorInfo() { } @Test - @DisplayName("case 2: 멘토가 팔로우는 되어 있으나, 질문답변 데이터가 존재하지 않는 경우") - void getFollowMentorInfo_QaData_is_Empty() { + @DisplayName("case 2: 팔로우 된 사용자는 존재하지만, 질문답변 데이터가 존재하지 않는 경우") + void getFollowUserInfo_QaData_is_Empty() { // given - // test_mentor_2의 경우 팔로우는 되어 있으나, 질문답변 정보는 없다. - String mentor2 = "test_mentor_2"; + // test_follower_2의 경우 팔로우는 되어 있으나, 질문답변 정보는 없다. + String follower_2 = "test_follower_2"; // when - FollowingMentorInfoResponse followMentorInfo = followingService.getFollowMentorInfo(TEST_MENTEE_NICKNAME, mentor2); + FollowingUserInfoResponse followMentorInfo = followingService.getFollowUserInfo(follower_2); // then assertThat(followMentorInfo).isNotNull(); @@ -168,6 +167,24 @@ void getFollowMentorInfo_QaData_is_Empty() { assertThat(followMentorInfo.getAnswers().size()).isZero(); } + /** + 이 부분은 회의에서 로직상, 팔로우 관계 없이도 사용자 정보를 열람하도록 정하였으므로, 따로 구현하지 않음. + */ + /*@Test + @DisplayName("case 3: 팔로우 관계가 존재하지 않는 사용자에 대해 요청하면, CustomException을 유발한다") + void getFollowUserInfo_Follow_is_not_existed_between_users() { + // given + String follower_100 = "test_follower_100"; + + // save User + User user = createTestUser("google_1112223344", follower_100 + "@google.com", follower_100); + userRepository.save(user); + + // when // then + Assertions.assertThatThrownBy(() -> followingService.getFollowUserInfo(follower_100)) + .isInstanceOf(CustomException.class); + }*/ + /** * getLastAnsweredMessages @@ -179,7 +196,7 @@ void getLastAnsweredMessages_returns_empty_List() { String id = "google_1234123124"; String email = "mentor2@mentor.com"; String nickname = "mentor_test_33"; - User mentor1 = createTestUser(id, email, nickname, UserRole.MENTOR); + User mentor1 = createTestUser(id, email, nickname); // when List lastAnsweredMessages = followingService.getLastAnsweredMessages(mentor1.getNickname()); @@ -195,7 +212,7 @@ void getLastAnsweredMessages_size_is_one() { String id = "google_1234123124"; String email = "mentor2@mentor.com"; String nickname = "mentor_test_33"; - User mentor1 = createTestUser(id, email, nickname, UserRole.MENTOR); + User mentor1 = createTestUser(id, email, nickname); int qaNum = 1; List qaLists = IntStream.rangeClosed(1, qaNum) @@ -217,8 +234,7 @@ void getLastAnsweredMessages_size_is_more_than_two() { String id = "google_1234123124"; String email = "mentor2@mentor.com"; String nickname = "mentor_test_33"; - User mentor1 = createTestUser(id, email, nickname, UserRole.MENTOR); - + User mentor1 = createTestUser(id, email, nickname); int qaNum = 5; List qaLists = IntStream.rangeClosed(1, qaNum) .mapToObj(i -> createTestQaListAndAnswerIsNotNull(mentor1.getNickname(), i)) @@ -232,15 +248,20 @@ void getLastAnsweredMessages_size_is_more_than_two() { assertThat(lastAnsweredMessages.size()).isEqualTo(2); } - private User createTestUser(String id, String email, String nickname, UserRole role) { + private User createTestUser(String id, String email, String nickname) { return User.builder() .id(id).email(email).provider("google").nickname(nickname) - .role(role).birthYear(2000).birthMonth(3) + .birthYear(2000).birthMonth(3) .school("서울과학기술대학교").score(3).scoreRange("중반") .graduateDate(2021).graduateMonth(3) .major("컴퓨터공학과").subMajor("심리학과") .minor(null).field("백엔드").techStack("AWS") - .optionInfo(new OptionInfo(null, null, null, null)) + .company(null) + .companyYear(0) + .career(null) + .certificate(null) + .awards(null) + .activity(null) .imgUrl("default/profile.png") // set img url .build(); } @@ -248,7 +269,7 @@ private User createTestUser(String id, String email, String nickname, UserRole r private QaList createTestQaListAndAnswerIsNotNull(String mentorNickname, int index) { LocalDateTime now = LocalDateTime.now(); return QaList.builder() - .menteeNickname(TEST_MENTEE_NICKNAME) + .menteeNickname(USER_NICKNAME) .mentorNickname(mentorNickname) .questionOrigin("origin message_" + index) .questionSummary("summary message_" + index) @@ -259,10 +280,11 @@ private QaList createTestQaListAndAnswerIsNotNull(String mentorNickname, int ind .build(); } + // TODO: QaList를 QaObject로 명칭 변경, 및 menteeNickname, mentorNickname column 명 변경 private QaList createTestQaListAndAnswerIsNull(String mentorNickname, int index) { LocalDateTime now = LocalDateTime.now(); return QaList.builder() - .menteeNickname(TEST_MENTEE_NICKNAME) + .menteeNickname(USER_NICKNAME) .mentorNickname(mentorNickname) .questionOrigin("origin message_" + index) .questionSummary("summary message_" + index) diff --git a/src/test/java/seoultech/capstone/menjil/domain/main/api/MainPageControllerTest.java b/src/test/java/seoultech/capstone/menjil/domain/main/api/MainPageControllerTest.java index ca5a018..660d091 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/main/api/MainPageControllerTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/main/api/MainPageControllerTest.java @@ -14,12 +14,11 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; import seoultech.capstone.menjil.domain.chat.application.RoomService; import seoultech.capstone.menjil.domain.chat.dto.response.RoomInfoResponse; import seoultech.capstone.menjil.domain.main.application.MainPageService; -import seoultech.capstone.menjil.domain.main.dto.response.FollowUserResponse; -import seoultech.capstone.menjil.domain.main.dto.response.MentorInfoResponse; +import seoultech.capstone.menjil.domain.main.application.dto.response.FollowUserResponse; +import seoultech.capstone.menjil.domain.main.application.dto.response.UserInfoResponse; import seoultech.capstone.menjil.global.config.WebConfig; import seoultech.capstone.menjil.global.exception.SuccessCode; @@ -72,8 +71,8 @@ void getMentors_page_0_mentor_is_not_exist() throws Exception { /* 여기서 주의사항: MainPageController의 파라미터 중 @PageableDefault에서 작성한 값과 일치하도록 작성해야 한다 */ Pageable pageable = PageRequest.of(pageNumber, SIZE, SORT); - List responseList = new ArrayList<>(); - Page page = new PageImpl<>(responseList); + List responseList = new ArrayList<>(); + Page page = new PageImpl<>(responseList); // when Mockito.when(mainPageService.getMentors(nickname, pageable)).thenReturn(page); @@ -83,8 +82,8 @@ void getMentors_page_0_mentor_is_not_exist() throws Exception { .queryParam("nickname", nickname) .queryParam("page", String.valueOf(pageable.getPageNumber()))) .andExpect(status().isOk()) - .andExpect(jsonPath("$.code", is(SuccessCode.GET_MENTOR_LIST_AVAILABLE.getCode()))) - .andExpect(jsonPath("$.message", is(SuccessCode.GET_MENTOR_LIST_AVAILABLE.getMessage()))) + .andExpect(jsonPath("$.code", is(SuccessCode.GET_USERS_AVAILABLE.getCode()))) + .andExpect(jsonPath("$.message", is(SuccessCode.GET_USERS_AVAILABLE.getMessage()))) .andExpect(jsonPath("$.data.content").isEmpty()) // 빈 리스트이므로, doesNotExist() (X) .andDo(print()); @@ -101,13 +100,13 @@ void getMentors_page_0_mentor_2() throws Exception { /* 여기서 주의사항: MainPageController의 파라미터 중 @PageableDefault에서 작성한 값과 일치하도록 작성해야 한다 */ Pageable pageable = PageRequest.of(pageNumber, SIZE, SORT); - // 현재 MentorInfoResponse는 of 메서드가 없으므로, + // 현재 UserInfoResponse는 of 메서드가 없으므로, // fromUserEntity 메서드를 사용하기 위해, userA, userB 생성 - User userA = createTestUser("google_1231323", "test@google.com", "test_1", UserRole.MENTOR); - User userB = createTestUser("google_1231324", "test2@google.com", "test_2", UserRole.MENTOR); - List responseList = List.of(MentorInfoResponse.fromUserEntity(userA), - MentorInfoResponse.fromUserEntity(userB)); - Page page = new PageImpl<>(responseList); + User userA = createTestUser("google_1231323", "test@google.com", "test_1"); + User userB = createTestUser("google_1231324", "test2@google.com", "test_2"); + List responseList = List.of(UserInfoResponse.fromUserEntity(userA), + UserInfoResponse.fromUserEntity(userB)); + Page page = new PageImpl<>(responseList); // when Mockito.when(mainPageService.getMentors(nickname, pageable)).thenReturn(page); @@ -117,8 +116,8 @@ void getMentors_page_0_mentor_2() throws Exception { .queryParam("nickname", nickname) .queryParam("page", String.valueOf(pageable.getPageNumber()))) .andExpect(status().isOk()) - .andExpect(jsonPath("$.code", is(SuccessCode.GET_MENTOR_LIST_AVAILABLE.getCode()))) - .andExpect(jsonPath("$.message", is(SuccessCode.GET_MENTOR_LIST_AVAILABLE.getMessage()))) + .andExpect(jsonPath("$.code", is(SuccessCode.GET_USERS_AVAILABLE.getCode()))) + .andExpect(jsonPath("$.message", is(SuccessCode.GET_USERS_AVAILABLE.getMessage()))) .andExpect(jsonPath("$.data").exists()) .andExpect(jsonPath("$.data.content[0]").isNotEmpty()) .andExpect(jsonPath("$.data.content[1]").isNotEmpty()) @@ -244,15 +243,18 @@ void getFollowersOfUser_follow_is_none() throws Exception { } - private User createTestUser(String id, String email, String nickname, UserRole role) { + private User createTestUser(String id, String email, String nickname) { return User.builder() .id(id).email(email).provider("google").nickname(nickname) - .role(role).birthYear(2000).birthMonth(3) + .birthYear(2000).birthMonth(3) .school("서울과학기술대학교").score(3).scoreRange("중반") .graduateDate(2021).graduateMonth(3) .major("컴퓨터공학과").subMajor(null) .minor(null).field("백엔드").techStack("AWS") - .optionInfo(null) + .career(null) + .certificate(null) + .awards(null) + .activity(null) .imgUrl("default/profile.png") // set img url .build(); } diff --git a/src/test/java/seoultech/capstone/menjil/domain/main/application/MainPageServiceTest.java b/src/test/java/seoultech/capstone/menjil/domain/main/application/MainPageServiceTest.java index 5207709..0bb697c 100644 --- a/src/test/java/seoultech/capstone/menjil/domain/main/application/MainPageServiceTest.java +++ b/src/test/java/seoultech/capstone/menjil/domain/main/application/MainPageServiceTest.java @@ -13,14 +13,13 @@ import org.springframework.transaction.annotation.Transactional; import seoultech.capstone.menjil.domain.auth.dao.UserRepository; import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; import seoultech.capstone.menjil.domain.chat.dao.MessageRepository; import seoultech.capstone.menjil.domain.chat.dao.QaListRepository; import seoultech.capstone.menjil.domain.chat.domain.QaList; import seoultech.capstone.menjil.domain.follow.dao.FollowRepository; import seoultech.capstone.menjil.domain.follow.domain.Follow; -import seoultech.capstone.menjil.domain.main.dto.response.FollowUserResponse; -import seoultech.capstone.menjil.domain.main.dto.response.MentorInfoResponse; +import seoultech.capstone.menjil.domain.main.application.dto.response.FollowUserResponse; +import seoultech.capstone.menjil.domain.main.application.dto.response.UserInfoResponse; import java.time.LocalDateTime; import java.util.List; @@ -59,8 +58,8 @@ class MainPageServiceTest { @BeforeEach void setUp() { - User userA = createTestUser("google_1231323", "test@google.com", TEST_MENTEE_NICKNAME, UserRole.MENTEE); - User userB = createTestUser("google_1231324", "test2@google.com", TEST_MENTOR_NICKNAME, UserRole.MENTOR); + User userA = createTestUser("google_1231323", "test@google.com", TEST_MENTEE_NICKNAME); + User userB = createTestUser("google_1231324", "test2@google.com", TEST_MENTOR_NICKNAME); userRepository.saveAll(List.of(userA, userB)); } @@ -92,11 +91,11 @@ void getMentors_page_0() throws InterruptedException { String testId = "google_" + i; String testEmail = "test_" + i + "@gmail.com"; String testNickname = MENTOR_NICKNAME + i; - return createTestUser(testId, testEmail, testNickname, UserRole.MENTOR); + return createTestUser(testId, testEmail, testNickname); }) .collect(Collectors.toList()); // collects the User objects into a List - // createdDate 값을 조절하기 위해, Thread.sleep() 사용: 하지만 MentorInfoResponse에서 시간 데이터를 사용하지 않으므로, 큰 의미는 없다. + // createdDate 값을 조절하기 위해, Thread.sleep() 사용: 하지만 UserInfoResponse에서 시간 데이터를 사용하지 않으므로, 큰 의미는 없다. // 추후 리팩토링할때 무시할 것 userRepository.saveAll(List.of(users.get(0), users.get(1))); Thread.sleep(1000); @@ -108,14 +107,14 @@ void getMentors_page_0() throws InterruptedException { // when // nickname은 중요하지 않다. - Page mentorList = mainPageService.getMentors("test1", pageRequest); + Page mentorList = mainPageService.getMentors("test1", pageRequest); // then assertThat(mentorList.getSize()).isEqualTo(3); - MentorInfoResponse firstMentor = mentorList.getContent().get(0); - MentorInfoResponse secondMentor = mentorList.getContent().get(1); - MentorInfoResponse thirdMentor = mentorList.getContent().get(2); + UserInfoResponse firstMentor = mentorList.getContent().get(0); + UserInfoResponse secondMentor = mentorList.getContent().get(1); + UserInfoResponse thirdMentor = mentorList.getContent().get(2); assertThat(firstMentor.getNickname()).isEqualTo(MENTOR_NICKNAME + 1); assertThat(firstMentor.getImgUrl()).isNotBlank(); @@ -141,11 +140,11 @@ void getMentors_page_2() throws InterruptedException { String testId = "google_" + i; String testEmail = "test_" + i + "@gmail.com"; String testNickname = MENTOR_NICKNAME + i; - return createTestUser(testId, testEmail, testNickname, UserRole.MENTOR); + return createTestUser(testId, testEmail, testNickname); }) .collect(Collectors.toList()); // collects the User objects into a List - // createdDate 값을 조절하기 위해, Thread.sleep() 사용: 하지만 MentorInfoResponse에서 시간 데이터를 사용하지 않으므로, 큰 의미는 없다. + // createdDate 값을 조절하기 위해, Thread.sleep() 사용: 하지만 UserInfoResponse에서 시간 데이터를 사용하지 않으므로, 큰 의미는 없다. // 추후 리팩토링할때 무시할 것 userRepository.saveAll(List.of(users.get(0), users.get(1))); Thread.sleep(1000); @@ -157,7 +156,7 @@ void getMentors_page_2() throws InterruptedException { // when // nickname은 중요하지 않다. - Page mentorList = mainPageService.getMentors("test1", pageRequest); + Page mentorList = mainPageService.getMentors("test1", pageRequest); // then // size 값은 SIZE 값과 동일하다. @@ -166,8 +165,8 @@ void getMentors_page_2() throws InterruptedException { // content의 개수가 2개이다. assertThat(mentorList.getContent().size()).isEqualTo(2); - MentorInfoResponse firstMentor = mentorList.getContent().get(0); - MentorInfoResponse secondMentor = mentorList.getContent().get(1); + UserInfoResponse firstMentor = mentorList.getContent().get(0); + UserInfoResponse secondMentor = mentorList.getContent().get(1); assertThat(firstMentor.getNickname()).isEqualTo(MENTOR_NICKNAME + 7); assertThat(firstMentor.getImgUrl()).isNotBlank(); @@ -192,11 +191,11 @@ void getMentors_page_3() throws InterruptedException { String testId = "google_" + i; String testEmail = "test_" + i + "@gmail.com"; String testNickname = MENTOR_NICKNAME + i; - return createTestUser(testId, testEmail, testNickname, UserRole.MENTOR); + return createTestUser(testId, testEmail, testNickname); }) .collect(Collectors.toList()); // collects the User objects into a List - // createdDate 값을 조절하기 위해, Thread.sleep() 사용: 하지만 MentorInfoResponse에서 시간 데이터를 사용하지 않으므로, 큰 의미는 없다. + // createdDate 값을 조절하기 위해, Thread.sleep() 사용: 하지만 UserInfoResponse에서 시간 데이터를 사용하지 않으므로, 큰 의미는 없다. // 추후 리팩토링할때 무시할 것 userRepository.saveAll(List.of(users.get(0), users.get(1))); Thread.sleep(1000); @@ -208,7 +207,7 @@ void getMentors_page_3() throws InterruptedException { // when // nickname은 중요하지 않다. - Page mentorList = mainPageService.getMentors("test1", pageRequest); + Page mentorList = mainPageService.getMentors("test1", pageRequest); // then assertThat(mentorList.getContent().size()).isEqualTo(0); @@ -236,7 +235,7 @@ void getFollowersOfUser() { String testId = "google_" + i; String testEmail = "test_" + i + "@gmail.com"; String testNickname = MENTOR_NICKNAME + i; - return createTestUser(testId, testEmail, testNickname, UserRole.MENTOR); + return createTestUser(testId, testEmail, testNickname); }) .collect(Collectors.toList()); @@ -270,7 +269,7 @@ void getLastAnsweredMessages_returns_empty_ArrayList() { String id = "google_1234123124"; String email = "mentor2@mentor.com"; String nickname = "mentor_test_33"; - User mentor1 = createTestUser(id, email, nickname, UserRole.MENTOR); + User mentor1 = createTestUser(id, email, nickname); // when List lastAnsweredMessages = mainPageService.getLastAnsweredMessages(mentor1.getNickname()); @@ -286,7 +285,7 @@ void getLastAnsweredMessages_size_is_one() { String id = "google_1234123124"; String email = "mentor2@mentor.com"; String nickname = "mentor_test_33"; - User mentor1 = createTestUser(id, email, nickname, UserRole.MENTOR); + User mentor1 = createTestUser(id, email, nickname); int qaNum = 1; List qaLists = IntStream.rangeClosed(1, qaNum) @@ -309,7 +308,7 @@ void getLastAnsweredMessages_size_is_more_than_two() { String id = "google_1234123124"; String email = "mentor2@mentor.com"; String nickname = "mentor_test_33"; - User mentor1 = createTestUser(id, email, nickname, UserRole.MENTOR); + User mentor1 = createTestUser(id, email, nickname); int qaNum = 5; List qaLists = IntStream.rangeClosed(1, qaNum) @@ -325,15 +324,18 @@ void getLastAnsweredMessages_size_is_more_than_two() { assertThat(lastAnsweredMessages.size()).isEqualTo(2); } - private User createTestUser(String id, String email, String nickname, UserRole role) { + private User createTestUser(String id, String email, String nickname) { return User.builder() .id(id).email(email).provider("google").nickname(nickname) - .role(role).birthYear(2000).birthMonth(3) + .birthYear(2000).birthMonth(3) .school("서울과학기술대학교").score(3).scoreRange("중반") .graduateDate(2021).graduateMonth(3) .major("경제학과").subMajor(null) .minor(null).field("백엔드").techStack("AWS") - .optionInfo(null) + .career(null) + .certificate(null) + .awards(null) + .activity(null) .imgUrl("default/profile.png") // set img url .build(); } diff --git a/src/test/java/seoultech/capstone/menjil/global/filter/JwtAuthenticationFilterTest.java b/src/test/java/seoultech/capstone/menjil/global/filter/JwtAuthenticationFilterTest.java index e5dfd5b..f436c59 100644 --- a/src/test/java/seoultech/capstone/menjil/global/filter/JwtAuthenticationFilterTest.java +++ b/src/test/java/seoultech/capstone/menjil/global/filter/JwtAuthenticationFilterTest.java @@ -18,7 +18,6 @@ import seoultech.capstone.menjil.domain.auth.dao.UserRepository; import seoultech.capstone.menjil.domain.auth.domain.RefreshToken; import seoultech.capstone.menjil.domain.auth.domain.User; -import seoultech.capstone.menjil.domain.auth.domain.UserRole; import seoultech.capstone.menjil.domain.auth.jwt.JwtTokenProvider; import java.sql.Timestamp; @@ -222,12 +221,15 @@ void refreshToken_is_not_reliable() throws Exception { private User createUser(String id, String email, String provider, String nickname) { return User.builder() .id(id).email(email).provider(provider).nickname(nickname) - .role(UserRole.MENTEE).birthYear(2000).birthMonth(3) + .birthYear(2000).birthMonth(3) .school("고려대학교").score(3).scoreRange("중반") .graduateDate(2021).graduateMonth(3) .major("경제학과").subMajor(null) .minor(null).field("백엔드").techStack("AWS") - .optionInfo(null) + .career(null) + .certificate(null) + .awards(null) + .activity(null) .build(); } } \ No newline at end of file