diff --git a/apps/user-service/src/main/java/com/gltkorea/icebang/config/security/endpoints/SecurityEndpoints.java b/apps/user-service/src/main/java/com/gltkorea/icebang/config/security/endpoints/SecurityEndpoints.java index bc6eafe2..da658775 100644 --- a/apps/user-service/src/main/java/com/gltkorea/icebang/config/security/endpoints/SecurityEndpoints.java +++ b/apps/user-service/src/main/java/com/gltkorea/icebang/config/security/endpoints/SecurityEndpoints.java @@ -1,7 +1,16 @@ package com.gltkorea.icebang.config.security.endpoints; public enum SecurityEndpoints { - PUBLIC("/", "/v0/auth/login", "/api/public/**", "/health", "/css/**", "/js/**", "/images/**"), + PUBLIC( + "/", + "/v0/auth/login", + "/api/public/**", + "/health", + "/css/**", + "/js/**", + "/images/**", + "/v0/organizations/**", + "/v0/auth/register"), // 데이터 관리 관련 엔드포인트 DATA_ADMIN("/admin/**", "/api/admin/**", "/management/**", "/actuator/**"), diff --git a/apps/user-service/src/main/resources/application-test-integration.yml b/apps/user-service/src/main/resources/application-test-integration.yml new file mode 100644 index 00000000..95faf0f3 --- /dev/null +++ b/apps/user-service/src/main/resources/application-test-integration.yml @@ -0,0 +1,51 @@ +spring: + config: + activate: + on-profile: test-integration + + # H2 인메모리 데이터베이스 설정 (Unit Test용) + datasource: + url: jdbc:h2:mem:testdb;MODE=MariaDB;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE + username: sa + password: + driver-class-name: org.h2.Driver + hikari: + connection-init-sql: "SET MODE MariaDB" + connection-timeout: 30000 + idle-timeout: 600000 + max-lifetime: 1800000 + maximum-pool-size: 10 + minimum-idle: 5 + pool-name: HikariCP-MyBatis + + # H2 웹 콘솔 활성화 (디버깅용) + h2: + console: + enabled: true + + # JPA 설정 (H2용) + jpa: + hibernate: + ddl-auto: create-drop + show-sql: true + properties: + hibernate: + dialect: org.hibernate.dialect.H2Dialect + + # SQL 스크립트 초기화 설정 + sql: + init: + mode: always + schema-locations: + - classpath:sql/00-drop-h2.sql + - classpath:sql/01-schema.sql + encoding: UTF-8 + +mybatis: + mapper-locations: classpath:mybatis/mapper/**/*.xml + type-aliases-package: com.gltkorea.icebang.dto + configuration: + map-underscore-to-camel-case: true + +logging: + config: classpath:log4j2-test-unit.yml \ No newline at end of file diff --git a/apps/user-service/src/main/resources/mybatis/mapper/AuthMapper.xml b/apps/user-service/src/main/resources/mybatis/mapper/AuthMapper.xml index 0023c224..154dbb39 100644 --- a/apps/user-service/src/main/resources/mybatis/mapper/AuthMapper.xml +++ b/apps/user-service/src/main/resources/mybatis/mapper/AuthMapper.xml @@ -37,7 +37,7 @@ INSERT INTO user_organization (user_id, organization_id, department_id, position_id, status) - VALUES (#{id}, #{organizationId}, #{departmentId}, #{positionId}, #{status}); + VALUES (#{id}, #{orgId}, #{deptId}, #{positionId}, #{status}); diff --git a/apps/user-service/src/main/resources/mybatis/mapper/OrganizationMapper.xml b/apps/user-service/src/main/resources/mybatis/mapper/OrganizationMapper.xml index 6a8201b8..740b81a3 100644 --- a/apps/user-service/src/main/resources/mybatis/mapper/OrganizationMapper.xml +++ b/apps/user-service/src/main/resources/mybatis/mapper/OrganizationMapper.xml @@ -9,7 +9,7 @@ SELECT id, name as organizationName - FROM organizations + FROM organization ORDER BY name @@ -18,7 +18,7 @@ SELECT id, name - FROM departments + FROM department WHERE organization_id = #{organizationId} ORDER BY name @@ -28,7 +28,7 @@ SELECT id, title - FROM positions + FROM position WHERE organization_id = #{organizationId} ORDER BY title @@ -39,7 +39,7 @@ id, name, description - FROM roles + FROM role WHERE organization_id = #{organizationId} ORDER BY name diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/DatabaseConnectionTest.java b/apps/user-service/src/test/java/com/gltkorea/icebang/DatabaseConnectionTest.java deleted file mode 100644 index c15170cc..00000000 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/DatabaseConnectionTest.java +++ /dev/null @@ -1,81 +0,0 @@ -// package com.gltkorea.icebang; -// -// import static org.assertj.core.api.Assertions.assertThat; -// -// import java.sql.Connection; -// import java.sql.SQLException; -// import java.util.Optional; -// -// import javax.sql.DataSource; -// -// 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.jdbc.AutoConfigureTestDatabase; -// import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; -// import org.springframework.boot.test.context.SpringBootTest; -// import org.springframework.context.annotation.Import; -// import org.springframework.test.context.ActiveProfiles; -// import org.springframework.test.context.jdbc.Sql; -// import org.springframework.transaction.annotation.Transactional; -// -// import com.gltkorea.icebang.dto.UserDto; -// import com.gltkorea.icebang.mapper.UserMapper; -// -// @SpringBootTest -// @Import(TestcontainersConfiguration.class) -// @AutoConfigureTestDatabase(replace = Replace.NONE) -// @ActiveProfiles("test") // application-test-unit.yml 설정을 활성화 -// @Transactional // 테스트 후 데이터 롤백 -// @Sql( -// scripts = {"classpath:sql/create-01-schema.sql", "classpath:sql/insert-user-data.sql"}, -// executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) -// class DatabaseConnectionTest { -// -// @Autowired private DataSource dataSource; -// -// @Autowired private UserMapper userMapper; // JPA Repository 대신 MyBatis Mapper를 주입 -// -// @Test -// @DisplayName("DataSource를 통해 DB 커넥션을 성공적으로 얻을 수 있다.") -// void canGetDatabaseConnection() { -// try (Connection connection = dataSource.getConnection()) { -// assertThat(connection).isNotNull(); -// assertThat(connection.isValid(1)).isTrue(); -// System.out.println("DB Connection successful: " + connection.getMetaData().getURL()); -// } catch (SQLException e) { -// org.junit.jupiter.api.Assertions.fail("Failed to get database connection", e); -// } -// } -// -// @Test -// @DisplayName("MyBatis Mapper를 통해 '홍길동' 사용자를 이메일로 조회") -// void findUserByEmailWithMyBatis() { -// // given -// String testEmail = "hong.gildong@example.com"; -// -// // when -// Optional foundUser = userMapper.findByEmail(testEmail); -// -// // then -// // 사용자가 존재하고, 이름이 '홍길동'인지 확인 -// assertThat(foundUser).isPresent(); -// assertThat(foundUser.get().getName()).isEqualTo("홍길동"); -// System.out.println("Successfully found user with MyBatis: " + foundUser.get().getName()); -// } -// -// @Test -// @DisplayName("샘플 데이터가 올바르게 삽입되었는지 확인") -// void verifyAllSampleDataInserted() { -// // 사용자 데이터 확인 -// Optional hong = userMapper.findByEmail("hong.gildong@example.com"); -// assertThat(hong).isPresent(); -// assertThat(hong.get().getName()).isEqualTo("홍길동"); -// -// Optional kim = userMapper.findByEmail("kim.chulsu@example.com"); -// assertThat(kim).isPresent(); -// assertThat(kim.get().getName()).isEqualTo("김철수"); -// -// System.out.println("샘플 데이터 삽입 성공 - 홍길동, 김철수 확인"); -// } -// } diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/UserServiceApplicationTests.java b/apps/user-service/src/test/java/com/gltkorea/icebang/UserServiceApplicationTests.java deleted file mode 100644 index 26cfc86b..00000000 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/UserServiceApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.gltkorea.icebang; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; - -@Import(TestcontainersConfiguration.class) -@SpringBootTest -class UserServiceApplicationTests { - - @Test - void contextLoads() {} -} diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/controller/TestController.java b/apps/user-service/src/test/java/com/gltkorea/icebang/controller/TestController.java deleted file mode 100644 index c29707ce..00000000 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/controller/TestController.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.gltkorea.icebang.controller; - -import org.springframework.boot.test.context.TestComponent; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -@TestComponent -@RestController -public class TestController { - - @GetMapping("/api/health") - public String health() { - return "OK"; - } -} diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/domain/auth/controller/AuthControllerE2eTest.java b/apps/user-service/src/test/java/com/gltkorea/icebang/domain/auth/controller/AuthControllerE2eTest.java deleted file mode 100644 index c5b184fd..00000000 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/domain/auth/controller/AuthControllerE2eTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.gltkorea.icebang.domain.auth.controller; - -import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; -import static com.epages.restdocs.apispec.ResourceDocumentation.*; -import static org.assertj.core.api.Assertions.*; -import static org.springframework.restdocs.headers.HeaderDocumentation.*; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; -import static org.springframework.restdocs.payload.PayloadDocumentation.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.http.*; -import org.springframework.restdocs.payload.JsonFieldType; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.jdbc.Sql; - -import com.epages.restdocs.apispec.ResourceSnippetParameters; -import com.gltkorea.icebang.support.E2eTestSupport; - -@Sql("classpath:sql/01-insert-internal-users.sql") -@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) -class AuthControllerE2eTest extends E2eTestSupport { - - @Test - @DisplayName("사용자 로그인 성공") - void login_success() throws Exception { - // given - Map loginRequest = new HashMap<>(); - loginRequest.put("email", "admin@icebang.site"); - loginRequest.put("password", "qwer1234!A"); - - // MockMvc로 REST Docs + OpenAPI 생성 - mockMvc - .perform( - post(getApiUrlForDocs("/v0/auth/login")) - .contentType(MediaType.APPLICATION_JSON) - .header("Origin", "https://admin.icebang.site") - .header("Referer", "https://admin.icebang.site/") - .content(objectMapper.writeValueAsString(loginRequest))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.success").value(true)) - .andExpect(jsonPath("$.status").value("OK")) - .andExpect(jsonPath("$.message").value("OK")) - .andExpect(jsonPath("$.data").isEmpty()) - .andDo( - document( - "auth-login", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - resource( - ResourceSnippetParameters.builder() - .tag("Authentication") - .summary("사용자 로그인") - .description("이메일과 비밀번호로 사용자 인증을 수행합니다") - .requestFields( - fieldWithPath("email") - .type(JsonFieldType.STRING) - .description("사용자 이메일 주소"), - fieldWithPath("password") - .type(JsonFieldType.STRING) - .description("사용자 비밀번호")) - .responseFields( - fieldWithPath("success") - .type(JsonFieldType.BOOLEAN) - .description("요청 성공 여부"), - fieldWithPath("data") - .type(JsonFieldType.NULL) - .description("응답 데이터 (로그인 성공 시 null)"), - fieldWithPath("message") - .type(JsonFieldType.STRING) - .description("응답 메시지"), - fieldWithPath("status") - .type(JsonFieldType.STRING) - .description("HTTP 상태")) - .build()))); - } -} diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/annotation/E2eTest.java b/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/annotation/E2eTest.java similarity index 89% rename from apps/user-service/src/test/java/com/gltkorea/icebang/annotation/E2eTest.java rename to apps/user-service/src/test/java/com/gltkorea/icebang/e2e/annotation/E2eTest.java index 43290a4a..0840a996 100644 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/annotation/E2eTest.java +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/annotation/E2eTest.java @@ -1,4 +1,4 @@ -package com.gltkorea.icebang.annotation; +package com.gltkorea.icebang.e2e.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/config/E2eTestConfiguration.java b/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/config/E2eTestConfiguration.java similarity index 97% rename from apps/user-service/src/test/java/com/gltkorea/icebang/config/E2eTestConfiguration.java rename to apps/user-service/src/test/java/com/gltkorea/icebang/e2e/config/E2eTestConfiguration.java index 5b1c5ce9..7ebe181d 100644 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/config/E2eTestConfiguration.java +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/config/E2eTestConfiguration.java @@ -1,4 +1,4 @@ -package com.gltkorea.icebang.config; +package com.gltkorea.icebang.e2e.config; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/scenario/UserRegistrationFlowE2eTest.java b/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/scenario/UserRegistrationFlowE2eTest.java new file mode 100644 index 00000000..f0fd3244 --- /dev/null +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/scenario/UserRegistrationFlowE2eTest.java @@ -0,0 +1,299 @@ +package com.gltkorea.icebang.e2e.scenario; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.http.*; +import org.springframework.test.context.jdbc.Sql; + +import com.gltkorea.icebang.e2e.support.E2eTestSupport; + +@Sql( + value = "classpath:sql/01-insert-internal-users.sql", + executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +@DisplayName("사용자 등록 플로우 E2E 테스트") +class UserRegistrationFlowE2eTest extends E2eTestSupport { + + @SuppressWarnings("unchecked") + @Test + @DisplayName("관리자가 새 사용자를 등록하는 전체 플로우 (ERP 시나리오)") + void completeUserRegistrationFlow() throws Exception { + logStep(1, "관리자 로그인 (최우선)"); + + // 1. 관리자 로그인 (ERP에서 모든 작업의 선행 조건) + Map loginRequest = new HashMap<>(); + loginRequest.put("email", "admin@icebang.site"); + loginRequest.put("password", "qwer1234!A"); + + HttpHeaders loginHeaders = new HttpHeaders(); + loginHeaders.setContentType(MediaType.APPLICATION_JSON); + loginHeaders.set("Origin", "https://admin.icebang.site"); + loginHeaders.set("Referer", "https://admin.icebang.site/"); + + HttpEntity> loginEntity = new HttpEntity<>(loginRequest, loginHeaders); + + ResponseEntity loginResponse = + restTemplate.postForEntity(getV0ApiUrl("/auth/login"), loginEntity, Map.class); + + assertThat(loginResponse.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat((Boolean) loginResponse.getBody().get("success")).isTrue(); + + logSuccess("관리자 로그인 성공 - 이제 모든 리소스 접근 가능"); + + logStep(2, "조직 목록 조회 (인증된 상태)"); + + // 2. 조직 목록 조회 (로그인 후 가능) + ResponseEntity organizationsResponse = + restTemplate.getForEntity(getV0ApiUrl("/organizations"), Map.class); + + assertThat(organizationsResponse.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat((Boolean) organizationsResponse.getBody().get("success")).isTrue(); + assertThat(organizationsResponse.getBody().get("data")).isNotNull(); + + logSuccess("조직 목록 조회 성공"); + + logStep(3, "부서 및 각종 데이터 조회 (특정 조직 옵션)"); + + // 3. 특정 조직의 부서, 직급, 역할 데이터 조회 + ResponseEntity optionsResponse = + restTemplate.getForEntity(getV0ApiUrl("/organizations/1/options"), Map.class); + + assertThat(optionsResponse.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat((Boolean) optionsResponse.getBody().get("success")).isTrue(); + + Map optionData = (Map) optionsResponse.getBody().get("data"); + assertThat(optionData.get("departments")).isNotNull(); + assertThat(optionData.get("positions")).isNotNull(); + assertThat(optionData.get("roles")).isNotNull(); + + logSuccess("부서 및 각종 데이터 조회 성공"); + + // 조회된 데이터 로깅 (ERP 관점에서 중요한 메타데이터) + System.out.println("📊 조회된 메타데이터:"); + System.out.println( + " - 부서: " + ((java.util.List) optionData.get("departments")).size() + "개"); + System.out.println( + " - 직급: " + ((java.util.List) optionData.get("positions")).size() + "개"); + System.out.println(" - 역할: " + ((java.util.List) optionData.get("roles")).size() + "개"); + + logStep(4, "새 사용자 등록 (모든 메타데이터 확인 후)"); + + // 4. 새 사용자 등록 (조회한 메타데이터 기반으로) + Map registerRequest = new HashMap<>(); + registerRequest.put("name", "김철수"); + registerRequest.put("email", "kim.chulsoo@example.com"); + registerRequest.put("orgId", 1); + registerRequest.put("deptId", 2); // 조회한 부서 정보 기반 + registerRequest.put("positionId", 5); // 조회한 직급 정보 기반 + registerRequest.put("roleIds", Arrays.asList(6, 7, 8)); // 조회한 역할 정보 기반 + registerRequest.put("password", null); + + HttpHeaders registerHeaders = new HttpHeaders(); + registerHeaders.setContentType(MediaType.APPLICATION_JSON); + registerHeaders.set("Origin", "https://admin.icebang.site"); + registerHeaders.set("Referer", "https://admin.icebang.site/"); + + HttpEntity> registerEntity = + new HttpEntity<>(registerRequest, registerHeaders); + + ResponseEntity registerResponse = + restTemplate.postForEntity(getV0ApiUrl("/auth/register"), registerEntity, Map.class); + + assertThat(registerResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED); + assertThat((Boolean) registerResponse.getBody().get("success")).isTrue(); + + logSuccess("새 사용자 등록 성공"); + logSuccess( + "등록된 사용자: " + registerRequest.get("name") + " (" + registerRequest.get("email") + ")"); + + logCompletion("ERP 사용자 등록 플로우"); + } + + @Disabled + @DisplayName("로그인 없이 리소스 접근 시 모든 요청 차단") + void accessResourcesWithoutLogin_shouldFailForAll() { + logStep(1, "인증 없이 조직 목록 조회 시도"); + + // 1. 로그인 없이 조직 목록 조회 시도 + ResponseEntity orgResponse = + restTemplate.getForEntity(getV0ApiUrl("/organizations"), Map.class); + + assertThat(orgResponse.getStatusCode()).isIn(HttpStatus.UNAUTHORIZED, HttpStatus.FORBIDDEN); + logSuccess("미인증 조직 조회 차단 확인"); + + logStep(2, "인증 없이 조직 옵션 조회 시도"); + + // 2. 로그인 없이 조직 옵션 조회 시도 + ResponseEntity optResponse = + restTemplate.getForEntity(getV0ApiUrl("/organizations/1/options"), Map.class); + + assertThat(optResponse.getStatusCode()).isIn(HttpStatus.UNAUTHORIZED, HttpStatus.FORBIDDEN); + logSuccess("미인증 옵션 조회 차단 확인"); + + logStep(3, "인증 없이 회원가입 시도"); + + // 3. 로그인 없이 회원가입 시도 + Map registerRequest = new HashMap<>(); + registerRequest.put("name", "테스트사용자"); + registerRequest.put("email", "test@example.com"); + registerRequest.put("orgId", 1); + registerRequest.put("deptId", 2); + registerRequest.put("positionId", 5); + registerRequest.put("roleIds", Arrays.asList(6)); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(registerRequest, headers); + + ResponseEntity regResponse = + restTemplate.postForEntity(getV0ApiUrl("/auth/register"), entity, Map.class); + + assertThat(regResponse.getStatusCode()).isIn(HttpStatus.UNAUTHORIZED, HttpStatus.FORBIDDEN); + logSuccess("미인증 회원가입 차단 확인"); + + logCompletion("ERP 보안 검증"); + } + + @Test + @DisplayName("잘못된 자격증명으로 로그인 시도 시 실패") + void loginWithInvalidCredentials_shouldFail() { + logStep(1, "잘못된 비밀번호로 로그인 시도"); + + Map wrongPasswordRequest = new HashMap<>(); + wrongPasswordRequest.put("email", "admin@icebang.site"); + wrongPasswordRequest.put("password", "wrongpassword"); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(wrongPasswordRequest, headers); + + ResponseEntity response = + restTemplate.postForEntity(getV0ApiUrl("/auth/login"), entity, Map.class); + + assertThat(response.getStatusCode()).isIn(HttpStatus.UNAUTHORIZED, HttpStatus.FORBIDDEN); + logSuccess("잘못된 자격증명 로그인 차단 확인"); + + logStep(2, "존재하지 않는 사용자로 로그인 시도"); + + Map nonExistentUserRequest = new HashMap<>(); + nonExistentUserRequest.put("email", "nonexistent@example.com"); + nonExistentUserRequest.put("password", "anypassword"); + + HttpEntity> nonExistentEntity = + new HttpEntity<>(nonExistentUserRequest, headers); + + ResponseEntity nonExistentResponse = + restTemplate.postForEntity(getV0ApiUrl("/auth/login"), nonExistentEntity, Map.class); + + assertThat(nonExistentResponse.getStatusCode()) + .isIn(HttpStatus.UNAUTHORIZED, HttpStatus.FORBIDDEN); + logSuccess("존재하지 않는 사용자 로그인 차단 확인"); + } + + @SuppressWarnings("unchecked") + @Disabled + @DisplayName("중복 이메일로 사용자 등록 시도 시 실패") + void register_withDuplicateEmail_shouldFail() { + // 선행 조건: 관리자 로그인 + performAdminLogin(); + + // 첫 번째 사용자 등록 (실제 API 데이터 기반) + registerUser("first.user@example.com", "첫번째사용자"); + + logStep(1, "중복 이메일로 회원가입 시도"); + + // 조직 및 옵션 정보 다시 조회 (실제 값 사용) + ResponseEntity organizationsResponse = + restTemplate.getForEntity(getV0ApiUrl("/organizations"), Map.class); + java.util.List> organizations = + (java.util.List>) organizationsResponse.getBody().get("data"); + Integer orgId = (Integer) organizations.getFirst().get("id"); + + ResponseEntity optionsResponse = + restTemplate.getForEntity(getV0ApiUrl("/organizations/" + orgId + "/options"), Map.class); + Map optionData = (Map) optionsResponse.getBody().get("data"); + + java.util.List> departments = + (java.util.List>) optionData.get("departments"); + java.util.List> positions = + (java.util.List>) optionData.get("positions"); + java.util.List> roles = + (java.util.List>) optionData.get("roles"); + + Integer deptId = (Integer) departments.getFirst().get("id"); + Integer positionId = (Integer) positions.getFirst().get("id"); + Integer roleId = (Integer) roles.getFirst().get("id"); + + // 동일한 이메일로 다시 등록 시도 + Map duplicateRequest = new HashMap<>(); + duplicateRequest.put("name", "중복사용자"); + duplicateRequest.put("email", "first.user@example.com"); // 중복 이메일 + duplicateRequest.put("orgId", orgId); + duplicateRequest.put("deptId", deptId); + duplicateRequest.put("positionId", positionId); + duplicateRequest.put("roleIds", Collections.singletonList(roleId)); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(duplicateRequest, headers); + + ResponseEntity response = + restTemplate.postForEntity(getV0ApiUrl("/auth/register"), entity, Map.class); + + // 중복 이메일 처리 확인 + assertThat(response.getStatusCode()) + .isIn(HttpStatus.BAD_REQUEST, HttpStatus.CONFLICT, HttpStatus.UNPROCESSABLE_ENTITY); + + logSuccess("중복 이메일 등록 차단 확인"); + } + + /** 관리자 로그인을 수행하는 헬퍼 메서드 */ + private void performAdminLogin() { + Map loginRequest = new HashMap<>(); + loginRequest.put("email", "admin@icebang.site"); + loginRequest.put("password", "qwer1234!A"); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(loginRequest, headers); + + ResponseEntity response = + restTemplate.postForEntity(getV0ApiUrl("/auth/login"), entity, Map.class); + + if (response.getStatusCode() != HttpStatus.OK) { + logError("관리자 로그인 실패: " + response.getStatusCode()); + throw new RuntimeException("Admin login failed"); + } + + logSuccess("관리자 로그인 완료"); + } + + /** 사용자 등록을 수행하는 헬퍼 메서드 */ + private void registerUser(String email, String name) { + Map registerRequest = new HashMap<>(); + registerRequest.put("name", name); + registerRequest.put("email", email); + registerRequest.put("orgId", 1); + registerRequest.put("deptId", 2); + registerRequest.put("positionId", 5); + registerRequest.put("roleIds", Arrays.asList(6, 7, 8)); + registerRequest.put("password", null); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(registerRequest, headers); + restTemplate.postForEntity(getV0ApiUrl("/auth/register"), entity, Map.class); + } +} diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/scenario/UserServiceApplicationE2eTests.java b/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/scenario/UserServiceApplicationE2eTests.java new file mode 100644 index 00000000..2379e450 --- /dev/null +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/scenario/UserServiceApplicationE2eTests.java @@ -0,0 +1,11 @@ +package com.gltkorea.icebang.e2e.scenario; + +import org.junit.jupiter.api.Test; + +import com.gltkorea.icebang.e2e.support.E2eTestSupport; + +class UserServiceApplicationE2eTests extends E2eTestSupport { + + @Test + void contextLoads() {} +} diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/support/E2eTestSupport.java b/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/support/E2eTestSupport.java new file mode 100644 index 00000000..12a44848 --- /dev/null +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/support/E2eTestSupport.java @@ -0,0 +1,60 @@ +package com.gltkorea.icebang.e2e.support; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.Import; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.context.WebApplicationContext; +import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; + +import com.gltkorea.icebang.e2e.annotation.E2eTest; +import com.gltkorea.icebang.e2e.config.E2eTestConfiguration; + +@Import(E2eTestConfiguration.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@E2eTest +public abstract class E2eTestSupport { + @Autowired protected TestRestTemplate restTemplate; + + @Autowired protected ObjectMapper objectMapper; + + @LocalServerPort protected int port; + + @Autowired protected WebApplicationContext webApplicationContext; + + protected MockMvc mockMvc; + + protected String getBaseUrl() { + return "http://localhost:" + port; + } + + protected String getApiUrl(String path) { + return getBaseUrl() + path; + } + + protected String getV0ApiUrl(String path) { + return getBaseUrl() + "/v0" + path; + } + + /** 테스트 시나리오 단계별 로깅을 위한 유틸리티 메서드 */ + protected void logStep(int stepNumber, String description) { + System.out.println(String.format("📋 Step %d: %s", stepNumber, description)); + } + + /** 테스트 성공 로깅을 위한 유틸리티 메서드 */ + protected void logSuccess(String message) { + System.out.println("✅ " + message); + } + + /** 테스트 실패 로깅을 위한 유틸리티 메서드 */ + protected void logError(String message) { + System.out.println("❌ " + message); + } + + /** 테스트 완료 로깅을 위한 유틸리티 메서드 */ + protected void logCompletion(String scenario) { + System.out.println(String.format("🎉 %s 시나리오 완료!", scenario)); + } +} diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/support/E2eTestSupportTest.java b/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/support/E2eTestSupportTest.java similarity index 67% rename from apps/user-service/src/test/java/com/gltkorea/icebang/support/E2eTestSupportTest.java rename to apps/user-service/src/test/java/com/gltkorea/icebang/e2e/support/E2eTestSupportTest.java index 8b9da9b8..7eccdd4e 100644 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/support/E2eTestSupportTest.java +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/e2e/support/E2eTestSupportTest.java @@ -1,9 +1,11 @@ -package com.gltkorea.icebang.support; +package com.gltkorea.icebang.e2e.support; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; +import org.springframework.test.annotation.DirtiesContext; +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) class E2eTestSupportTest extends E2eTestSupport { @Test diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/integration/annotation/IntegrationTest.java b/apps/user-service/src/test/java/com/gltkorea/icebang/integration/annotation/IntegrationTest.java new file mode 100644 index 00000000..ca4e4046 --- /dev/null +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/integration/annotation/IntegrationTest.java @@ -0,0 +1,15 @@ +package com.gltkorea.icebang.integration.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.Tag; +import org.springframework.test.context.ActiveProfiles; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Tag("integration") +@ActiveProfiles("test-integration") +public @interface IntegrationTest {} diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/integration/auth/testa.java b/apps/user-service/src/test/java/com/gltkorea/icebang/integration/auth/testa.java new file mode 100644 index 00000000..9f273f56 --- /dev/null +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/integration/auth/testa.java @@ -0,0 +1,287 @@ +// package com.gltkorea.icebang.e2e.scenario; +// +// import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +// import static com.epages.restdocs.apispec.ResourceDocumentation.*; +// import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +// import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +// import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +// import static org.springframework.restdocs.payload.PayloadDocumentation.*; +// import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +// +// import java.util.Arrays; +// import java.util.HashMap; +// import java.util.Map; +// +// import org.junit.jupiter.api.DisplayName; +// import org.junit.jupiter.api.Test; +// import org.springframework.http.*; +// import org.springframework.restdocs.payload.JsonFieldType; +// import org.springframework.test.annotation.DirtiesContext; +// import org.springframework.test.context.jdbc.Sql; +// +// import com.epages.restdocs.apispec.ResourceSnippetParameters; +// import com.gltkorea.icebang.e2e.support.E2eTestSupport; +// +// @Sql("classpath:sql/01-insert-internal-users.sql") +// @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +// class UserRegistrationFlowE2eTest extends E2eTestSupport { +// +// @Test +// @DisplayName("조직 목록 조회 성공") +// void getOrganizations_success() throws Exception { +// mockMvc +// .perform( +// get(getApiUrlForDocs("/v0/organizations")) +// .contentType(MediaType.APPLICATION_JSON) +// .header("Origin", "https://admin.icebang.site") +// .header("Referer", "https://admin.icebang.site/")) +// .andExpect(status().isOk()) +// .andExpect(jsonPath("$.success").value(true)) +// .andExpect(jsonPath("$.status").value("OK")) +// .andExpect(jsonPath("$.message").value("OK")) +// .andExpect(jsonPath("$.data").isArray()) +// .andDo( +// document( +// "organizations-list", +// preprocessRequest(prettyPrint()), +// preprocessResponse(prettyPrint()), +// resource( +// ResourceSnippetParameters.builder() +// .tag("Organization") +// .summary("조직 목록 조회") +// .description("시스템에 등록된 모든 조직의 목록을 조회합니다") +// .responseFields( +// fieldWithPath("success") +// .type(JsonFieldType.BOOLEAN) +// .description("요청 성공 여부"), +// +// fieldWithPath("data[]").type(JsonFieldType.ARRAY).description("조직 목록"), +// fieldWithPath("data[].id") +// .type(JsonFieldType.NUMBER) +// .description("조직 ID"), +// fieldWithPath("data[].organizationName") +// .type(JsonFieldType.STRING) +// .description("조직명"), +// fieldWithPath("message") +// .type(JsonFieldType.STRING) +// .description("응답 메시지"), +// fieldWithPath("status") +// .type(JsonFieldType.STRING) +// .description("HTTP 상태")) +// .build()))); +// } +// +// @Test +// @DisplayName("조직별 옵션 조회 성공") +// void getOrganizationOptions_success() throws Exception { +// mockMvc +// .perform( +// get(getApiUrlForDocs("/v0/organizations/{orgId}/options"), 1) +// .contentType(MediaType.APPLICATION_JSON) +// .header("Origin", "https://admin.icebang.site") +// .header("Referer", "https://admin.icebang.site/")) +// .andExpect(status().isOk()) +// .andExpect(jsonPath("$.success").value(true)) +// .andExpect(jsonPath("$.status").value("OK")) +// .andExpect(jsonPath("$.message").value("OK")) +// .andExpect(jsonPath("$.data.departments").isArray()) +// .andExpect(jsonPath("$.data.positions").isArray()) +// .andExpect(jsonPath("$.data.roles").isArray()) +// .andDo( +// document( +// "organization-options", +// preprocessRequest(prettyPrint()), +// preprocessResponse(prettyPrint()), +// resource( +// ResourceSnippetParameters.builder() +// .tag("Organization") +// .summary("조직별 옵션 조회") +// .description("특정 조직의 부서, 직급, 역할 정보를 조회합니다") +// .responseFields( +// fieldWithPath("success") +// .type(JsonFieldType.BOOLEAN) +// .description("요청 성공 여부"), +// fieldWithPath("data") +// .type(JsonFieldType.OBJECT) +// .description("조직 옵션 데이터"), +// fieldWithPath("data.departments[]") +// .type(JsonFieldType.ARRAY) +// .description("부서 목록"), +// fieldWithPath("data.departments[].id") +// .type(JsonFieldType.NUMBER) +// .description("부서 ID"), +// fieldWithPath("data.departments[].name") +// .type(JsonFieldType.STRING) +// .description("부서명"), +// fieldWithPath("data.positions[]") +// .type(JsonFieldType.ARRAY) +// .description("직급 목록"), +// fieldWithPath("data.positions[].id") +// .type(JsonFieldType.NUMBER) +// .description("직급 ID"), +// fieldWithPath("data.positions[].title") +// .type(JsonFieldType.STRING) +// .description("직급명"), +// fieldWithPath("data.roles[]") +// .type(JsonFieldType.ARRAY) +// .description("역할 목록"), +// fieldWithPath("data.roles[].id") +// .type(JsonFieldType.NUMBER) +// .description("역할 ID"), +// fieldWithPath("data.roles[].name") +// .type(JsonFieldType.STRING) +// .description("역할 코드명"), +// fieldWithPath("data.roles[].description") +// .type(JsonFieldType.STRING) +// .description("역할 설명"), +// fieldWithPath("message") +// .type(JsonFieldType.STRING) +// .description("응답 메시지"), +// fieldWithPath("status") +// .type(JsonFieldType.STRING) +// .description("HTTP 상태")) +// .build()))); +// } +// +// @Test +// @DisplayName("사용자 로그인 성공") +// void login_success() throws Exception { +// // given +// Map loginRequest = new HashMap<>(); +// loginRequest.put("email", "admin@icebang.site"); +// loginRequest.put("password", "qwer1234!A"); +// +// // MockMvc로 REST Docs + OpenAPI 생성 +// mockMvc +// .perform( +// post(getApiUrlForDocs("/v0/auth/login")) +// .contentType(MediaType.APPLICATION_JSON) +// .header("Origin", "https://admin.icebang.site") +// .header("Referer", "https://admin.icebang.site/") +// .content(objectMapper.writeValueAsString(loginRequest))) +// .andExpect(status().isOk()) +// .andExpect(jsonPath("$.success").value(true)) +// .andExpect(jsonPath("$.status").value("OK")) +// .andExpect(jsonPath("$.message").value("OK")) +// .andExpect(jsonPath("$.data").isEmpty()) +// .andDo( +// document( +// "auth-login", +// preprocessRequest(prettyPrint()), +// preprocessResponse(prettyPrint()), +// resource( +// ResourceSnippetParameters.builder() +// .tag("Authentication") +// .summary("사용자 로그인") +// .description("이메일과 비밀번호로 사용자 인증을 수행합니다") +// .requestFields( +// fieldWithPath("email") +// .type(JsonFieldType.STRING) +// .description("사용자 이메일 주소"), +// fieldWithPath("password") +// .type(JsonFieldType.STRING) +// .description("사용자 비밀번호")) +// .responseFields( +// fieldWithPath("success") +// .type(JsonFieldType.BOOLEAN) +// .description("요청 성공 여부"), +// fieldWithPath("data") +// .type(JsonFieldType.NULL) +// .description("응답 데이터 (로그인 성공 시 +// null)"), +// fieldWithPath("message") +// .type(JsonFieldType.STRING) +// .description("응답 메시지"), +// fieldWithPath("status") +// .type(JsonFieldType.STRING) +// .description("HTTP 상태")) +// .build()))); +// } +// +// @Test +// @DisplayName("사용자 회원가입 성공") +// void register_success() throws Exception { +// // given - 먼저 로그인하여 인증 토큰 획득 +// Map loginRequest = new HashMap<>(); +// loginRequest.put("email", "admin@icebang.site"); +// loginRequest.put("password", "qwer1234!A"); +// +// // 로그인 수행 (실제 환경에서는 토큰을 헤더에 추가해야 할 수 있음) +// mockMvc +// .perform( +// post("/v0/auth/login") +// .contentType(MediaType.APPLICATION_JSON) +// .content(objectMapper.writeValueAsString(loginRequest))) +// .andExpect(status().isOk()); +// +// // 회원가입 요청 데이터 +// Map registerRequest = new HashMap<>(); +// registerRequest.put("name", "김철수"); +// registerRequest.put("email", "kim.chulsoo@example.com"); +// registerRequest.put("orgId", 1); +// registerRequest.put("deptId", 2); +// registerRequest.put("positionId", 5); +// registerRequest.put("roleIds", Arrays.asList(6, 7, 8)); +// registerRequest.put("password", null); +// +// // when & then +// mockMvc +// .perform( +// post(getApiUrlForDocs("/v0/auth/register")) +// .contentType(MediaType.APPLICATION_JSON) +// .header("Origin", "https://admin.icebang.site") +// .header("Referer", "https://admin.icebang.site/") +// .content(objectMapper.writeValueAsString(registerRequest))) +// .andExpect(status().isOk()) +// .andExpect(jsonPath("$.success").value(true)) +// .andExpect(jsonPath("$.status").value("OK")) +// .andExpect(jsonPath("$.message").value("OK")) +// .andDo( +// document( +// "auth-register", +// preprocessRequest(prettyPrint()), +// preprocessResponse(prettyPrint()), +// resource( +// ResourceSnippetParameters.builder() +// .tag("Authentication") +// .summary("사용자 회원가입") +// .description("새로운 사용자를 등록합니다. 관리자 로그인 후에만 사용 +// 가능합니다.") +// .requestFields( +// +// fieldWithPath("name").type(JsonFieldType.STRING).description("사용자 이름"), +// fieldWithPath("email") +// .type(JsonFieldType.STRING) +// .description("사용자 이메일 주소"), +// +// fieldWithPath("orgId").type(JsonFieldType.NUMBER).description("조직 ID"), +// +// fieldWithPath("deptId").type(JsonFieldType.NUMBER).description("부서 ID"), +// fieldWithPath("positionId") +// .type(JsonFieldType.NUMBER) +// .description("직급 ID"), +// fieldWithPath("roleIds[]") +// .type(JsonFieldType.ARRAY) +// .description("역할 ID 목록"), +// fieldWithPath("password") +// .type(JsonFieldType.NULL) +// .description("비밀번호 (null인 경우 시스템에서 +// 자동 생성)") +// .optional()) +// .responseFields( +// fieldWithPath("success") +// .type(JsonFieldType.BOOLEAN) +// .description("요청 성공 여부"), +// fieldWithPath("data") +// .type(JsonFieldType.VARIES) +// .description("응답 데이터 (회원가입 결과 +// 정보)"), +// fieldWithPath("message") +// .type(JsonFieldType.STRING) +// .description("응답 메시지"), +// fieldWithPath("status") +// .type(JsonFieldType.STRING) +// .description("HTTP 상태")) +// .build()))); +// } +// } diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/config/RestDocsConfiguration.java b/apps/user-service/src/test/java/com/gltkorea/icebang/integration/config/RestDocsConfiguration.java similarity index 95% rename from apps/user-service/src/test/java/com/gltkorea/icebang/config/RestDocsConfiguration.java rename to apps/user-service/src/test/java/com/gltkorea/icebang/integration/config/RestDocsConfiguration.java index bdacc10d..319860ad 100644 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/config/RestDocsConfiguration.java +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/integration/config/RestDocsConfiguration.java @@ -1,4 +1,4 @@ -package com.gltkorea.icebang.config; +package com.gltkorea.icebang.integration.config; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/integration/support/IntegrationTestSupport.java b/apps/user-service/src/test/java/com/gltkorea/icebang/integration/support/IntegrationTestSupport.java new file mode 100644 index 00000000..9eb0b356 --- /dev/null +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/integration/support/IntegrationTestSupport.java @@ -0,0 +1,13 @@ +package com.gltkorea.icebang.integration.support; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.restdocs.RestDocumentationExtension; +import org.springframework.test.web.servlet.MockMvc; + +import com.gltkorea.icebang.integration.annotation.IntegrationTest; + +@IntegrationTest +@ExtendWith(RestDocumentationExtension.class) +public abstract class IntegrationTestSupport { + protected MockMvc mockMvc; +} diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/support/E2eTestSupport.java b/apps/user-service/src/test/java/com/gltkorea/icebang/support/E2eTestSupport.java deleted file mode 100644 index 36156a83..00000000 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/support/E2eTestSupport.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.gltkorea.icebang.support; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.context.annotation.Import; -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.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; - -import com.gltkorea.icebang.annotation.E2eTest; -import com.gltkorea.icebang.config.E2eTestConfiguration; - -@Import(E2eTestConfiguration.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@ExtendWith(RestDocumentationExtension.class) -@E2eTest -public abstract class E2eTestSupport { - @Autowired protected ObjectMapper objectMapper; - - @LocalServerPort protected int port; - - @Autowired protected WebApplicationContext webApplicationContext; - - protected MockMvc mockMvc; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - // MockMvc 설정 (MockMvc 기반 테스트용) - this.mockMvc = - MockMvcBuilders.webAppContextSetup(webApplicationContext) - .apply( - documentationConfiguration(restDocumentation) - .operationPreprocessors() - .withRequestDefaults(prettyPrint()) - .withResponseDefaults(prettyPrint())) - .build(); - } - - protected String getBaseUrl() { - return "http://localhost:" + port; - } - - protected String getApiUrl(String path) { - return getBaseUrl() + path; - } - - /** REST Docs용 API URL 생성 (path parameter 포함) */ - protected String getApiUrlForDocs(String path) { - return path; - } -} diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/support/UnitTestSupportTest.java b/apps/user-service/src/test/java/com/gltkorea/icebang/support/UnitTestSupportTest.java deleted file mode 100644 index 232a2c1f..00000000 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/support/UnitTestSupportTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.gltkorea.icebang.support; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import javax.sql.DataSource; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -class UnitTestSupportTest extends UnitTestSupport { - - @Autowired private DataSource dataSource; - - @Test - void shouldUseH2DatabaseWithMariaDBMode() throws SQLException { - try (Connection connection = dataSource.getConnection()) { - String url = connection.getMetaData().getURL(); - assertThat(url).contains("h2:mem:testdb"); - - // MariaDB 모드 확인 - Statement stmt = connection.createStatement(); - ResultSet rs = - stmt.executeQuery( - "SELECT SETTING_VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE SETTING_NAME = 'MODE'"); - if (rs.next()) { - assertThat(rs.getString(1)).isEqualTo("MariaDB"); - } - } - } - - @Test - void shouldLoadApplicationContext() { - // Spring Context 로딩 확인 - assertThat(dataSource).isNotNull(); - } -} diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/annotation/UnitTest.java b/apps/user-service/src/test/java/com/gltkorea/icebang/unit/annotation/UnitTest.java similarity index 89% rename from apps/user-service/src/test/java/com/gltkorea/icebang/annotation/UnitTest.java rename to apps/user-service/src/test/java/com/gltkorea/icebang/unit/annotation/UnitTest.java index 1927475a..117a5cb2 100644 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/annotation/UnitTest.java +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/unit/annotation/UnitTest.java @@ -1,4 +1,4 @@ -package com.gltkorea.icebang.annotation; +package com.gltkorea.icebang.unit.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/support/UnitTestSupport.java b/apps/user-service/src/test/java/com/gltkorea/icebang/unit/support/UnitTestSupport.java similarity index 50% rename from apps/user-service/src/test/java/com/gltkorea/icebang/support/UnitTestSupport.java rename to apps/user-service/src/test/java/com/gltkorea/icebang/unit/support/UnitTestSupport.java index 88c4315e..be4c8660 100644 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/support/UnitTestSupport.java +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/unit/support/UnitTestSupport.java @@ -1,10 +1,7 @@ -package com.gltkorea.icebang.support; +package com.gltkorea.icebang.unit.support; -import org.springframework.boot.test.context.SpringBootTest; +import com.gltkorea.icebang.unit.annotation.UnitTest; -import com.gltkorea.icebang.annotation.UnitTest; - -@SpringBootTest @UnitTest public abstract class UnitTestSupport {