diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index b81ca45..20252a1 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -11,12 +11,58 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + install: true + + - name: Create buildx builder + run: | + docker buildx create --use --name mybuilder + docker buildx inspect --bootstrap + + - name: Login to DockerHub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build & Push Dependency Cache + run: | + docker buildx build \ + --builder mybuilder \ + --platform linux/amd64 \ + --push \ + --file Dockerfile \ + --tag ${{ secrets.DOCKERHUB_USERNAME }}/assu-app:dependency-cache \ + --target dependencies \ + --cache-to type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/assu-app:dependency-cache,mode=max \ + . + + - name: Build & Push Final App Image + run: | + docker buildx build \ + --builder mybuilder \ + --platform linux/amd64 \ + --push \ + --file Dockerfile \ + --tag ${{ secrets.DOCKERHUB_USERNAME }}/assu-app:latest \ + --build-arg DEPENDENCY_IMAGE=${{ secrets.DOCKERHUB_USERNAME }}/assu-app:dependency-cache \ + --cache-from type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/assu-app:dependency-cache \ + . + - name: Create application-secret.yml run: | mkdir -p ./temp_secret echo "${{ secrets.APPLICATION_SECRET }}" > ./temp_secret/application-secret.yml shell: bash + - name: Create service-account.json + run: | + mkdir -p ./temp_secret + echo "${{ secrets.SERVICE_ACCOUNT }}" > ./temp_secret/service-account.json + shell: bash + - name: Copy application-secret.yml to EC2 uses: appleboy/scp-action@v0.1.3 with: @@ -26,6 +72,15 @@ jobs: source: ./temp_secret/application-secret.yml target: /home/ubuntu/secret/ + - name: Copy service-account.json to EC2 + uses: appleboy/scp-action@v0.1.3 + with: + username: ubuntu + host: ${{ secrets.EC2_HOST }} + key: ${{ secrets.EC2_SSH_KEY }} + source: ./temp_secret/service-account.json + target: /home/ubuntu/secret/ + - name: Copy docker-compose.yml uses: appleboy/scp-action@v0.1.3 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c6e1a0..bb76cd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,8 @@ name: CI Pipeline on: push: branches: [ develop ] + pull_request: + branches: [ develop ] jobs: ci: @@ -32,44 +34,4 @@ jobs: run: chmod +x ./gradlew - name: Build and Test - run: ./gradlew clean build test - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - install: true - - - name: Create buildx builder - run: | - docker buildx create --use --name mybuilder - docker buildx inspect --bootstrap - - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Build & Push Dependency Cache - run: | - docker buildx build \ - --builder mybuilder \ - --platform linux/amd64 \ - --push \ - --file Dockerfile \ - --tag ${{ secrets.DOCKERHUB_USERNAME }}/assu-app:dependency-cache \ - --target dependencies \ - --cache-to type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/assu-app:dependency-cache,mode=max \ - . - - - name: Build & Push Final App Image - run: | - docker buildx build \ - --builder mybuilder \ - --platform linux/amd64 \ - --push \ - --file Dockerfile \ - --tag ${{ secrets.DOCKERHUB_USERNAME }}/assu-app:latest \ - --build-arg DEPENDENCY_IMAGE=${{ secrets.DOCKERHUB_USERNAME }}/assu-app:dependency-cache \ - --cache-from type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/assu-app:dependency-cache \ - . + run: ./gradlew clean build test --no-build-cache diff --git a/.gitignore b/.gitignore index a0e3821..4c53f8a 100644 --- a/.gitignore +++ b/.gitignore @@ -44,8 +44,6 @@ out/ ### Secret ### src/main/resources/application-secret.yml -src/test/resources/application-test.yml -src/test/resources/application-secret.yml ### Firebase ### src/main/resources/firebase/ diff --git a/docker-compose.yml b/docker-compose.yml index 1ece20c..59c3991 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,8 @@ services: - SPRING_PROFILES_ACTIVE=blue - SPRING_CONFIG_ADDITIONAL_LOCATION=file:/app/config/ volumes: - - /home/ubuntu/app/config/application-secret.yml:/app/config/application-secret.yml:ro + - /home/ubuntu/secret/application-secret.yml:/app/config/application-secret.yml:ro + - /home/ubuntu/secret/service-account.json:/app/config/service-account.json:ro networks: - assu-network @@ -27,7 +28,8 @@ services: - SPRING_PROFILES_ACTIVE=green - SPRING_CONFIG_ADDITIONAL_LOCATION=file:/app/config/ volumes: - - /home/ubuntu/app/config/application-secret.yml:/app/config/application-secret.yml:ro + - /home/ubuntu/secret/application-secret.yml:/app/config/application-secret.yml:ro + - /home/ubuntu/secret/service-account.json:/app/config/service-account.json:ro networks: - assu-network diff --git a/src/main/java/com/assu/server/domain/auth/controller/AuthController.java b/src/main/java/com/assu/server/domain/auth/controller/AuthController.java index 7346a4f..3e60979 100644 --- a/src/main/java/com/assu/server/domain/auth/controller/AuthController.java +++ b/src/main/java/com/assu/server/domain/auth/controller/AuthController.java @@ -28,7 +28,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -@Tag(name = "Auth", description = "인증/회원가입 API") +@Tag(name = "Auth", description = "인증/인가 API") @RestController @RequiredArgsConstructor @RequestMapping("/auth") @@ -41,12 +41,16 @@ public class AuthController { private final SSUAuthService ssuAuthService; @Operation( - summary = "휴대폰 인증번호 발송 API (추후 개발)", - description = "# v1.0\n" + + summary = "휴대폰 인증번호 발송 API", + description = "#[v1.0 (2025-09-03)](https://clumsy-seeder-416.notion.site/2241197c19ed801bbcd9f61c3e5f5457?source=copy_link)\n" + "- 입력한 휴대폰 번호로 1회용 인증번호(OTP)를 발송합니다.\n" + - "- 유효시간/재요청 제한 정책은 서버 설정에 따릅니다." + "- 유효시간/재요청 제한 정책은 서버 설정에 따릅니다.\n" + + "\n**Request Body:**\n" + + " - `phoneNumber` (String, required): 인증번호를 받을 휴대폰 번호\n" + + "\n**Response:**\n" + + " - 성공 시 200(OK)과 성공 메시지 반환" ) - @PostMapping("/phone-numbers/send") + @PostMapping("/phone-verification/send") public BaseResponse sendAuthNumber( @RequestBody @Valid PhoneAuthRequestDTO.PhoneAuthSendRequest request ) { @@ -55,12 +59,17 @@ public BaseResponse sendAuthNumber( } @Operation( - summary = "휴대폰 인증번호 검증 API (추후 개발)", - description = "# v1.0\n" + + summary = "휴대폰 인증번호 검증 API", + description = "#[v1.0 (2025-09-03)](https://clumsy-seeder-416.notion.site/2241197c19ed81bb8c05d9061c0306c0?source=copy_link)\n" + "- 발송된 인증번호(OTP)를 검증합니다.\n" + - "- 성공 시 서버에 휴대폰 인증 상태가 기록됩니다." + "- 성공 시 서버에 휴대폰 인증 상태가 기록됩니다.\n" + + "\n**Request Body:**\n" + + " - `phoneNumber` (String, required): 인증받을 휴대폰 번호\n" + + " - `authNumber` (String, required): 발송받은 인증번호(OTP)\n" + + "\n**Response:**\n" + + " - 성공 시 200(OK)과 성공 메시지 반환" ) - @PostMapping("/phone-numbers/verify") + @PostMapping("/phone-verification/verify") public BaseResponse checkAuthNumber( @RequestBody @Valid PhoneAuthRequestDTO.PhoneAuthVerifyRequest request ) { @@ -73,12 +82,24 @@ public BaseResponse checkAuthNumber( @Operation( summary = "학생 회원가입 API", - description = "# v1.0 (2025-08-15)\n" + + description = "#[v1.0 (2025-09-03)](https://clumsy-seeder-416.notion.site/2241197c19ed81129c85cf5bbe1f7971?source=copy_link)\n" + "- `application/json` 요청 바디를 사용합니다.\n" + "- 처리: users + ssu_auth 등 가입 레코드 생성, 휴대폰 인증 여부 확인.\n" + - "- 성공 시 201(Created)과 생성된 memberId 반환." + "- 성공 시 201(Created)과 생성된 memberId 반환.\n" + + "\n**Request Body:**\n" + + " - `StudentSignUpRequest` 객체 (JSON, required): 학생 가입 정보\n" + + " - `email` (String, required): 이메일 주소\n" + + " - `password` (String, required): 비밀번호\n" + + " - `phoneNumber` (String, required): 휴대폰 번호\n" + + " - `studentNumber` (String, required): 학번\n" + + " - `name` (String, required): 학생 이름\n" + + " - `major` (Major enum, required): 전공\n" + + " - `grade` (Integer, required): 학년\n" + + " - `semester` (Integer, required): 학기\n" + + "\n**Response:**\n" + + " - 성공 시 201(Created)과 `SignUpResponse` 객체 반환" ) - @PostMapping(value = "/signup/student", consumes = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "/students/signup", consumes = MediaType.APPLICATION_JSON_VALUE) public BaseResponse signupStudent( @Valid @RequestBody StudentSignUpRequest request) { return BaseResponse.onSuccess(SuccessStatus._OK, signUpService.signupStudent(request)); @@ -86,13 +107,25 @@ public BaseResponse signupStudent( @Operation( summary = "제휴업체 회원가입 API", - description = "# v1.0 (2025-08-15)\n" + + description = "#[v1.0 (2025-09-03)](https://clumsy-seeder-416.notion.site/2501197c19ed80d7a8f2c3a6fcd8b537?source=copy_link)\n" + "- `multipart/form-data`로 호출합니다.\n" + "- 파트: `payload`(JSON, PartnerSignUpRequest) + `licenseImage`(파일, 사업자등록증).\n" + "- 처리: users + common_auth 생성, 이메일 중복/비밀번호 규칙 검증.\n" + - "- 성공 시 201(Created)과 생성된 memberId 반환." + "- 성공 시 201(Created)과 생성된 memberId 반환.\n" + + "\n**Request Parts:**\n" + + " - `request` (JSON, required): `PartnerSignUpRequest` 객체\n" + + " - `email` (String, required): 이메일 주소\n" + + " - `password` (String, required): 비밀번호\n" + + " - `phoneNumber` (String, required): 휴대폰 번호\n" + + " - `companyName` (String, required): 회사명\n" + + " - `businessNumber` (String, required): 사업자등록번호\n" + + " - `representativeName` (String, required): 대표자명\n" + + " - `address` (String, required): 회사 주소\n" + + " - `licenseImage` (MultipartFile, required): 사업자등록증 이미지 파일\n" + + "\n**Response:**\n" + + " - 성공 시 201(Created)과 `SignUpResponse` 객체 반환" ) - @PostMapping(value = "/signup/partner", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping(value = "/partners/signup", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public BaseResponse signupPartner( @Valid @RequestPart("request") @Parameter( @@ -117,13 +150,24 @@ public BaseResponse signupPartner( @Operation( summary = "관리자 회원가입 API", - description = "# v1.0 (2025-08-15)\n" + + description = "#[v1.0 (2025-09-03)](https://clumsy-seeder-416.notion.site/2501197c19ed80cdb98bc2b4d5042b48?source=copy_link)\n" + "- `multipart/form-data`로 호출합니다.\n" + "- 파트: `payload`(JSON, AdminSignUpRequest) + `signImage`(파일, 신분증).\n" + "- 처리: users + common_auth 생성, 이메일 중복/비밀번호 규칙 검증.\n" + - "- 성공 시 201(Created)과 생성된 memberId 반환." + "- 성공 시 201(Created)과 생성된 memberId 반환.\n" + + "\n**Request Parts:**\n" + + " - `request` (JSON, required): `AdminSignUpRequest` 객체\n" + + " - `email` (String, required): 이메일 주소\n" + + " - `password` (String, required): 비밀번호\n" + + " - `phoneNumber` (String, required): 휴대폰 번호\n" + + " - `name` (String, required): 관리자 이름\n" + + " - `department` (String, required): 소속 부서\n" + + " - `position` (String, required): 직책\n" + + " - `signImage` (MultipartFile, required): 인감 이미지 파일\n" + + "\n**Response:**\n" + + " - 성공 시 201(Created)과 `SignUpResponse` 객체 반환" ) - @PostMapping(value = "/signup/admin", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping(value = "/admins/signup", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public BaseResponse signupAdmin( @Valid @RequestPart("request") @Parameter( @@ -148,17 +192,26 @@ public BaseResponse signupAdmin( // 로그인 (파트너/관리자 공통) @Operation( summary = "공통 로그인 API", - description = "# v1.0 (2025-08-15)\n" + + description = "#[v1.0 (2025-09-03)](https://clumsy-seeder-416.notion.site/2241197c19ed811c961be6a474de0e50?source=copy_link)\n" + "- `application/json`로 호출합니다.\n" + "- 바디: `LoginRequest(email, password)`.\n" + "- 처리: 자격 증명 검증 후 Access/Refresh 토큰 발급 및 저장.\n" + - "- 성공 시 200(OK)과 토큰/만료시각 반환." + "- 성공 시 200(OK)과 토큰/만료시각 반환.\n" + + "\n**Request Body:**\n" + + " - `CommonLoginRequest` 객체 (JSON, required): 로그인 정보\n" + + " - `email` (String, required): 이메일 주소\n" + + " - `password` (String, required): 비밀번호\n" + + "\n**Response:**\n" + + " - 성공 시 200(OK)과 `LoginResponse` 객체 반환\n" + + " - `accessToken` (String): 액세스 토큰\n" + + " - `refreshToken` (String): 리프레시 토큰\n" + + " - `expiresAt` (LocalDateTime): 토큰 만료 시각" ) @io.swagger.v3.oas.annotations.parameters.RequestBody( required = true, content = @Content(schema = @Schema(implementation = CommonLoginRequest.class)) ) - @PostMapping(value = "/login/common", consumes = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "/commons/login", consumes = MediaType.APPLICATION_JSON_VALUE) public BaseResponse loginCommon( @RequestBody @Valid CommonLoginRequest request ) { @@ -169,17 +222,27 @@ public BaseResponse loginCommon( // 학생 로그인 @Operation( summary = "학생 로그인 API", - description = "# v1.1 (2025-08-18)\n" + + description = "#[v1.0 (2025-09-03)](https://clumsy-seeder-416.notion.site/2501197c19ed80f6b495fa37f8c084a8?source=copy_link)\n" + "- `application/json`로 호출합니다.\n" + "- 바디: `바디: `StudentLoginRequest(studentNumber, studentPassword, school)`.\n" + "- 처리: 자격 증명 검증 후 Access/Refresh 토큰 발급 및 저장.\n" + - "- 성공 시 200(OK)과 토큰/만료시각 반환." + "- 성공 시 200(OK)과 토큰/만료시각 반환.\n" + + "\n**Request Body:**\n" + + " - `StudentLoginRequest` 객체 (JSON, required): 학생 로그인 정보\n" + + " - `studentNumber` (String, required): 학번\n" + + " - `studentPassword` (String, required): 학생 포털 비밀번호\n" + + " - `school` (String, required): 학교명\n" + + "\n**Response:**\n" + + " - 성공 시 200(OK)과 `LoginResponse` 객체 반환\n" + + " - `accessToken` (String): 액세스 토큰\n" + + " - `refreshToken` (String): 리프레시 토큰\n" + + " - `expiresAt` (LocalDateTime): 토큰 만료 시각" ) @io.swagger.v3.oas.annotations.parameters.RequestBody( required = true, content = @Content(schema = @Schema(implementation = StudentLoginRequest.class)) ) - @PostMapping(value = "/login/student", consumes = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "/students/login", consumes = MediaType.APPLICATION_JSON_VALUE) public BaseResponse loginStudent( @RequestBody @Valid StudentLoginRequest request ) { @@ -190,11 +253,20 @@ public BaseResponse loginStudent( // 액세스 토큰 갱신 @Operation( summary = "Access Token 갱신 API", - description = "# v1.0 (2025-08-15)\n" + + description = "#[v1.0 (2025-09-03)](https://clumsy-seeder-416.notion.site/2501197c19ed806ea8cff29f9cd8695a?source=copy_link)\n" + "- 헤더로 호출합니다.\n" + "- 헤더: `Authorization: Bearer `(만료 허용), `RefreshToken: `.\n" + "- 처리: Refresh 검증/회전 후 신규 Access/Refresh 발급 및 저장.\n" + - "- 성공 시 200(OK)과 새 토큰/만료시각 반환." + "- 성공 시 200(OK)과 새 토큰/만료시각 반환.\n" + + "\n**Headers:**\n" + + " - `Authorization` (String, required): Bearer 토큰 형식의 액세스 토큰 (만료 허용)\n" + + " - `RefreshToken` (String, required): 리프레시 토큰\n" + + "\n**Response:**\n" + + " - 성공 시 200(OK)과 `RefreshResponse` 객체 반환\n" + + " - `accessToken` (String): 새로운 액세스 토큰\n" + + " - `refreshToken` (String): 새로운 리프레시 토큰\n" + + " - `expiresAt` (LocalDateTime): 새 토큰 만료 시각\n" + + " - 성공 시 200(OK)과 새 토큰/만료시각 반환." ) @Parameters({ @Parameter(name = "Authorization", description = "Access Token (만료 허용). 형식: `Bearer `", required = true, @@ -202,7 +274,7 @@ public BaseResponse loginStudent( @Parameter(name = "RefreshToken", description = "Refresh Token", required = true, in = ParameterIn.HEADER, schema = @Schema(type = "string")) }) - @PostMapping("/refresh") + @PostMapping("/tokens/refresh") public BaseResponse refreshToken( @RequestHeader("RefreshToken") String refreshToken ) { @@ -213,7 +285,7 @@ public BaseResponse refreshToken( // 로그아웃 @Operation( summary = "로그아웃 API", - description = "# v1.0 (2025-08-15)\n" + + description = "#[v1.0 (2025-09-03)](https://clumsy-seeder-416.notion.site/23a1197c19ed809e9a09fcd741f554c8?source=copy_link)\n" + "- 헤더로 호출합니다.\n" + "- 헤더: `Authorization: Bearer `.\n" + "- 처리: Refresh 무효화(선택), Access 블랙리스트 등록.\n" + @@ -233,7 +305,7 @@ public BaseResponse logout( // 숭실대 인증 및 개인정보 조회 @Operation( summary = "숭실대 유세인트 인증 API", - description = "# v1.0 (2025-08-20)\n" + + description = "#[v1.0 (2025-09-03)](https://clumsy-seeder-416.notion.site/23a1197c19ed808d9266e641e5c4ea14?source=copy_link)\n" + "- `application/json`으로 호출합니다.\n" + "- 요청 바디: `USaintAuthRequest(sToken, sIdno)`.\n" + "- 처리 순서:\n" + @@ -248,7 +320,7 @@ public BaseResponse logout( required = true, content = @Content(schema = @Schema(implementation = USaintAuthRequest.class)) ) - @PostMapping(value = "/schools/ssu", consumes = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "/students/ssu-verify", consumes = MediaType.APPLICATION_JSON_VALUE) public BaseResponse ssuAuth( @RequestBody @Valid USaintAuthRequest request ) { diff --git a/src/main/java/com/assu/server/domain/auth/security/jwt/JwtUtil.java b/src/main/java/com/assu/server/domain/auth/security/jwt/JwtUtil.java index 84238bf..b3ae7f3 100644 --- a/src/main/java/com/assu/server/domain/auth/security/jwt/JwtUtil.java +++ b/src/main/java/com/assu/server/domain/auth/security/jwt/JwtUtil.java @@ -16,6 +16,7 @@ import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -33,6 +34,7 @@ */ @Component @RequiredArgsConstructor +@Profile("!test") public class JwtUtil { @Value("${jwt.secret}") @@ -49,7 +51,9 @@ public class JwtUtil { @PostConstruct public void clearRedisOnStartup() { - redisTemplate.getConnectionFactory().getConnection().flushAll(); + if (redisTemplate != null && redisTemplate.getConnectionFactory() != null) { + redisTemplate.getConnectionFactory().getConnection().flushAll(); + } } // ───────── 토큰 생성 공통 유틸 ───────── diff --git a/src/main/java/com/assu/server/global/config/FirebaseConfig.java b/src/main/java/com/assu/server/global/config/FirebaseConfig.java index fc2e0a7..2279867 100644 --- a/src/main/java/com/assu/server/global/config/FirebaseConfig.java +++ b/src/main/java/com/assu/server/global/config/FirebaseConfig.java @@ -7,11 +7,13 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; import org.springframework.core.io.Resource; import java.io.InputStream; @Configuration +@Profile("!test") public class FirebaseConfig { @Value("${firebase.project-id}") diff --git a/src/main/java/com/assu/server/global/config/RedisConfig.java b/src/main/java/com/assu/server/global/config/RedisConfig.java index b645501..35a9aba 100644 --- a/src/main/java/com/assu/server/global/config/RedisConfig.java +++ b/src/main/java/com/assu/server/global/config/RedisConfig.java @@ -8,6 +8,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @@ -17,6 +18,7 @@ @Configuration @EnableConfigurationProperties(RedisProperties.class) @RequiredArgsConstructor +@Profile("!test") public class RedisConfig { private final RedisProperties properties; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 25d23c9..c76f75b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -4,8 +4,6 @@ spring: initialize-schema: never job: enabled: false - profiles: - active: local # 여기에 local, blue, green 셋중 하나로 입력 config: import: - optional:classpath:application-secret.yml @@ -23,8 +21,4 @@ spring: logging: level: org.springframework.web: DEBUG - org.springframework.web.client.DefaultRestClient: OFF - -kakao: - base-url: https://dapi.kakao.com - rest-api-key: ${KAKAO_REST_API_KEY} \ No newline at end of file + org.springframework.web.client.DefaultRestClient: OFF \ No newline at end of file diff --git a/src/test/java/com/assu/server/ServerApplicationTests.java b/src/test/java/com/assu/server/ServerApplicationTests.java index b685821..5faba62 100644 --- a/src/test/java/com/assu/server/ServerApplicationTests.java +++ b/src/test/java/com/assu/server/ServerApplicationTests.java @@ -1,13 +1,54 @@ package com.assu.server; +import com.assu.server.domain.auth.security.jwt.JwtUtil; +import com.google.firebase.messaging.FirebaseMessaging; import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.ActiveProfiles; @SpringBootTest @ActiveProfiles("test") class ServerApplicationTests { + @Mock + private FirebaseMessaging firebaseMessaging; + + @TestConfiguration + static class MockConfig { + @Bean + FirebaseMessaging firebaseMessaging() { + return Mockito.mock(FirebaseMessaging.class); + } + + @Bean + RedisConnectionFactory redisConnectionFactory() { + return Mockito.mock(RedisConnectionFactory.class); + } + + @Bean + @SuppressWarnings("unchecked") + RedisTemplate redisTemplate() { + return Mockito.mock(RedisTemplate.class); + } + + @Bean + StringRedisTemplate stringRedisTemplate() { + return Mockito.mock(StringRedisTemplate.class); + } + + @Bean + JwtUtil jwtUtil() { + return Mockito.mock(JwtUtil.class); + } + } + @Test void contextLoads() { } diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml new file mode 100644 index 0000000..95151d4 --- /dev/null +++ b/src/test/resources/application-test.yml @@ -0,0 +1,41 @@ +spring: + autoconfigure: + exclude: + - org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration + - org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration + datasource: + url: jdbc:h2:mem:testdb + driver-class-name: org.h2.Driver + username: sa + password: + +jwt: + header: Authorization + prefix: Bearer + secret: dummy-secret-key-for-testing + access-valid-seconds: 3600 + refresh-valid-seconds: 1209600 + +assu: + security: + school-crypto: + base64-key: "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=" #"dummy-base64-key"를 Base64로 인코딩한 값 + +cloud: + aws: + s3: + bucket: test-bucket + region: + static: ap-northeast-2 + stack: + auto: false + credentials: + accessKey: dummy-access + secretKey: dummy-secret + +firebase: + enabled: false + +kakao: + base-url: https://dapi.kakao.com + rest-api-key: dummy-kakao-key \ No newline at end of file