From 2f718ad08477d95f52b00144abf623433bcba804 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Tue, 5 Nov 2024 20:59:24 +0900 Subject: [PATCH 01/13] Feat: Set CiCD --- .github/workflows/CICD.yml | 59 ++++++++++++++++++++++++++++++ appspec.yml | 22 +++++++++++ scripts/start.sh | 21 +++++++++++ scripts/stop.sh | 19 ++++++++++ src/main/resources/application.yml | 2 +- 5 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/CICD.yml create mode 100644 appspec.yml create mode 100644 scripts/start.sh create mode 100644 scripts/stop.sh diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml new file mode 100644 index 00000000..80e1036a --- /dev/null +++ b/.github/workflows/CICD.yml @@ -0,0 +1,59 @@ +# 1 워크플로의 이름 지정 +name: CI and Deploy to Amazon EC2 +# 2 워크플로가 시작될 조건 지정 +env: + AWS_REGION: ap-northeast-2 + S3_BUCKET_NAME: billage-deploy + CODE_DEPLOY_APPLICATION_NAME: billage-deploy + CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: billage-deploy-group +permissions: + contents: read +on: + push: + branches: [ master ] +jobs: + build: + runs-on: ubuntu-latest # 3 실행 환경 지정 + #4 실행스텝지정 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '17' + # db.properties 파일 생성 + - name: Make application.properties + run: | + cd ./src/main/resources + echo "${{ secrets.SECRET_PROPERTIES }}" > ./application-prod.yml + - name: Make FCM JSON + run: | + cd ./src/main/resources + mkdir -p ./firebase + echo "${{ secrets.AWS_FCM_JSON }}" > ./FirebaseAdminSDK.yml + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew clean build + # (4) AWS 인증 (IAM 사용자 Access Key, Secret Key 활용) + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + # (5) 빌드 결과물을 S3 버킷에 업로드 + - name: Upload to AWS S3 + run: | + aws deploy push \ + --application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \ + --s3-location s3://$S3_BUCKET_NAME/$GITHUB_SHA.zip \ + --source . + # (6) S3 버킷에 있는 파일을 대상으로 CodeDeploy 실행 + - name: Deploy to AWS EC2 from S3 + run: | + aws deploy create-deployment \ + --application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \ + --deployment-config-name CodeDeployDefault.AllAtOnce \ + --deployment-group-name ${{ env.CODE_DEPLOY_DEPLOYMENT_GROUP_NAME }} \ + --s3-location bucket=$S3_BUCKET_NAME,key=$GITHUB_SHA.zip,bundleType=zip diff --git a/appspec.yml b/appspec.yml new file mode 100644 index 00000000..3b004dd1 --- /dev/null +++ b/appspec.yml @@ -0,0 +1,22 @@ +version: 0.0 +os: linux +# destination에 아티팩트가 unzip된 결과가 생성될 디렉토리명을 넣어준다. +files: + - source: / + destination: /home/ubuntu/build/ + overwrite: yes +permissions: + - object: / + pattern: "**" + owner: ubuntu + group: ubuntu + +hooks: + AfterInstall: #AfterInstall에서 기존에 실행중이던 어플리케이션 종료시키고 ApplicationStart에서 새로운 어플리케이션 실행 + - location: scripts/stop.sh + timeout: 60 + runas: ubuntu + ApplicationStart: + - location: scripts/start.sh + timeout: 60 + runas: ubuntu diff --git a/scripts/start.sh b/scripts/start.sh new file mode 100644 index 00000000..6ef857a4 --- /dev/null +++ b/scripts/start.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +PROJECT_ROOT="/home/ubuntu" +JAR_FILE="$PROJECT_ROOT/billage-0.0.1-SNAPSHOT.jar" + +APP_LOG="$PROJECT_ROOT/application.log" +ERROR_LOG="$PROJECT_ROOT/error.log" +DEPLOY_LOG="$PROJECT_ROOT/deploy.log" + +TIME_NOW=$(date +%c) + +# build 파일 복사 +echo "$TIME_NOW > $JAR_FILE 파일 복사" >> $DEPLOY_LOG +cp $PROJECT_ROOT/build/build/libs/*.jar $JAR_FILE + +# jar 파일 실행 +echo "$TIME_NOW > $JAR_FILE 파일 실행" >> $DEPLOY_LOG +nohup java -jar $JAR_FILE > $APP_LOG 2> $ERROR_LOG & + +CURRENT_PID=$(pgrep -f $JAR_FILE) +echo "$TIME_NOW > 실행된 프로세스 아이디 $CURRENT_PID 입니다." >> $DEPLOY_LOG diff --git a/scripts/stop.sh b/scripts/stop.sh new file mode 100644 index 00000000..8a091e3c --- /dev/null +++ b/scripts/stop.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +PROJECT_ROOT="/home/ubuntu" +JAR_FILE="billage-0.0.1-SNAPSHOT.jar" + +DEPLOY_LOG="$PROJECT_ROOT/deploy.log" + +TIME_NOW=$(date +%c) + +# 현재 구동 중인 애플리케이션 pid 확인 +CURRENT_PID=$(pgrep -f $JAR_FILE) + +# 프로세스가 켜져 있으면 종료 +if [ -z $CURRENT_PID ]; then + echo "$TIME_NOW > 현재 실행중인 애플리케이션이 없습니다" >> $DEPLOY_LOG +else + echo "$TIME_NOW > 실행중인 $CURRENT_PID 애플리케이션 종료 " >> $DEPLOY_LOG + kill -15 $CURRENT_PID +fi diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a8687091..d2c11e32 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,6 @@ spring: profiles: - active: dev + active: prod data: redis: From c0c1ffd698cff43b11da051b2056307483991f69 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Tue, 5 Nov 2024 21:57:05 +0900 Subject: [PATCH 02/13] Delete: Test Code --- .../billage/BillageApplicationTests.java | 13 -- .../com/groomiz/billage/RedisCrudTest.java | 84 ------- .../billage/RefreshTokenRedisTest.java | 87 -------- .../billage/auth/AuthControllerTest.java | 96 -------- .../billage/auth/UserControllerTest.java | 113 ---------- .../building/BuildingRepositoryTest.java | 81 ------- .../classroom/ClassroomRepositoryTest.java | 91 -------- .../billage/member/entity/MajorTest.java | 27 --- .../repository/ReservationRepositoryTest.java | 147 ------------- .../service/AdminReservationServiceTest.java | 175 --------------- .../service/ReservationServiceTest.java | 205 ------------------ 11 files changed, 1119 deletions(-) delete mode 100644 src/test/java/com/groomiz/billage/BillageApplicationTests.java delete mode 100644 src/test/java/com/groomiz/billage/RedisCrudTest.java delete mode 100644 src/test/java/com/groomiz/billage/RefreshTokenRedisTest.java delete mode 100644 src/test/java/com/groomiz/billage/auth/AuthControllerTest.java delete mode 100644 src/test/java/com/groomiz/billage/auth/UserControllerTest.java delete mode 100644 src/test/java/com/groomiz/billage/building/BuildingRepositoryTest.java delete mode 100644 src/test/java/com/groomiz/billage/classroom/ClassroomRepositoryTest.java delete mode 100644 src/test/java/com/groomiz/billage/member/entity/MajorTest.java delete mode 100644 src/test/java/com/groomiz/billage/reservation/repository/ReservationRepositoryTest.java delete mode 100644 src/test/java/com/groomiz/billage/reservation/service/AdminReservationServiceTest.java delete mode 100644 src/test/java/com/groomiz/billage/reservation/service/ReservationServiceTest.java diff --git a/src/test/java/com/groomiz/billage/BillageApplicationTests.java b/src/test/java/com/groomiz/billage/BillageApplicationTests.java deleted file mode 100644 index 902428c0..00000000 --- a/src/test/java/com/groomiz/billage/BillageApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.groomiz.billage; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class BillageApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/src/test/java/com/groomiz/billage/RedisCrudTest.java b/src/test/java/com/groomiz/billage/RedisCrudTest.java deleted file mode 100644 index 6e1fc5a8..00000000 --- a/src/test/java/com/groomiz/billage/RedisCrudTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.groomiz.billage; - -import static org.assertj.core.api.AssertionsForClassTypes.*; -import static org.awaitility.Awaitility.*; - -import java.time.Duration; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import com.groomiz.billage.auth.service.RedisService; - -@SpringBootTest -class RedisCrudTest { - final String KEY = "key"; - final String VALUE = "value"; - final Duration DURATION = Duration.ofMillis(5000); - @Autowired - private RedisService redisService; - - @BeforeEach - void shutDown() { - redisService.setValues(KEY, VALUE, DURATION); - } - - @AfterEach - void tearDown() { - redisService.deleteValues(KEY); - } - - @Test - @DisplayName("Redis에 데이터를 저장하면 정상적으로 조회된다.") - void saveAndFindTest() throws Exception { - // when - String findValue = redisService.getValues(KEY); - - // then - assertThat(VALUE).isEqualTo(findValue); - } - - @Test - @DisplayName("Redis에 저장된 데이터를 수정할 수 있다.") - void updateTest() throws Exception { - // given - String updateValue = "updateValue"; - redisService.setValues(KEY, updateValue, DURATION); - - // when - String findValue = redisService.getValues(KEY); - - // then - assertThat(updateValue).isEqualTo(findValue); - assertThat(VALUE).isNotEqualTo(findValue); - } - - @Test - @DisplayName("Redis에 저장된 데이터를 삭제할 수 있다.") - void deleteTest() throws Exception { - // when - redisService.deleteValues(KEY); - String findValue = redisService.getValues(KEY); - - // then - assertThat(findValue).isEqualTo("false"); - } - - @Test - @DisplayName("Redis에 저장된 데이터는 만료시간이 지나면 삭제된다.") - void expiredTest() throws Exception { - // when - String findValue = redisService.getValues(KEY); - await().pollDelay(Duration.ofMillis(6000)).untilAsserted( - () -> { - String expiredValue = redisService.getValues(KEY); - assertThat(expiredValue).isNotEqualTo(findValue); - assertThat(expiredValue).isEqualTo("false"); - } - ); - } -} \ No newline at end of file diff --git a/src/test/java/com/groomiz/billage/RefreshTokenRedisTest.java b/src/test/java/com/groomiz/billage/RefreshTokenRedisTest.java deleted file mode 100644 index fcc42678..00000000 --- a/src/test/java/com/groomiz/billage/RefreshTokenRedisTest.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.groomiz.billage; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import java.time.Duration; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.test.web.servlet.MockMvc; - -import com.groomiz.billage.auth.jwt.JwtUtil; - -@SpringBootTest -@AutoConfigureMockMvc -public class RefreshTokenRedisTest { - - @Autowired - private MockMvc mockMvc; - - @Autowired - private RedisTemplate redisTemplate; - - @Autowired - private JwtUtil jwtUtil; - - private static final String REFRESH_TOKEN_KEY = "test"; - - @BeforeEach - public void setUp() { - // Redis 클린업 (기존 데이터를 삭제) - redisTemplate.delete(REFRESH_TOKEN_KEY); - } - - @Test - @DisplayName("RefreshToken Redis에 저장 및 검증") - public void testSaveAndRetrieveRefreshToken() throws Exception { - // 1. RefreshToken 생성 - String refreshToken = jwtUtil.createJwt("RefreshToken", "test", "ADMIN", 100000L); - - // 2. Redis에 저장 - redisTemplate.opsForValue().set("refreshToken:testUser", refreshToken, Duration.ofDays(1)); - - // 3. Redis에서 저장된 RefreshToken을 가져옴 - String storedRefreshToken = redisTemplate.opsForValue().get("refreshToken:testUser"); - assertThat(storedRefreshToken).isEqualTo(refreshToken); - } - - @Test - @DisplayName("AccessToken 만료 시 RefreshToken을 통한 재발급") - public void testAccessTokenExpirationAndRefresh() throws Exception { - // 1. RefreshToken 생성 - String refreshToken = jwtUtil.createJwt("RefreshToken", "test", "ADMIN", 86400000L); // 1일 만료 - - // 2. Redis에 RefreshToken 저장 - redisTemplate.opsForValue().set("test", refreshToken, Duration.ofDays(1)); - - // 3. AccessToken 생성 (만료 시간 1초) - String expiredAccessToken = jwtUtil.createJwt("AccessToken", "test", "ADMIN", 1000L); - - // 4. AccessToken 만료를 위해 2초 대기 - Thread.sleep(2000); - - // 5. 만료된 AccessToken을 사용하여 요청, 401 Unauthorized 기대 - mockMvc.perform(get("/api/admin") - .header("Authorization", "Bearer " + expiredAccessToken)) - .andExpect(status().isUnauthorized()); - - // 6. Redis에서 저장된 RefreshToken 값 확인 - String storedToken = redisTemplate.opsForValue().get("test"); - assertThat(storedToken).isEqualTo(refreshToken); - - // 7. RefreshToken을 사용해 새로운 AccessToken 요청 - mockMvc.perform(post("/api/auth/refresh-token") - .header("RefreshToken", "Bearer " + refreshToken)) - .andExpect(status().isOk()) - .andExpect(header().exists("Authorization")) - .andExpect(header().exists("RefreshToken")); - } -} - diff --git a/src/test/java/com/groomiz/billage/auth/AuthControllerTest.java b/src/test/java/com/groomiz/billage/auth/AuthControllerTest.java deleted file mode 100644 index efc4ed69..00000000 --- a/src/test/java/com/groomiz/billage/auth/AuthControllerTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.groomiz.billage.auth; - -import static org.junit.jupiter.api.Assertions.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.groomiz.billage.auth.dto.LoginRequest; -import com.groomiz.billage.auth.dto.RegisterRequest; -import com.groomiz.billage.auth.exception.AuthException; -import com.groomiz.billage.auth.jwt.JwtUtil; -import com.groomiz.billage.member.service.MemberService; - -@SpringBootTest -@AutoConfigureMockMvc -public class AuthControllerTest { - - @Autowired - private MockMvc mockMvc; - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private MemberService memberService; - - @Autowired - private JwtUtil jwtUtil; - - @BeforeEach - public void setUp() { - RegisterRequest registerRequest = new RegisterRequest("홍길동", "20100000", "password1234!", "010-1234-5678", - "정보통신대학", "컴퓨터공학과", true, "asdf1234@gmail.com"); - - if (!memberService.isExists(registerRequest.getStudentNumber())) { - memberService.register(registerRequest); - } - - } - - @Test - @DisplayName("로그인 성공하였습니다.") - public void testLoginSuccess() throws Exception { - LoginRequest loginRequest = new LoginRequest("20100000", "password1234!"); - - mockMvc.perform(post("/api/v1/users/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(loginRequest))) - .andExpect(status().isOk()) - .andExpect(header().exists("Authorization")) - .andExpect(header().exists("RefreshToken")); - } - - @Test - @DisplayName("토큰이 만료 되었습니다.") - public void testExpiredToken() throws Exception { - - // 만료된 Access Token을 사용한 요청 테스트 - String expiredToken = jwtUtil.createJwt("AccessToken", "20100000", "ADMIN", 1L); // 1ms 유효한 토큰 - - // 짧은 시간 동안 토큰 만료를 기다림 - Thread.sleep(1000); - - // AuthException이 발생하는지 확인 - assertThrows(AuthException.class, () -> { - mockMvc.perform(get("/api/admin") - .header("Authorization", "Bearer " + expiredToken)) - .andExpect(status().isUnauthorized()); // 만료된 토큰은 401 상태를 기대 - }); - } - @Test - @DisplayName("유효하지 않은 토큰입니다.") - public void testInvalidToken() throws Exception { - - // 유효하지 않은 Access Token을 생성 (JWT 형식이 올바르지 않거나 변조된 토큰) - String invalidToken = "invalid.token.string"; // 형식이 잘못된 임의의 토큰 - - // AuthException이 발생하는지 확인 - assertThrows(AuthException.class, () -> { - mockMvc.perform(get("/api/admin") - .header("Authorization", "Bearer " + invalidToken)) - .andExpect(status().isUnauthorized()); // 유효하지 않은 토큰은 401 상태를 기대 - }); - } - - -} diff --git a/src/test/java/com/groomiz/billage/auth/UserControllerTest.java b/src/test/java/com/groomiz/billage/auth/UserControllerTest.java deleted file mode 100644 index 81136d73..00000000 --- a/src/test/java/com/groomiz/billage/auth/UserControllerTest.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.groomiz.billage.auth; - -import static org.junit.jupiter.api.Assertions.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.groomiz.billage.auth.dto.LoginRequest; -import com.groomiz.billage.auth.dto.RegisterRequest; -import com.groomiz.billage.auth.exception.AuthException; -import com.groomiz.billage.auth.jwt.JwtUtil; -import com.groomiz.billage.member.exception.MemberException; -import com.groomiz.billage.member.service.MemberService; - -@SpringBootTest -@AutoConfigureMockMvc -public class UserControllerTest { - - @Autowired - private MockMvc mockMvc; - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private MemberService memberService; - - @Autowired - private JwtUtil jwtUtil; - - @BeforeEach - public void setUp() { - // 회원가입을 위한 테스트 데이터를 미리 설정 - RegisterRequest registerRequest = new RegisterRequest("홍길동", "20100000", "password1234!", "010-1234-5678", - "정보통신대학", "컴퓨터공학과", true, "asdf1234@gmail.com"); - - if (!memberService.isExists(registerRequest.getStudentNumber())) { - memberService.register(registerRequest); - } - } - - @Test - @DisplayName("로그인 성공") - public void testLoginSuccess() throws Exception { - // 로그인 요청 생성 - LoginRequest loginRequest = new LoginRequest("20100000", "password1234!"); - - mockMvc.perform(post("/api/v1/users/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(loginRequest))) - .andExpect(status().isOk()) - .andExpect(header().exists("Authorization")) - .andExpect(header().exists("RefreshToken")); - } - - @Test - @DisplayName("로그인 실패 - 잘못된 비밀번호") - public void testLoginFailure() throws Exception { - // 잘못된 비밀번호로 로그인 요청 - LoginRequest loginRequest = new LoginRequest("20100000", "wrongpassword!"); - - // AuthException 발생하는지 확인 - mockMvc.perform(post("/api/v1/users/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(loginRequest))) - .andExpect(status().isBadRequest()); - - } - - @Test - @DisplayName("로그인 실패 - 잘못된 아이디") - public void testLoginFailureWithInvalidUserId() throws Exception { - // 존재하지 않는 아이디로 로그인 요청 - LoginRequest loginRequest = new LoginRequest("99999999", "password1234!"); - - // 아이디가 틀렸을 때 AuthException 발생하는지 확인 - mockMvc.perform(post("/api/v1/users/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(loginRequest))) - .andExpect(status().isBadRequest()); // 혹은 UNAUTHORIZED로 설정 - } - - @Test - @DisplayName("회원가입 성공") - public void testRegisterSuccess() throws Exception { - RegisterRequest registerRequest = new RegisterRequest("이순신", "20190000", "newpassword!", "010-5678-1234", - "정보통신대학", "전자공학과", true, "lee1234@gmail.com"); - - mockMvc.perform(post("/api/v1/users/register") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(registerRequest))) - .andExpect(status().isOk()) - .andExpect(content().string("회원가입이 완료되었습니다.")); - } - - @Test - @DisplayName("회원 로그아웃 성공") - public void testLogoutSuccess() throws Exception { - mockMvc.perform(post("/api/v1/users/logout")) - .andExpect(status().isOk()) - .andExpect(content().string("로그아웃 성공하였습니다.")); - } - -} diff --git a/src/test/java/com/groomiz/billage/building/BuildingRepositoryTest.java b/src/test/java/com/groomiz/billage/building/BuildingRepositoryTest.java deleted file mode 100644 index bc94afe1..00000000 --- a/src/test/java/com/groomiz/billage/building/BuildingRepositoryTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.groomiz.billage.building; - -import static org.assertj.core.api.Assertions.*; - -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import com.groomiz.billage.building.entity.Building; -import com.groomiz.billage.building.repository.BuildingRepository; -import com.groomiz.billage.classroom.entity.Classroom; -import com.groomiz.billage.classroom.repository.ClassroomRepository; - -@SpringBootTest -class BuildingRepositoryTest { - - @Autowired - private BuildingRepository buildingRepository; - - @Autowired - private ClassroomRepository classroomRepository; - - private Building building1; - private Building building2; - - @BeforeEach - void setUp() { - building1 = Building.builder() - .name("창학관") - .number("3") - .startFloor(1L) - .endFloor(5L) - .imageUrl("image_url_1") - .build(); - - building2 = Building.builder() - .name("미래관") - .number("60") - .startFloor(1L) - .endFloor(7L) - .imageUrl("image_url_2") - .build(); - - buildingRepository.save(building1); - buildingRepository.save(building2); - - Classroom classroom1 = Classroom.builder() - .name("전자공학과 강의실") - .number("201") - .floor(2L) - .description("전자공학과 강의실") - .capacity(40) - .building(building1) - .build(); - - Classroom classroom2 = Classroom.builder() - .name("컴퓨터공학과 실습실") - .number("301") - .floor(3L) - .description("컴퓨터공학과 실습실") - .capacity(60) - .building(building2) - .build(); - - classroomRepository.save(classroom1); - classroomRepository.save(classroom2); - } - - @Test - @DisplayName("BuildingRepository: 수용 가능한 건물 조회") - void findBuildingsByCapacity() { - // 인원 50명 기준으로 조회 (창학관은 제외되고 미래관만 조회되어야 함) - List buildings = buildingRepository.findBuildingsByCapacity(50).orElseThrow(); - assertThat(buildings).hasSize(1); - assertThat(buildings.get(0).getName()).isEqualTo("미래관"); - } -} \ No newline at end of file diff --git a/src/test/java/com/groomiz/billage/classroom/ClassroomRepositoryTest.java b/src/test/java/com/groomiz/billage/classroom/ClassroomRepositoryTest.java deleted file mode 100644 index 11895c6d..00000000 --- a/src/test/java/com/groomiz/billage/classroom/ClassroomRepositoryTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.groomiz.billage.classroom; - -import com.groomiz.billage.building.entity.Building; -import com.groomiz.billage.building.repository.BuildingRepository; -import com.groomiz.billage.classroom.entity.Classroom; -import com.groomiz.billage.classroom.repository.ClassroomRepository; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.List; - -@SpringBootTest -class ClassroomRepositoryTest { - - @Autowired - private BuildingRepository buildingRepository; - - @Autowired - private ClassroomRepository classroomRepository; - - private Building building1; - private Building building2; - - @BeforeEach - void setUp() { - building1 = Building.builder() - .name("창학관") - .number("3") - .startFloor(1L) - .endFloor(5L) - .imageUrl("image_url_1") - .build(); - - building2 = Building.builder() - .name("미래관") - .number("60") - .startFloor(1L) - .endFloor(7L) - .imageUrl("image_url_2") - .build(); - - buildingRepository.save(building1); - buildingRepository.save(building2); - - Classroom classroom1 = Classroom.builder() - .name("전자공학과 강의실") - .number("101") - .floor(2L) - .description("전자공학과 강의실") - .capacity(40) - .building(building1) - .build(); - - Classroom classroom2 = Classroom.builder() - .name("컴퓨터공학과 실습실") - .number("301") - .floor(3L) - .description("컴퓨터공학과 실습실") - .capacity(60) - .building(building2) - .build(); - - Classroom classroom3 = Classroom.builder() - .name("컴퓨터공학과 강의실") - .number("302") - .floor(3L) - .description("컴퓨터공학과 강의실") - .capacity(31) - .building(building2) - .build(); - - classroomRepository.save(classroom1); - classroomRepository.save(classroom2); - classroomRepository.save(classroom3); - } - - @Test - @DisplayName("ClassroomRepository: 강의실 수용인원 이상 강의실 조회") - void findClassroomByCapacity() { - List classrooms = classroomRepository.findByBuildingIdAndFloorAndCapacityGreaterThanEqual(building2.getId(), 3L, 30).orElseThrow(); - assertThat(classrooms).hasSize(2); - assertThat(classrooms.get(0).getName()).isEqualTo("컴퓨터공학과 실습실"); - assertThat(classrooms.get(1).getName()).isEqualTo("컴퓨터공학과 강의실"); - } -} diff --git a/src/test/java/com/groomiz/billage/member/entity/MajorTest.java b/src/test/java/com/groomiz/billage/member/entity/MajorTest.java deleted file mode 100644 index 4bcfabfc..00000000 --- a/src/test/java/com/groomiz/billage/member/entity/MajorTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.groomiz.billage.member.entity; - -import static org.assertj.core.api.AssertionsForInterfaceTypes.*; - -import java.util.List; - -import org.junit.jupiter.api.Test; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -class MajorTest { - - @Test - public void 단과대에_소속된_학과_조회_성공() throws Exception { - //given - College college = College.ENGINEER; - - //when - List majors = Major.getMajorsByCollege(college); - log.info("majors: {}", majors); - - //then - assertThat(majors).contains(Major.MSD); - assertThat(majors).isNotEmpty(); - } -} \ No newline at end of file diff --git a/src/test/java/com/groomiz/billage/reservation/repository/ReservationRepositoryTest.java b/src/test/java/com/groomiz/billage/reservation/repository/ReservationRepositoryTest.java deleted file mode 100644 index 1e16da2a..00000000 --- a/src/test/java/com/groomiz/billage/reservation/repository/ReservationRepositoryTest.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.groomiz.billage.reservation.repository; - -import static org.assertj.core.api.Assertions.*; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.annotation.Rollback; -import org.springframework.transaction.annotation.Transactional; - -import com.groomiz.billage.building.entity.Building; -import com.groomiz.billage.building.repository.BuildingRepository; -import com.groomiz.billage.classroom.dto.ReservationTime; -import com.groomiz.billage.classroom.entity.Classroom; -import com.groomiz.billage.classroom.repository.ClassroomRepository; -import com.groomiz.billage.member.entity.College; -import com.groomiz.billage.member.entity.Major; -import com.groomiz.billage.member.entity.Member; -import com.groomiz.billage.member.entity.Role; -import com.groomiz.billage.member.repository.MemberRepository; -import com.groomiz.billage.reservation.dto.request.ClassroomReservationRequest; -import com.groomiz.billage.reservation.entity.Reservation; -import com.groomiz.billage.reservation.entity.ReservationPurpose; -import com.groomiz.billage.reservation.entity.ReservationStatusType; -import com.groomiz.billage.reservation.exception.ReservationErrorCode; -import com.groomiz.billage.reservation.exception.ReservationException; -import com.groomiz.billage.reservation.service.ReservationService; - -@SpringBootTest -@Transactional -class ReservationRepositoryTest { - - @Autowired - private MemberRepository memberRepository; - - @Autowired - private BuildingRepository buildingRepository; - - @Autowired - private ClassroomRepository classroomRepository; - - @Autowired - private ReservationService reservationService; - @Autowired - private ReservationRepository reservationRepository; - - @Test - public void 해당하는_날짜에_승인_미승인_예약_시작시간_종료시간_조회_성공() throws Exception{ - //given - Member student = saveStudent(); - Building building = saveBuilding("미래관"); - Building building2 = saveBuilding("상상관"); - Classroom classroom = saveClassroom(building); - Classroom classroom2 = saveClassroom(building2); - - LocalDate applyDate = LocalDate.now(); - LocalTime startTime = LocalTime.of(13, 0); - LocalTime endTime = LocalTime.of(14, 0); - LocalTime startTime2 = LocalTime.of(14, 0); - LocalTime endTime2 = LocalTime.of(15, 0); - LocalTime startTime3 = LocalTime.of(15, 0); - LocalTime endTime3 = LocalTime.of(16, 0); - LocalTime startTime4 = LocalTime.of(13, 0); - LocalTime endTime4 = LocalTime.of(16, 0); - - Long reservationId = reserveClassroom(classroom, student, applyDate, startTime, endTime); - Long reservationId2 = reserveClassroom(classroom, student, applyDate, startTime2, endTime2); - Long reservationId3 = reserveClassroom(classroom, student, applyDate, startTime3, endTime3); - Long reservationId4 = reserveClassroom(classroom2, student, applyDate, startTime4, endTime4); - - Reservation reservation = reservationRepository.findById(reservationId) - .orElseThrow(() -> new ReservationException(ReservationErrorCode.RESERVATION_NOT_FOUND)); - Reservation reservation2 = reservationRepository.findById(reservationId2) - .orElseThrow(() -> new ReservationException(ReservationErrorCode.RESERVATION_NOT_FOUND)); - reservation.getReservationStatus().updateStatus(ReservationStatusType.APPROVED); - reservation2.getReservationStatus().updateStatus(ReservationStatusType.REJECTED); - - //when - List result = reservationRepository.findPendingOrApprovedReservationsByDateAndClassroom( - applyDate, classroom.getId()); - - //then - Assertions.assertAll( - () -> { - assertThat(result.size()).isEqualTo(2); - } - ); - } - - private Long reserveClassroom(Classroom classroom, Member student, LocalDate applyDate, LocalTime startTime, LocalTime endTime) { - String phoneNumber = "010-1234-5678"; - - ClassroomReservationRequest request = ClassroomReservationRequest.builder() - .classroomId(classroom.getId()) - .phoneNumber(phoneNumber) - .applyDate(applyDate) - .headcount(30) - .startTime(startTime) - .endTime(endTime) - .purpose(ReservationPurpose.CLUB_EVENT) - .build(); - - return reservationService.reserveClassroom(request, student.getStudentNumber()); - } - - private Member saveStudent() { - Member member = Member.builder() - .username("홍길동") - .password("password1234!") - .phoneNumber("010-1234-5678") - .role(Role.STUDENT) - .isAdmin(false) - .isValid(true) - .agreedToTerms(true) - .studentNumber("21000000") - .college(College.ICE) - .major(Major.CS) - .build(); - - return memberRepository.save(member); - } - - private Building saveBuilding(String name) { - Building building = Building.builder() - .name(name) - .number("60") - .build(); - - return buildingRepository.save(building); - } - - private Classroom saveClassroom(Building building) { - Classroom classroom = Classroom.builder() - .number("101") - .floor(1L) - .capacity(30) - .building(building) - .build(); - - return classroomRepository.save(classroom); - } -} \ No newline at end of file diff --git a/src/test/java/com/groomiz/billage/reservation/service/AdminReservationServiceTest.java b/src/test/java/com/groomiz/billage/reservation/service/AdminReservationServiceTest.java deleted file mode 100644 index 2de84e4b..00000000 --- a/src/test/java/com/groomiz/billage/reservation/service/AdminReservationServiceTest.java +++ /dev/null @@ -1,175 +0,0 @@ -package com.groomiz.billage.reservation.service; - -import static org.assertj.core.api.Assertions.*; - -import java.time.LocalDate; -import java.time.LocalTime; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.annotation.Rollback; -import org.springframework.transaction.annotation.Transactional; - -import com.groomiz.billage.auth.dto.RegisterRequest; -import com.groomiz.billage.building.entity.Building; -import com.groomiz.billage.building.entity.BuildingAdmin; -import com.groomiz.billage.building.repository.BuildingAdminRepository; -import com.groomiz.billage.building.repository.BuildingRepository; -import com.groomiz.billage.classroom.entity.Classroom; -import com.groomiz.billage.classroom.repository.ClassroomRepository; -import com.groomiz.billage.member.entity.Member; -import com.groomiz.billage.member.entity.Role; -import com.groomiz.billage.member.repository.MemberRepository; -import com.groomiz.billage.member.service.MemberService; -import com.groomiz.billage.reservation.dto.request.ClassroomReservationRequest; -import com.groomiz.billage.reservation.dto.response.AdminReservationStatusListResponse; -import com.groomiz.billage.reservation.entity.ReservationPurpose; -import com.groomiz.billage.reservation.entity.ReservationStatusType; -import com.groomiz.billage.reservation.repository.ReservationRepository; - -import lombok.extern.slf4j.Slf4j; - -@SpringBootTest -@Transactional -@Slf4j -class AdminReservationServiceTest { - - @Autowired - AdminReservationService adminReservationService; - - @Autowired - MemberRepository memberRepository; - @Autowired - MemberService memberService; - - @Autowired - BuildingRepository buildingRepository; - - @Autowired - ClassroomRepository classroomRepository; - - @Autowired - ReservationService reservationService; - @Autowired - ReservationRepository reservationRepository; - @Autowired - private BuildingAdminRepository buildingAdminRepository; - - @BeforeEach - public void setUp() { - register("admin", Role.ADMIN, "1"); - register("student", Role.STUDENT, "2"); - - Member admin = memberRepository.findByStudentNumber("1").get(); - Member student = memberRepository.findByStudentNumber("2").get(); - - - Building building = saveBuilding("미래관"); - Building building2 = saveBuilding("상상관"); - Classroom classroom = saveClassroom(building); - Classroom classroom2 = saveClassroom(building2); - - LocalDate applyDate = LocalDate.now(); - LocalDate applyDate2 = LocalDate.now().plusDays(3); - LocalTime startTime = LocalTime.of(13, 0); - LocalTime endTime = LocalTime.of(14, 0); - LocalTime startTime2 = LocalTime.of(15, 0); - LocalTime endTime2 = LocalTime.of(16, 0); - LocalTime startTime3 = LocalTime.of(17, 0); - LocalTime endTime3 = LocalTime.of(20, 0); - LocalTime startTime4 = LocalTime.of(13, 0); - LocalTime endTime4 = LocalTime.of(16, 0); - - Long rid = reserveClassroom(classroom, student, applyDate, startTime, endTime); - Long rid2 = reserveClassroom(classroom, student, applyDate, startTime2, endTime2); - Long rid3 = reserveClassroom(classroom, student, applyDate2, startTime3, endTime3); - Long rid4 = reserveClassroom(classroom2, admin, applyDate, startTime4, endTime4); - Long rid5 = reserveClassroom(classroom2, admin, applyDate2, startTime2, endTime2); - Long rid6 = reserveClassroom(classroom2, student, applyDate2, startTime3, endTime3); - - // 대기 2, 승인 1, 거절 1, 관리자 취소 1, 학생 취소 1 - reservationRepository.findById(rid).get().getReservationStatus().approve(admin); - reservationRepository.findById(rid2).get().getReservationStatus().reject(admin, "null"); - reservationRepository.findById(rid5).get().getReservationStatus().cancelByAdmin(admin); - reservationRepository.findById(rid6).get().getReservationStatus().approve(admin); - reservationRepository.findById(rid6).get().getReservationStatus().cancelByStudent(); - - BuildingAdmin buildingAdmin = new BuildingAdmin(admin, building); - BuildingAdmin buildingAdmin2 = new BuildingAdmin(admin, building2); - buildingAdminRepository.save(buildingAdmin); - buildingAdminRepository.save(buildingAdmin2); - } - - @Test - @Rollback(value = false) - public void 관리자_대기_예약_조회_성공() throws Exception{ - //given - String adminStudentNumber = "1"; - - //when - AdminReservationStatusListResponse pending = adminReservationService.getReservationByStatus( - ReservationStatusType.PENDING, adminStudentNumber); - AdminReservationStatusListResponse approved = adminReservationService.getReservationByStatus( - ReservationStatusType.APPROVED, adminStudentNumber); - AdminReservationStatusListResponse rejectedAndCanceled = adminReservationService.getReservationByStatus( - ReservationStatusType.REJECTED, adminStudentNumber); - - //then - Assertions.assertAll( - () -> { - assertThat(pending.getReservations().size()).isEqualTo(2); - assertThat(approved.getReservations().size()).isEqualTo(1); - assertThat(rejectedAndCanceled.getReservations().size()).isEqualTo(3); - } - ); - } - - private Long reserveClassroom(Classroom classroom, Member student, LocalDate applyDate, LocalTime startTime, LocalTime endTime) { - String phoneNumber = "010-1234-5678"; - - ClassroomReservationRequest request = ClassroomReservationRequest.builder() - .classroomId(classroom.getId()) - .phoneNumber(phoneNumber) - .applyDate(applyDate) - .headcount(30) - .startTime(startTime) - .endTime(endTime) - .purpose(ReservationPurpose.CLUB_EVENT) - .build(); - - return reservationService.reserveClassroom(request, student.getStudentNumber()); - } - - private Member register(String username, Role role, String studentNumber) { - RegisterRequest registerRequest = new RegisterRequest(username, studentNumber, "password1234!", "010-1234-5678", - "정보통신대학", "컴퓨터공학과", true, "asdf1234@gmail.com"); - - memberService.register(registerRequest); - return memberRepository.findByStudentNumber(studentNumber).get(); - } - - private Building saveBuilding(String name) { - Building building = Building.builder() - .name(name) - .startFloor(1L) - .endFloor(3L) - .number("60") - .build(); - - return buildingRepository.save(building); - } - - private Classroom saveClassroom(Building building) { - Classroom classroom = Classroom.builder() - .number("101") - .floor(1L) - .capacity(30) - .building(building) - .build(); - - return classroomRepository.save(classroom); - } -} \ No newline at end of file diff --git a/src/test/java/com/groomiz/billage/reservation/service/ReservationServiceTest.java b/src/test/java/com/groomiz/billage/reservation/service/ReservationServiceTest.java deleted file mode 100644 index 2780c7a7..00000000 --- a/src/test/java/com/groomiz/billage/reservation/service/ReservationServiceTest.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.groomiz.billage.reservation.service; - -import static org.assertj.core.api.Assertions.*; - -import java.time.LocalDate; -import java.time.LocalTime; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.transaction.annotation.Transactional; - -import com.groomiz.billage.building.entity.Building; -import com.groomiz.billage.building.repository.BuildingRepository; -import com.groomiz.billage.classroom.entity.Classroom; -import com.groomiz.billage.classroom.repository.ClassroomRepository; -import com.groomiz.billage.member.entity.College; -import com.groomiz.billage.member.entity.Major; -import com.groomiz.billage.member.entity.Member; -import com.groomiz.billage.member.entity.Role; -import com.groomiz.billage.member.repository.MemberRepository; -import com.groomiz.billage.reservation.dto.request.ClassroomReservationRequest; -import com.groomiz.billage.reservation.entity.Reservation; -import com.groomiz.billage.reservation.entity.ReservationPurpose; -import com.groomiz.billage.reservation.entity.ReservationStatus; -import com.groomiz.billage.reservation.entity.ReservationStatusType; -import com.groomiz.billage.reservation.exception.ReservationErrorCode; -import com.groomiz.billage.reservation.exception.ReservationException; -import com.groomiz.billage.reservation.repository.ReservationRepository; - -@SpringBootTest -@Transactional -class ReservationServiceTest { - - @Autowired - private MemberRepository memberRepository; - - @Autowired - private BuildingRepository buildingRepository; - - @Autowired - private ClassroomRepository classroomRepository; - - @Autowired - private ReservationService reservationService; - @Autowired - private ReservationRepository reservationRepository; - - @Test - public void 학생_강의실_예약_성공() throws Exception{ - //given - Member student = saveStudent(); - Building building = saveBuilding("미래관"); - Classroom classroom = saveClassroom(building); - - - LocalDate applyDate = LocalDate.now(); - LocalTime startTime = LocalTime.of(13, 0); - LocalTime endTime = LocalTime.of(14, 0); - - Long reservationId = reserveClassroom(classroom, student, applyDate, startTime, endTime); - - Reservation reservation = reservationRepository.findById(reservationId) - .orElseThrow(() -> new ReservationException(ReservationErrorCode.RESERVATION_NOT_FOUND)); - - //then - Assertions.assertAll( - () -> { - assert reservation != null; - ReservationStatus reservationStatus = reservation.getReservationStatus(); - assert reservationStatus != null; - assertThat(reservationStatus.getRequester()).isEqualTo(student); - } - ); - } - - @Test - public void 강의실_시간_중복_예약_시_예외() throws Exception{ - //given - Member student = saveStudent(); - Building building = saveBuilding("미래관"); - Building building2 = saveBuilding("상상관"); - Classroom classroom = saveClassroom(building); - Classroom classroom2 = saveClassroom(building2); - - LocalDate applyDate = LocalDate.now(); - LocalTime startTime = LocalTime.of(13, 0); - LocalTime endTime = LocalTime.of(14, 0); - LocalTime startTime2 = LocalTime.of(15, 0); - LocalTime endTime2 = LocalTime.of(16, 0); - LocalTime startTime3 = LocalTime.of(14, 0); - LocalTime endTime3 = LocalTime.of(20, 0); - LocalTime startTime4 = LocalTime.of(13, 0); - LocalTime endTime4 = LocalTime.of(16, 0); - - reserveClassroom(classroom, student, applyDate, startTime, endTime); - reserveClassroom(classroom, student, applyDate, startTime2, endTime2); - reserveClassroom(classroom2, student, applyDate, startTime4, endTime4); - - //when & then - assertThatThrownBy(() -> reserveClassroom(classroom, student, applyDate, startTime3, endTime3)) - .isInstanceOf(ReservationException.class); - } - - @Test - public void 학생_예약_취소_성공() throws Exception{ - //given - Member student = saveStudent(); - Building building = saveBuilding("미래관"); - Classroom classroom = saveClassroom(building); - - LocalDate applyDate = LocalDate.now(); - LocalTime startTime = LocalTime.of(13, 0); - LocalTime endTime = LocalTime.of(14, 0); - - Long reservationId = reserveClassroom(classroom, student, applyDate, startTime, endTime); - - //when - reservationService.cancelReservation(reservationId, student.getStudentNumber()); - - //then - Reservation reservation = reservationRepository.findById(reservationId) - .orElseThrow(() -> new ReservationException(ReservationErrorCode.RESERVATION_NOT_FOUND)); - - assertThat(reservation.getReservationStatus().isCanceledByStudent()).isTrue(); - } - - - @Test - public void 학생_예약_실패_이미_거절() throws Exception{ - //given - Member student = saveStudent(); - Building building = saveBuilding("미래관"); - Classroom classroom = saveClassroom(building); - - LocalDate applyDate = LocalDate.now(); - LocalTime startTime = LocalTime.of(13, 0); - LocalTime endTime = LocalTime.of(14, 0); - - Long reservationId = reserveClassroom(classroom, student, applyDate, startTime, endTime); - - //when - Reservation reservation = reservationRepository.findById(reservationId) - .orElseThrow(() -> new ReservationException(ReservationErrorCode.RESERVATION_NOT_FOUND)); - reservation.getReservationStatus().updateStatus(ReservationStatusType.REJECTED); - - //then - assertThatThrownBy(() -> reservationService.cancelReservation(reservationId, student.getStudentNumber())) - .isInstanceOf(ReservationException.class); - } - - private Long reserveClassroom(Classroom classroom, Member student, LocalDate applyDate, LocalTime startTime, LocalTime endTime) { - String phoneNumber = "010-1234-5678"; - - ClassroomReservationRequest request = ClassroomReservationRequest.builder() - .classroomId(classroom.getId()) - .phoneNumber(phoneNumber) - .applyDate(applyDate) - .headcount(30) - .startTime(startTime) - .endTime(endTime) - .purpose(ReservationPurpose.CLUB_EVENT) - .build(); - - return reservationService.reserveClassroom(request, student.getStudentNumber()); - } - - private Member saveStudent() { - Member member = Member.builder() - .username("홍길동") - .password("password1234!") - .phoneNumber("010-1234-5678") - .role(Role.STUDENT) - .isAdmin(false) - .isValid(true) - .agreedToTerms(true) - .studentNumber("21000000") - .college(College.ICE) - .major(Major.CS) - .build(); - - return memberRepository.save(member); - } - - private Building saveBuilding(String name) { - Building building = Building.builder() - .name(name) - .number("60") - .build(); - - return buildingRepository.save(building); - } - - private Classroom saveClassroom(Building building) { - Classroom classroom = Classroom.builder() - .number("101") - .floor(1L) - .capacity(30) - .building(building) - .build(); - - return classroomRepository.save(classroom); - } -} \ No newline at end of file From 362d83c562443a18ffe74f70089c697b87a60d20 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Tue, 5 Nov 2024 22:40:01 +0900 Subject: [PATCH 03/13] Feat: CI/CD --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 80e1036a..de501b23 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -10,7 +10,7 @@ permissions: contents: read on: push: - branches: [ master ] + branches: [ master,SCRUM-27--Feature/aws-deploy ] jobs: build: runs-on: ubuntu-latest # 3 실행 환경 지정 From 864058fd427ccdcbe1db9e8e7dfb99d75857f3ab Mon Sep 17 00:00:00 2001 From: hawardShin Date: Tue, 5 Nov 2024 22:44:53 +0900 Subject: [PATCH 04/13] Feat: Swagger server add --- .../java/com/groomiz/billage/global/config/SwaggerConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/groomiz/billage/global/config/SwaggerConfig.java b/src/main/java/com/groomiz/billage/global/config/SwaggerConfig.java index 451e0015..f2b75b6d 100644 --- a/src/main/java/com/groomiz/billage/global/config/SwaggerConfig.java +++ b/src/main/java/com/groomiz/billage/global/config/SwaggerConfig.java @@ -65,6 +65,7 @@ public OpenAPI openAPI() { return new OpenAPI() .addServersItem(new Server().url("http://localhost:8080")) + .addServersItem(new Server().url("https://billage.duckdns.org")) .components(new Components()) .info(info) .addSecurityItem(securityRequirement) From 8c7b58f13c0b6da54a3282886e5372cd8fa12de6 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Tue, 5 Nov 2024 22:51:38 +0900 Subject: [PATCH 05/13] Feat: Set URI --- .../java/com/groomiz/billage/global/config/SwaggerConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/groomiz/billage/global/config/SwaggerConfig.java b/src/main/java/com/groomiz/billage/global/config/SwaggerConfig.java index f2b75b6d..a0956704 100644 --- a/src/main/java/com/groomiz/billage/global/config/SwaggerConfig.java +++ b/src/main/java/com/groomiz/billage/global/config/SwaggerConfig.java @@ -50,7 +50,7 @@ public OpenAPI openAPI() { .title("빌리지 API Document") .version("1.0") .description( - "환영합니다! [발리지](https://example.com)는 서울과학기술대학교 강의실을 빌리기 위해서 위해 만들어진 플랫폼입니다. 이 API 문서는 빌리지의 API를 사용하는 방법을 설명합니다.\n") + "환영합니다! [발리지](https://billage.duckdns.org)는 서울과학기술대학교 강의실을 빌리기 위해서 위해 만들어진 플랫폼입니다. 이 API 문서는 빌리지의 API를 사용하는 방법을 설명합니다.\n") .contact(new io.swagger.v3.oas.models.info.Contact().email("billage.official@gmail.com")); String jwtScheme = "jwtAuth"; From 9312d35b9f39df13ebbb396d5b541d4c21d25cf8 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Wed, 6 Nov 2024 14:33:16 +0900 Subject: [PATCH 06/13] Feat: Set Json --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index de501b23..b262ace3 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -30,7 +30,7 @@ jobs: run: | cd ./src/main/resources mkdir -p ./firebase - echo "${{ secrets.AWS_FCM_JSON }}" > ./FirebaseAdminSDK.yml + echo "${{ secrets.AWS_FCM_JSON }}" > ./FirebaseAdminSDK.json - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle From 1dc378700758c6043d59210643c2346d32fd2de0 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Wed, 6 Nov 2024 15:13:17 +0900 Subject: [PATCH 07/13] Feat: Set CICD --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index b262ace3..52ed37fa 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -29,7 +29,7 @@ jobs: - name: Make FCM JSON run: | cd ./src/main/resources - mkdir -p ./firebase + mkdir firebase echo "${{ secrets.AWS_FCM_JSON }}" > ./FirebaseAdminSDK.json - name: Grant execute permission for gradlew run: chmod +x gradlew From 6b71009899052be3aed6d5d84d364a15cd965bcc Mon Sep 17 00:00:00 2001 From: hawardShin Date: Wed, 6 Nov 2024 15:25:46 +0900 Subject: [PATCH 08/13] Feat: Set FirebaseAdminSDK.json --- .github/workflows/CICD.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 52ed37fa..7c3fe9ed 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -29,7 +29,8 @@ jobs: - name: Make FCM JSON run: | cd ./src/main/resources - mkdir firebase + mkdir -p firebase + cd firebase echo "${{ secrets.AWS_FCM_JSON }}" > ./FirebaseAdminSDK.json - name: Grant execute permission for gradlew run: chmod +x gradlew From b81c14d356f5622b6b2c3e56615cbb2216113b4d Mon Sep 17 00:00:00 2001 From: hawardShin Date: Wed, 6 Nov 2024 15:50:19 +0900 Subject: [PATCH 09/13] Feat: set Start Script --- scripts/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/start.sh b/scripts/start.sh index 6ef857a4..35226374 100644 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -11,7 +11,7 @@ TIME_NOW=$(date +%c) # build 파일 복사 echo "$TIME_NOW > $JAR_FILE 파일 복사" >> $DEPLOY_LOG -cp $PROJECT_ROOT/build/build/libs/*.jar $JAR_FILE +cp $PROJECT_ROOT/build/build/libs/billage-0.0.1-SNAPSHOT.jar $JAR_FILE # jar 파일 실행 echo "$TIME_NOW > $JAR_FILE 파일 실행" >> $DEPLOY_LOG From d7d6471c732529f45e0c6ed0fcfacb1dc89b2d7d Mon Sep 17 00:00:00 2001 From: hawardShin Date: Wed, 6 Nov 2024 19:58:44 +0900 Subject: [PATCH 10/13] Feat: set Json File Add --- .github/workflows/CICD.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 7c3fe9ed..5a000302 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -26,12 +26,19 @@ jobs: run: | cd ./src/main/resources echo "${{ secrets.SECRET_PROPERTIES }}" > ./application-prod.yml - - name: Make FCM JSON + - name: create firebase-admin.json + id: create-json + uses: jsdaniell/create-json@1.1.2 + with: + name: FirebaseAdminSDK.json + json: ${{ secrets.AWS_FCM_JSON }} + + - name: MOVE FCM JSON run: | + mv firebase-admin.json ./src/main/resources cd ./src/main/resources mkdir -p firebase - cd firebase - echo "${{ secrets.AWS_FCM_JSON }}" > ./FirebaseAdminSDK.json + mv FirebaseAdminSDK.json ./firebase - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle From fbd67f61408783036d8b357db803c05ed5662ea8 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Wed, 6 Nov 2024 20:15:15 +0900 Subject: [PATCH 11/13] Feat: set Json File Add --- .github/workflows/CICD.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 5a000302..b6822ea3 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -35,10 +35,11 @@ jobs: - name: MOVE FCM JSON run: | - mv firebase-admin.json ./src/main/resources + mv firebase-admin.json src/main/resources/ cd ./src/main/resources mkdir -p firebase - mv FirebaseAdminSDK.json ./firebase + mv FirebaseAdminSDK.json firebase/ + - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle From ed9e8fa0bf22a260776c4af738de296b8600d244 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Wed, 6 Nov 2024 20:16:25 +0900 Subject: [PATCH 12/13] Feat: Set CICD JSON --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index b6822ea3..98ab06eb 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -35,7 +35,7 @@ jobs: - name: MOVE FCM JSON run: | - mv firebase-admin.json src/main/resources/ + mv FirebaseAdminSDK.json src/main/resources/ cd ./src/main/resources mkdir -p firebase mv FirebaseAdminSDK.json firebase/ From e8b8af22e8028a8831e0f76c85ce9b3809bd2786 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Thu, 7 Nov 2024 16:34:15 +0900 Subject: [PATCH 13/13] Feat : merge code --- .../billage/auth/config/CorsConfig.java | 3 +- .../dto/response/ClassroomDetailResponse.java | 7 ++- .../repository/ClassroomRepository.java | 5 +- .../service/ClassroomImageService.java | 49 ++++++++++--------- .../classroom/service/ClassroomService.java | 13 +++-- .../billage/classroom/service/S3Service.java | 42 ++++++++-------- .../billage/member/service/MemberService.java | 2 +- 7 files changed, 67 insertions(+), 54 deletions(-) diff --git a/src/main/java/com/groomiz/billage/auth/config/CorsConfig.java b/src/main/java/com/groomiz/billage/auth/config/CorsConfig.java index 8a41a758..ee15c095 100644 --- a/src/main/java/com/groomiz/billage/auth/config/CorsConfig.java +++ b/src/main/java/com/groomiz/billage/auth/config/CorsConfig.java @@ -18,7 +18,8 @@ public CorsFilter corsFilter() { config.addAllowedOriginPattern("*"); // 모든 ip에 응답을 허용하겠다. config.addAllowedHeader("*"); // 모든 header에 응답을 허용하겠다. config.addAllowedMethod("*"); // 모든 post, get, put, delete, patch 요청을 허용하겠다. - source.registerCorsConfiguration("*", config); // api로 시작하는 모든 요청에 대해 적용 + source.registerCorsConfiguration("/**", config); // 모든 경로에 대해 적용 + return new CorsFilter(source); } diff --git a/src/main/java/com/groomiz/billage/classroom/dto/response/ClassroomDetailResponse.java b/src/main/java/com/groomiz/billage/classroom/dto/response/ClassroomDetailResponse.java index 68bb03c8..ffc73a21 100644 --- a/src/main/java/com/groomiz/billage/classroom/dto/response/ClassroomDetailResponse.java +++ b/src/main/java/com/groomiz/billage/classroom/dto/response/ClassroomDetailResponse.java @@ -43,10 +43,10 @@ public ClassroomDetailResponse(Long classroomId, String classroomName, String cl this.reservationTimes = reservationTimes; } - public static ClassroomDetailResponse from(Classroom classroom) { - + // 강의실 정보와 예약 정보를 외부에서 주입받아 ClassroomDetailResponse 생성 + public static ClassroomDetailResponse from(Classroom classroom, List reservations) { + List images = classroom.getImages(); - List reservations = classroom.getReservations(); return ClassroomDetailResponse.builder() .classroomId(classroom.getId()) @@ -64,5 +64,4 @@ public static ClassroomDetailResponse from(Classroom classroom) { ) .build(); } - } diff --git a/src/main/java/com/groomiz/billage/classroom/repository/ClassroomRepository.java b/src/main/java/com/groomiz/billage/classroom/repository/ClassroomRepository.java index 95e5bf93..49111034 100644 --- a/src/main/java/com/groomiz/billage/classroom/repository/ClassroomRepository.java +++ b/src/main/java/com/groomiz/billage/classroom/repository/ClassroomRepository.java @@ -21,5 +21,8 @@ public interface ClassroomRepository extends JpaRepository { + "AND rs.status in ('PENDING', 'APPROVED')") Optional findClassroomByIdAndDate(Long classroomId, LocalDate date); - List findByBuildingIdAndFloorAndCapacityGreaterThanEqual(Long buildingId, Long floor, Integer capacity); + Optional findClassroomById(Long classroomId); + + List findClassroomByBuildingIdAndFloorAndCapacityGreaterThanEqual(Long buildingId, Long floor, Integer capacity); + } diff --git a/src/main/java/com/groomiz/billage/classroom/service/ClassroomImageService.java b/src/main/java/com/groomiz/billage/classroom/service/ClassroomImageService.java index b6c46217..90225fae 100644 --- a/src/main/java/com/groomiz/billage/classroom/service/ClassroomImageService.java +++ b/src/main/java/com/groomiz/billage/classroom/service/ClassroomImageService.java @@ -1,11 +1,5 @@ package com.groomiz.billage.classroom.service; -import java.util.UUID; - -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; - import com.groomiz.billage.classroom.dto.response.ClassroomImageResponse; import com.groomiz.billage.classroom.entity.Classroom; import com.groomiz.billage.classroom.entity.ClassroomImage; @@ -16,36 +10,43 @@ import com.groomiz.billage.global.config.S3Config; import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.UUID; @RequiredArgsConstructor @Transactional @Service public class ClassroomImageService { - private final S3Service s3Service; - private final ClassroomRepository classroomRepository; - private final ClassroomImageRepository classroomImageRepository; - private final S3Config s3Config; + private final S3Service s3Service; + private final ClassroomRepository classroomRepository; + private final ClassroomImageRepository classroomImageRepository; + private final S3Config s3Config; + + public ClassroomImageResponse uploadClassroomImage(Long classroomId, MultipartFile imageFile) throws Exception { + //Classroom 엔티티 찾기 + Classroom classroom = classroomRepository.findById(classroomId) + .orElseThrow(() -> new ClassroomException(ClassroomErrorCode.CLASSROOM_NOT_FOUND)); + + String fileName = UUID.randomUUID() + imageFile.getOriginalFilename(); + // 파일데이터와 파일명 넘겨서 S3에 저장 - public ClassroomImageResponse uploadClassroomImage(Long classroomId, MultipartFile imageFile) throws Exception { - //Classroom 엔티티 찾기 - Classroom classroom = classroomRepository.findById(classroomId) - .orElseThrow(() -> new ClassroomException(ClassroomErrorCode.CLASSROOM_NOT_FOUND)); - String fileName = UUID.randomUUID() + imageFile.getOriginalFilename(); - // 파일데이터와 파일명 넘겨서 S3에 저장 - String imageUrl = s3Service.uploadFile(imageFile, fileName); + String imageUrl = s3Service.uploadFile(imageFile, fileName); - ClassroomImage classroomImage = ClassroomImage.builder() - .classroom(classroom) - .imageUrl(imageUrl) - .build(); + ClassroomImage classroomImage = ClassroomImage.builder() + .classroom(classroom) + .imageUrl(imageUrl) + .build(); - classroomImageRepository.save(classroomImage); + classroomImageRepository.save(classroomImage); - return new ClassroomImageResponse(classroomImage.getId(), classroomId, classroomImage.getImageUrl()); + return new ClassroomImageResponse(classroomImage.getId(), classroomId ,classroomImage.getImageUrl()); - } + } } diff --git a/src/main/java/com/groomiz/billage/classroom/service/ClassroomService.java b/src/main/java/com/groomiz/billage/classroom/service/ClassroomService.java index 60063191..291e3e0d 100644 --- a/src/main/java/com/groomiz/billage/classroom/service/ClassroomService.java +++ b/src/main/java/com/groomiz/billage/classroom/service/ClassroomService.java @@ -19,6 +19,8 @@ import com.groomiz.billage.classroom.exception.ClassroomErrorCode; import com.groomiz.billage.classroom.exception.ClassroomException; import com.groomiz.billage.classroom.repository.ClassroomRepository; +import com.groomiz.billage.reservation.entity.Reservation; + import com.groomiz.billage.reservation.repository.ReservationRepository; import lombok.RequiredArgsConstructor; @@ -35,10 +37,14 @@ public class ClassroomService { @Transactional(readOnly = true) public ClassroomDetailResponse findClassroomByIdAndDate(Long classroomId, LocalDate date) { - Classroom classroom = classroomRepository.findClassroomByIdAndDate(classroomId, date) + Classroom classroom = classroomRepository.findClassroomById(classroomId) .orElseThrow(() -> new ClassroomException(ClassroomErrorCode.CLASSROOM_NOT_FOUND)); - return ClassroomDetailResponse.from(classroom); + List reservationsByClassroomIdsAndDate = reservationRepository.findReservationsByClassroomIdsAndDate( + List.of(classroomId), date); + + return ClassroomDetailResponse.from(classroom, reservationsByClassroomIdsAndDate); + } @Transactional(readOnly = true) @@ -53,7 +59,8 @@ public List findAllClassroom(ClassroomListRequest request throw new BuildingException(BuildingErrorCode.FLOOR_NOT_FOUND); } - List classrooms = classroomRepository.findByBuildingIdAndFloorAndCapacityGreaterThanEqual( + List classrooms = classroomRepository.findClassroomByBuildingIdAndFloorAndCapacityGreaterThanEqual( + request.getBuildingId(), request.getFloor(), request.getHeadcount() ); diff --git a/src/main/java/com/groomiz/billage/classroom/service/S3Service.java b/src/main/java/com/groomiz/billage/classroom/service/S3Service.java index ada04c9e..99d1eed4 100644 --- a/src/main/java/com/groomiz/billage/classroom/service/S3Service.java +++ b/src/main/java/com/groomiz/billage/classroom/service/S3Service.java @@ -1,32 +1,34 @@ package com.groomiz.billage.classroom.service; -import java.io.IOException; -import java.net.URLDecoder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.ObjectMetadata; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.ObjectMetadata; +import java.io.IOException; + +import java.net.URLDecoder; -import lombok.RequiredArgsConstructor; -@Service @RequiredArgsConstructor + +@Service public class S3Service { - private final AmazonS3 amazonS3; - - @Value("${cloud.aws.s3.bucket-name}") - private String bucketName; - - public String uploadFile(MultipartFile imageFile, String s3FileName) throws IOException { - // 메타데이터 생성 - ObjectMetadata objMeta = new ObjectMetadata(); - objMeta.setContentLength(imageFile.getInputStream().available()); - // S3에 객체 등록 - amazonS3.putObject(bucketName, s3FileName, imageFile.getInputStream(), objMeta); - // 등록된 객체의 url 반환 - return URLDecoder.decode(amazonS3.getUrl(bucketName, s3FileName).toString(), "utf-8"); - } + private final AmazonS3 amazonS3; + + @Value("${cloud.aws.s3.bucket-name}") + private String bucketName; + + public String uploadFile(MultipartFile imageFile, String s3FileName) throws IOException { + // 메타데이터 생성 + ObjectMetadata objMeta = new ObjectMetadata(); + objMeta.setContentLength(imageFile.getInputStream().available()); + // S3에 객체 등록 + amazonS3.putObject(bucketName, s3FileName, imageFile.getInputStream(), objMeta); + // 등록된 객체의 url 반환 + return URLDecoder.decode(amazonS3.getUrl(bucketName, s3FileName).toString(), "utf-8"); + } } diff --git a/src/main/java/com/groomiz/billage/member/service/MemberService.java b/src/main/java/com/groomiz/billage/member/service/MemberService.java index c510ae34..87275c6d 100644 --- a/src/main/java/com/groomiz/billage/member/service/MemberService.java +++ b/src/main/java/com/groomiz/billage/member/service/MemberService.java @@ -43,7 +43,7 @@ public void register(RegisterRequest registerRequest) { .username(registerRequest.getName()) .password("{bcrypt}" + encodedPassword) .phoneNumber(registerRequest.getPhoneNumber()) - .role(Role.ADMIN) + .role(Role.STUDENT) .studentNumber(registerRequest.getStudentNumber()) .isAdmin(true) .agreedToTerms(registerRequest.isAgreedToTerms())