Conversation
|
Caution Review failedThe pull request is closed. WalkthroughHeartbeat 관련 API·도메인·DTO 제거, TestContainers 기반 통합 테스트 인프라 및 단위 테스트 베이스/모킹 추가, Redis 설정 리팩토링 및 기간 집계 쿼리 일부 캡핑/순위 로직 조정. (50자 이내) Changes
Sequence Diagram(s)(생성 조건에 부합하지 않아 다이어그램을 생략합니다.) Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Fix all issues with AI Agents 🤖
In @build.gradle:
- Around line 66-69: The TestContainers dependencies in build.gradle are pinned
to the old 1.19.3 version; update the three coordinates
'org.testcontainers:junit-jupiter', 'org.testcontainers:mysql', and
'org.testcontainers:testcontainers' to use version 2.0.2 instead of 1.19.3 so
your testImplementation lines reference '...:2.0.2' to pick up the latest fixes
and features.
In
@src/main/java/com/gpt/geumpumtabackend/study/service/StudySessionService.java:
- Line 29: 컨트롤러와 코드베이스에 남아있는 HeartBeat 잔여물을 정리하세요: StudySessionController에서
HeartBeatRequest 및 HeartBeatResponse 임포트(현재 미사용)를 제거하고 파일 내에서 관련 참조가 전혀 없는지
확인하세요; HeartBeatRequest.java와 HeartBeatResponse.java DTO 파일은 실제로 사용되지 않으면 삭제하거나
아카이브(또는 주석으로 보관)하고 변경사항을 커밋 메시지에 명시하세요; 엔티티 StudySession에서 heartBeatAt 필드와
updateHeartBeatAt() 메서드를 삭제하고, StudySessionRepository에서 해당 필드를 참조하는 모든 쿼리/메서드를
찾아서 제거 또는 수정하여 컴파일 오류가 나지 않도록 하세요; 변경 후 관련 유닛/통합 테스트와 빌드를 실행해 깨진 참조가 없는지 검증하세요.
In @src/test/java/com/gpt/geumpumtabackend/unit/config/BaseUnitTest.java:
- Around line 9-12: BaseUnitTest is annotated with @ActiveProfiles("unit-test")
but no application-unit-test.* exists and the class comments mention H2 while
build.gradle lacks the H2 dependency; either create
application-unit-test.yml/properties and implement the BaseUnitTest class to
wire mocks and (optionally) H2 test config, or change the annotation to
@ActiveProfiles("test") and reuse application-test.yml while updating the top
comments to reflect the actual configuration; if you choose the new profile
option, also add H2 dependency to build.gradle (or remove H2 mentions) so
comments, profile name, and dependencies remain consistent with the
@ActiveProfiles setting.
In
@src/test/java/com/gpt/geumpumtabackend/unit/rank/service/DepartmentRankServiceTest.java:
- Around line 46-324: The issue is that GetCompletedDailyDepartmentRanking,
GetCurrentWeeklyDepartmentRanking, GetCurrentMonthlyDepartmentRanking,
BuildDepartmentRankingResponse and the helper methods (createTestUser,
createMockDepartmentRankingTemp) are accidentally defined inside the
GetCurrentDailyDepartmentRanking nested test class; fix by moving the closing
brace of GetCurrentDailyDepartmentRanking to immediately after its last @Test
method so those other nested classes and helper methods are declared at the same
level as GetCurrentDailyDepartmentRanking (i.e., as separate nested classes or
class-level methods of DepartmentRankServiceTest), ensuring symbols like
GetCurrentDailyDepartmentRanking, GetCompletedDailyDepartmentRanking,
GetCurrentWeeklyDepartmentRanking, GetCurrentMonthlyDepartmentRanking,
BuildDepartmentRankingResponse, createTestUser and
createMockDepartmentRankingTemp are top-level members of the test class rather
than nested inside another test class.
In
@src/test/java/com/gpt/geumpumtabackend/unit/rank/service/PersonalRankServiceTest.java:
- Around line 272-298: The test 많은_랭킹_데이터에서_마지막_순위_종합 creates only 3
PersonalRankingTemp entries (largeRankings) but the comment and test name claim
"100명의 랭킹 데이터"; update the test to match intent by either changing the
comment/name or generating a larger list programmatically (e.g., fill
largeRankings with 100 createMockPersonalRankingTemp(...) calls), ensure the
expected assertion on response.myRanking().rank() is adjusted accordingly, and
keep references to the existing methods/variables
(createMockPersonalRankingTemp, largeRankings,
personalRankService.getCurrentDaily, notInRankingUserId) so the test reflects
the actual dataset size.
In
@src/test/java/com/gpt/geumpumtabackend/unit/wifi/service/CampusWiFiValidationServiceTest.java:
- Around line 22-23: 테스트에서 CampusWiFiValidationService
자체(wifiValidationService)를 @Mock 하고 있어 서비스 로직을 검증하지 못합니다; 대신
CampusWiFiValidationService를 @InjectMocks로 선언하고 그 의존성들(예: CampusWiFiSettings,
RedisTemplate<String,Object> 등 현재 서비스가 사용하는 필드들)을 @Mock으로 만들어 주입하도록 변경하세요 (즉,
제거할 대상: @Mock private CampusWiFiValidationService wifiValidationService; 추가/수정
대상: @Mock private CampusWiFiSettings campusWiFiSettings; @Mock private
RedisTemplate<String,Object> redisTemplate; @InjectMocks private
CampusWiFiValidationService wifiValidationService;), 그리고 필요 시 각 의존성에 대한
Mockito.when(...) 스텁을 설정해 실제 서비스 메서드 호출을 검증하도록 테스트를 수정하세요.
🧹 Nitpick comments (14)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (2)
73-88: 테스트 초기화 로직에 대한 개선 제안
System.out.println대신 SLF4J 로거 사용을 권장합니다.@BeforeEach와@Transactional조합 시, 트랜잭션이 각 테스트 메서드와 별도로 관리되어 예상치 못한 동작이 발생할 수 있습니다. DDL 확인만 필요하다면@Transactional제거를 고려해 주세요.🔎 로깅 개선 제안
+import lombok.extern.slf4j.Slf4j; +@Slf4j public abstract class BaseIntegrationTest { // ... @BeforeEach - @Transactional void ensureTablesExist() { try { entityManager.createNativeQuery("SELECT 1").getSingleResult(); - System.out.println("EntityManager initialized - Hibernate DDL should be executed"); + log.debug("EntityManager initialized - Hibernate DDL should be executed"); jdbcTemplate.queryForObject("SELECT COUNT(*) FROM user LIMIT 1", Integer.class); - System.out.println("User table exists - DDL executed successfully"); + log.debug("User table exists - DDL executed successfully"); } catch (Exception e) { - System.out.println("Table check failed: " + e.getMessage()); + log.error("Table check failed: {}", e.getMessage()); throw new RuntimeException("Tables not created properly", e); } }
103-123: H2 분기 코드가 사용되지 않을 수 있습니다.
build.gradle에서 H2 의존성이 제거되었으므로, H2 관련 분기 로직(isH2체크 및 관련 쿼리)이 실행되지 않습니다. 코드 간결성을 위해 제거하거나, 향후 H2 사용 가능성을 위해 유지할지 결정해 주세요.src/test/resources/application-test.yml (1)
6-10:BaseIntegrationTest의@DynamicPropertySource와 중복 설정이 있습니다.
BaseIntegrationTest에서DynamicPropertySource를 통해 동일한 속성들을 동적으로 설정하고 있어, 이 YAML 설정은 런타임에 오버라이드됩니다. 혼란을 피하기 위해 둘 중 하나를 제거하거나, 각각의 역할을 명확히 문서화해 주세요.src/test/java/com/gpt/geumpumtabackend/unit/wifi/service/CampusWiFiValidationServiceTest.java (1)
174-189: NULL/빈 입력값 테스트 구조
isNull()과eq("")매처를 사용한 stubbing은 정상 동작하지만, 실제 서비스 로직의 null 처리를 검증하지 못합니다. 위의 근본적인 이슈가 해결되면 이 테스트도 실제 예외 처리 동작을 검증할 수 있습니다.src/test/java/com/gpt/geumpumtabackend/unit/config/TestWiFiMockConfig.java (1)
16-34: LGTM! 테스트용 WiFi Mock 설정테스트 환경에서 Wi-Fi 검증을 제어하기 위한 적절한 설정입니다.
@Primary를 통해 실제 빈을 대체합니다.기본 fallback 동작을 추가하면 예상치 못한 입력에 대한 테스트 안정성이 향상됩니다:
🔎 기본 fallback 동작 추가 제안
// 에러 시뮬레이션용 (특정 IP에서 에러 발생) when(mock.validateFromCache("error.test.ip", anyString())) .thenReturn(WiFiValidationResult.error("Redis 연결 실패 (Mock)")); + + // 기타 모든 요청에 대한 기본 동작 (invalid 반환) + when(mock.validateFromCache(anyString(), anyString())) + .thenReturn(WiFiValidationResult.invalid("알 수 없는 네트워크 (Mock)")); return mock;주의: Mockito는 가장 구체적인 매처가 우선되므로, 순서에 주의하거나 구체적 매처를 먼저 설정해야 합니다.
src/test/java/com/gpt/geumpumtabackend/integration/rank/service/DepartmentRankServiceIntegrationTest.java (1)
403-422: 테스트 헬퍼 메서드
createAndSaveUser와createCompletedStudySession헬퍼가 잘 정의되어 있습니다.StudySessionIntegrationTest.java의 헬퍼와 유사한 패턴을 따르고 있어 일관성이 있습니다.여러 통합 테스트에서 동일한 헬퍼 메서드를 사용하고 있다면,
BaseIntegrationTest나 별도의 테스트 유틸리티 클래스로 추출하여 코드 중복을 줄일 수 있습니다.src/test/java/com/gpt/geumpumtabackend/unit/study/service/StudySessionServiceTest.java (2)
96-96: 사용되지 않는 변수
startTime변수가 선언되었지만 사용되지 않습니다. 불필요한 코드이므로 제거하는 것이 좋습니다.🔎 사용되지 않는 변수 제거
Long userId = 1L; String gatewayIp = "192.168.10.1"; String clientIp = "192.168.10.100"; - LocalDateTime startTime = LocalDateTime.now(); StudyStartRequest request = new StudyStartRequest(gatewayIp, clientIp);Lines 120, 140에도 동일하게 적용됩니다.
Also applies to: 120-120, 140-140
281-287: 사용되지 않는 헬퍼 메서드
createTestStudySession메서드가 정의되어 있지만 테스트에서 사용되지 않습니다. 향후 사용 계획이 없다면 제거하는 것이 좋습니다.src/test/java/com/gpt/geumpumtabackend/integration/study/service/StudySessionIntegrationTest.java (2)
74-77: 예외 검증을 더 구체적으로 하면 좋겠습니다.
BusinessException의exceptionType필드도 함께 검증하면 어떤 종류의 실패인지 명확히 확인할 수 있습니다.PersonalRankServiceTest에서처럼hasFieldOrPropertyWithValue를 사용하는 것을 권장합니다.🔎 제안하는 수정
// When & Then - WiFi 검증 실패로 예외 발생 assertThatThrownBy(() -> studySessionService.startStudySession(request, testUser.getId())) - .isInstanceOf(BusinessException.class); + .isInstanceOf(BusinessException.class) + .hasFieldOrPropertyWithValue("exceptionType", ExceptionType.INVALID_CAMPUS_NETWORK); // 실제 예외 타입에 맞게 수정
120-123: IP 범위 경계값 테스트의 정확성을 확인해주세요.
172.30.64.0/18범위에서 네트워크 주소는172.30.64.0이고 브로드캐스트 주소는172.30.127.255입니다. 주석에서 "범위 내 첫 번째 IP"라고 한172.30.64.1은 실제로 두 번째 사용 가능한 호스트 IP입니다. 네트워크 주소(172.30.64.0)와 브로드캐스트 주소(172.30.127.255)도 경계값 테스트에 포함하면 더 완전한 테스트가 됩니다.src/test/java/com/gpt/geumpumtabackend/unit/rank/service/PersonalRankServiceTest.java (4)
131-162: 완료된 일간 랭킹 테스트의 확장을 고려해보세요.현재 정상 조회 케이스만 있습니다. 사용자가 랭킹에 없는 경우의 fallback 처리나 데이터가 없는 날짜 조회 등의 테스트도 추가하면 커버리지가 향상됩니다.
168-189: 주간 기간 계산 검증이 누락되어 있습니다.테스트 이름은 "월요일부터 일요일까지의 기간으로 계산된다"고 하지만, 실제로 전달되는 날짜 범위를 검증하지 않습니다.
ArgumentCaptor를 사용하여calculateCurrentPeriodRanking에 전달되는 시작/종료 시간이 올바른지 검증하면 테스트의 신뢰도가 높아집니다.🔎 ArgumentCaptor 사용 예시
@Test @DisplayName("현재 주간 랭킹 조회 시 월요일부터 일요일까지의 기간으로 계산된다") void getCurrentWeekly_정상조회_주간기간계산() { // Given Long userId = 1L; List<PersonalRankingTemp> mockRankingData = List.of( createMockPersonalRankingTemp(1L, "김철수", "profile1.jpg", "SOFTWARE", 25200000L, 1L) ); ArgumentCaptor<LocalDateTime> startCaptor = ArgumentCaptor.forClass(LocalDateTime.class); ArgumentCaptor<LocalDateTime> endCaptor = ArgumentCaptor.forClass(LocalDateTime.class); given(studySessionRepository.calculateCurrentPeriodRanking(startCaptor.capture(), endCaptor.capture(), any())) .willReturn(mockRankingData); // When PersonalRankingResponse response = personalRankService.getCurrentWeekly(userId); // Then LocalDateTime capturedStart = startCaptor.getValue(); LocalDateTime capturedEnd = endCaptor.getValue(); // 시작일이 월요일인지 검증 assertThat(capturedStart.getDayOfWeek()).isEqualTo(DayOfWeek.MONDAY); // 종료일이 일요일인지 검증 assertThat(capturedEnd.getDayOfWeek()).isEqualTo(DayOfWeek.SUNDAY); }
309-326: Reflection 코드 중복을 제거하면 좋겠습니다.
createTestUser헬퍼 메서드와 동일한 Reflection 로직이 중복됩니다. 헬퍼 메서드가nulldepartment를 지원하도록 수정하거나, ID 설정을 위한 별도의 유틸리티 메서드를 추출하면 중복을 줄일 수 있습니다.🔎 헬퍼 메서드 활용 예시
// 기존 createTestUser를 nullable department로 수정 private User createTestUser(Long id, String name, Department department) { User user = User.builder() .name(name) .email("test@kumoh.ac.kr") .department(department) // null 허용 // ... 나머지 필드 .build(); setIdViaReflection(user, id); return user; } private void setIdViaReflection(User user, Long id) { try { java.lang.reflect.Field idField = User.class.getDeclaredField("id"); idField.setAccessible(true); idField.set(user, id); } catch (Exception e) { throw new RuntimeException("Failed to set test user ID", e); } } // 테스트에서 사용 User userWithNoDepartment = createTestUser(userId, "무소속사용자", null);
374-394: 동일한 테스트 케이스가 중복되어 있습니다.이 테스트(
랭킹에도_없고_DB에도_없는_사용자에게_USER_NOT_FOUND_예외가_발생한다)는GetCurrentDaily클래스의getCurrentDaily_사용자없음_예외발생테스트와 동일한 시나리오를 검증합니다. 하나로 통합하거나,ExceptionHandling클래스에서는 다른 메서드(예:getCurrentWeekly,getCurrentMonthly)에 대한 예외 처리를 테스트하는 것을 고려해보세요.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
build.gradlesrc/main/java/com/gpt/geumpumtabackend/global/config/redis/RedisConfig.javasrc/main/java/com/gpt/geumpumtabackend/rank/dto/PersonalRankingTemp.javasrc/main/java/com/gpt/geumpumtabackend/rank/service/DepartmentRankService.javasrc/main/java/com/gpt/geumpumtabackend/statistics/controller/StatisticsController.javasrc/main/java/com/gpt/geumpumtabackend/study/api/StudySessionApi.javasrc/main/java/com/gpt/geumpumtabackend/study/controller/StudySessionController.javasrc/main/java/com/gpt/geumpumtabackend/study/repository/StudySessionRepository.javasrc/main/java/com/gpt/geumpumtabackend/study/service/StudySessionService.javasrc/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.javasrc/test/java/com/gpt/geumpumtabackend/integration/rank/service/DepartmentRankServiceIntegrationTest.javasrc/test/java/com/gpt/geumpumtabackend/integration/study/service/StudySessionIntegrationTest.javasrc/test/java/com/gpt/geumpumtabackend/unit/config/BaseUnitTest.javasrc/test/java/com/gpt/geumpumtabackend/unit/config/TestWiFiMockConfig.javasrc/test/java/com/gpt/geumpumtabackend/unit/rank/service/DepartmentRankServiceTest.javasrc/test/java/com/gpt/geumpumtabackend/unit/rank/service/PersonalRankServiceTest.javasrc/test/java/com/gpt/geumpumtabackend/unit/study/service/StudySessionServiceTest.javasrc/test/java/com/gpt/geumpumtabackend/unit/wifi/service/CampusWiFiValidationServiceTest.javasrc/test/resources/application-test.yml
💤 Files with no reviewable changes (3)
- src/main/java/com/gpt/geumpumtabackend/study/api/StudySessionApi.java
- src/main/java/com/gpt/geumpumtabackend/statistics/controller/StatisticsController.java
- src/main/java/com/gpt/geumpumtabackend/study/controller/StudySessionController.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-11T11:48:29.664Z
Learnt from: kon28289
Repo: Geumpumta/backend PR: 16
File: src/main/java/com/gpt/geumpumtabackend/study/repository/StudySessionRepository.java:264-287
Timestamp: 2025-11-11T11:48:29.664Z
Learning: In the Geumpumta backend project, study session statistics (daily, weekly, monthly, grass) intentionally exclude in-progress sessions where end_time is NULL. Only completed sessions (with non-null end_time) should be counted in the statistics calculations.
Applied to files:
src/main/java/com/gpt/geumpumtabackend/study/service/StudySessionService.javasrc/main/java/com/gpt/geumpumtabackend/study/repository/StudySessionRepository.java
🧬 Code graph analysis (3)
src/test/java/com/gpt/geumpumtabackend/unit/config/BaseUnitTest.java (1)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (1)
SpringBootTest(24-146)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (1)
src/test/java/com/gpt/geumpumtabackend/unit/config/BaseUnitTest.java (1)
SpringBootTest(6-13)
src/test/java/com/gpt/geumpumtabackend/unit/study/service/StudySessionServiceTest.java (3)
src/test/java/com/gpt/geumpumtabackend/unit/rank/service/DepartmentRankServiceTest.java (5)
Nested(44-324)Nested(109-141)Nested(143-172)Nested(174-203)Nested(205-286)src/test/java/com/gpt/geumpumtabackend/unit/rank/service/PersonalRankServiceTest.java (6)
Nested(46-129)Nested(131-162)Nested(164-190)Nested(192-218)Nested(220-368)Nested(370-395)src/test/java/com/gpt/geumpumtabackend/unit/wifi/service/CampusWiFiValidationServiceTest.java (3)
Nested(25-87)Nested(89-151)Nested(153-190)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (28)
src/main/java/com/gpt/geumpumtabackend/rank/dto/PersonalRankingTemp.java (1)
24-34: LGTM! 방어적인 예외 처리가 잘 구현되었습니다.
getDepartmentKoreanName()메서드가 null 체크와IllegalArgumentException처리를 통해 안전하게 enum 변환을 수행합니다. 잘못된 값에 대해 원본 문자열을 반환하는 fallback 로직도 적절합니다.src/main/java/com/gpt/geumpumtabackend/rank/service/DepartmentRankService.java (2)
100-109: LGTM! 0시간 학과 필터링 로직이 적절합니다.공부 시간이 0인 학과는
topRankings에서 제외하면서, 사용자의 학과는 공부 시간과 관계없이myRanking으로 찾도록 분리된 로직이 잘 구현되었습니다.
111-118: 폴백 순위 계산이 필터링된 목록 크기를 올바르게 사용합니다.활성 학과(공부 시간 > 0)만 포함된
topRankings.size() + 1을 사용하여 사용자 학과의 순위를 계산하는 것이 필터링 로직과 일관됩니다.build.gradle (1)
74-76: Java 21 호환성을 위한 JVM 인자가 적절합니다.
-XX:+EnableDynamicAgentLoading은 Java 21에서 Mockito inline mock maker가 정상 작동하도록 필수적인 설정입니다.src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (2)
34-62: TestContainers 설정이 잘 구성되었습니다.MySQL과 Redis 컨테이너 설정, 그리고
@DynamicPropertySource를 통한 동적 속성 주입이 표준 패턴을 따르고 있습니다. WiFi 테스트 설정도 함께 구성되어 통합 테스트 환경이 완성되었습니다.
140-145: Redis 캐시 정리가 적절합니다.
try-with-resources를 사용하여 연결을 자동으로 닫고,flushAll()로 테스트 간 격리를 보장합니다.getConnectionFactory()가 null을 반환할 가능성은 테스트 컨텍스트에서 낮지만, 방어적 null 체크를 추가하면 더 안전합니다.src/main/java/com/gpt/geumpumtabackend/global/config/redis/RedisConfig.java (2)
16-31: 생성자 주입과 조건부 비밀번호 설정이 잘 구현되었습니다.
@RequiredArgsConstructor를 통한RedisProperties주입은@Value필드 주입보다 테스트하기 쉽고 명시적입니다. 비밀번호가 비어있을 때 설정하지 않는 방어 로직도 적절합니다.
37-52: RedisTemplate 설정이 개선되었습니다.
RedisConnectionFactory를 파라미터로 주입받아 테스트 용이성이 향상되었습니다. 단일StringRedisSerializer인스턴스를 재사용하여 메모리 효율도 개선되었습니다.src/test/resources/application-test.yml (2)
6-10: TestContainers 데이터소스 설정이 올바릅니다.
jdbc:tc:mysql:8.0:///geumpumta-testURL 형식이 TestContainers JDBC 드라이버에 적합합니다. 빈 비밀번호는 TestContainers MySQL 기본 설정과 호환됩니다.
100-112: WiFi 테스트 설정이 적절합니다.캠퍼스 WiFi 검증을 위한 테스트 네트워크 설정이 완비되었습니다. CIDR 범위
172.30.64.0/18과 게이트웨이172.30.64.1이 일관되게 구성되어 있습니다.src/main/java/com/gpt/geumpumtabackend/study/repository/StudySessionRepository.java (2)
51-51: LGTM! 진행 중인 세션의 시간 계산 경계 조건 수정
end_time IS NULL인 진행 중 세션에 대해LEAST(:now, :periodEnd)를 사용하여 현재 시간과 기간 종료 시간 중 더 작은 값을 사용하도록 수정되었습니다. 이는 기간 종료 시간을 초과하는 시간이 집계되는 것을 방지합니다.Also applies to: 61-61, 76-76
156-156: LGTM! 학과 랭킹 쿼리에도 동일한 경계 조건 수정이 일관되게 적용됨
calculateCurrentDepartmentRanking쿼리에서도 개인 랭킹과 동일하게LEAST(:now, :periodEnd)로직이 적용되어 일관성이 유지됩니다.Also applies to: 168-168
src/main/java/com/gpt/geumpumtabackend/study/service/StudySessionService.java (1)
77-83: LGTM! WiFi 검증 예외 매핑 로직
mapWiFiValidationException메서드에서INVALID,ERROR,default케이스를 적절히 처리하고 있습니다. Switch expression을 활용한 깔끔한 구현입니다.src/test/java/com/gpt/geumpumtabackend/integration/rank/service/DepartmentRankServiceIntegrationTest.java (3)
116-117: 세션 종료 시간이 시작 시간보다 이전입니다
oneHourAgo(현재 - 1시간)가 시작 시간이고thirtyMinAgo(현재 - 30분)가 종료 시간으로 설정되어 있어 종료 시간이 시작 시간보다 이후입니다. 코멘트에는 "30분"이라고 되어 있지만 실제로는 30분 학습이 됩니다.코멘트와 실제 동작이 일치하지만, 변수명이 혼란스러울 수 있습니다. 의도가 맞는지 확인해 주세요:
oneHourAgo→thirtyMinAgo: 이동 시간 = 30분 (정상)
49-56: LGTM! 테스트 사용자 설정
@BeforeEach에서 여러 학과의 테스트 사용자를 생성하여 다양한 시나리오를 검증할 수 있도록 준비합니다.
299-334: 상위 30명 집계 로직 테스트Top-30 제한 검증을 위해 35명의 사용자를 생성하고 등차수열 합 공식을 사용한 기대값 계산이 정확합니다:
- (350 + 60) * 30 / 2 = 6,150분 = 369,000,000ms
src/test/java/com/gpt/geumpumtabackend/unit/rank/service/DepartmentRankServiceTest.java (1)
289-310: Reflection을 사용한 ID 설정 헬퍼JPA 엔티티의 ID를 테스트에서 설정하기 위해 Reflection을 사용하는 것은 일반적인 패턴입니다.
PersonalRankServiceTest와 동일한 패턴을 따르고 있어 일관성이 있습니다.src/test/java/com/gpt/geumpumtabackend/unit/study/service/StudySessionServiceTest.java (3)
37-47: LGTM! 적절한 Mock 구조
@Mock으로 의존성(Repository, Service)을 선언하고@InjectMocks로 테스트 대상 서비스를 주입받는 올바른 단위 테스트 구조입니다.
163-254: LGTM! 공부시간 계산 로직 테스트도메인 객체(
StudySession)의 시간 계산 로직을 다양한 시나리오(정상, 1초, 12시간, 자정 경과)로 검증합니다. 경계값 테스트가 잘 포함되어 있습니다.
156-157: Heartbeat 관련 주석Heartbeat 기능 제거에 대한 문서화 주석이 잘 작성되어 있어 코드 이력 이해에 도움이 됩니다.
src/test/java/com/gpt/geumpumtabackend/integration/study/service/StudySessionIntegrationTest.java (4)
21-33: LGTM!클래스 구조와 의존성 주입이 적절하게 구성되어 있습니다.
BaseIntegrationTest를 상속하여 Testcontainers 기반 통합 테스트 인프라를 활용하고 있습니다.
57-63: 동일 사용자가 연속으로 세션을 시작하는 동작을 확인해주세요.두 번째
startStudySession호출이 첫 번째 세션을 종료하지 않고 새 세션을 생성하는 것이 의도된 동작인지 확인이 필요합니다. 일반적으로 사용자당 하나의 활성 세션만 허용하거나, 기존 세션을 자동 종료하는 로직이 있을 수 있습니다.
92-112: 캐시 동작 테스트가 잘 구성되어 있습니다.캐시 미스와 히트를 명확히 구분하여 검증하고 있습니다. 다만 메시지 문자열에 의존하는 방식은 메시지 변경 시 테스트가 깨질 수 있으므로, 가능하다면 별도의 캐시 히트 여부를 나타내는 플래그를 응답에 추가하는 것도 고려해볼 수 있습니다.
140-152: LGTM!헬퍼 메서드가 고유한
providerId를 생성하여 테스트 격리를 보장합니다.DepartmentRankServiceIntegrationTest의 패턴과 일관성 있게 구현되어 있습니다.src/test/java/com/gpt/geumpumtabackend/unit/rank/service/PersonalRankServiceTest.java (4)
30-44: LGTM!Mockito 확장을 사용한 단위 테스트 설정이 적절합니다. 필요한 의존성들이 올바르게 모킹되어 있습니다.
50-76: LGTM!정상 조회 테스트가 잘 구성되어 있습니다. 응답의 주요 필드들을 적절히 검증하고 있으며, repository 호출도 verify하고 있습니다.
420-434: **createMockPersonalRankingTemp에서 잘못된 department 문자열 처리가 필요합니다.**현재 테스트 코드에서는department파라미터가 항상 유효한Departmentenum 값("SOFTWARE", "COMPUTER_ENGINEERING" 등)으로 전달되지만,IllegalArgumentException은 "메서드에 적절하지 않은 인자가 전달될 때 발생하는 RuntimeException"이므로, 잘못된 문자열이 전달될 경우Department.valueOf(department)에서 예외가 발생합니다. 테스트 코드이므로 현재는 문제없지만, 방어적 코딩 관점에서 참고하세요.
397-418: LGTM!
createTestUser헬퍼 메서드가 잘 구현되어 있습니다. Reflection을 통한 ID 설정은 JPA 엔티티 테스트에서 일반적인 패턴입니다.
src/main/java/com/gpt/geumpumtabackend/study/service/StudySessionService.java
Show resolved
Hide resolved
src/test/java/com/gpt/geumpumtabackend/unit/rank/service/DepartmentRankServiceTest.java
Show resolved
Hide resolved
src/test/java/com/gpt/geumpumtabackend/unit/rank/service/PersonalRankServiceTest.java
Show resolved
Hide resolved
src/test/java/com/gpt/geumpumtabackend/unit/wifi/service/CampusWiFiValidationServiceTest.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
Fix all issues with AI Agents 🤖
In
@src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java:
- Around line 91-106: In ensureTablesExist(), the raw SQL uses the reserved word
user and prints to stdout; update the jdbcTemplate query to quote the table name
(e.g., wrap with backticks or use the dialect-appropriate quoted identifier)
when calling jdbcTemplate.queryForObject("SELECT COUNT(*) FROM `user` LIMIT 1",
Integer.class) and replace System.out.println calls in ensureTablesExist() with
a logger (e.g., a private static final Logger) and use logger.info/error in the
try/catch and when reporting failures.
🧹 Nitpick comments (4)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (4)
71-72: Redis 포트 타입 확인이 필요합니다.
spring.data.redis.port는 일반적으로Integer타입을 기대하지만, 여기서는toString()으로String을 반환하고 있습니다. Spring의 타입 변환이 처리해주지만, 명시적으로Integer를 반환하는 것이 더 안전합니다.🔎 수정 제안
- registry.add("spring.data.redis.port", () -> redisContainer.getMappedPort(6379).toString()); + registry.add("spring.data.redis.port", () -> redisContainer.getMappedPort(6379));
114-156: H2 지원 코드가 포함되어 있지만, 이 클래스는 MySQL 컨테이너만 사용합니다.
truncateAllTables()메서드에 H2 데이터베이스 지원 로직이 포함되어 있습니다. 그러나BaseIntegrationTest는 MySQL Testcontainer만 사용하므로 H2 분기 코드는 실행되지 않습니다.
BaseUnitTest에서 H2를 사용한다면 해당 클래스에서 별도로 정리 로직을 구현하거나, 이 코드를 공통 유틸리티로 분리하는 것이 더 명확합니다. 현재 구조에서는 불필요한 복잡성을 추가합니다.
158-163:getConnectionFactory()가 null을 반환할 수 있습니다.
redisTemplate.getConnectionFactory()는 null을 반환할 수 있으며, 이 경우NullPointerException이 발생합니다. 테스트 환경에서는 발생 가능성이 낮지만, 방어적 코딩을 권장합니다.🔎 수정 제안
private void cleanRedisCache() { // Redis의 모든 캐시 데이터 삭제 (Connection을 try-with-resources로 자동 close) - try (var connection = redisTemplate.getConnectionFactory().getConnection()) { - connection.serverCommands().flushAll(); + var connectionFactory = redisTemplate.getConnectionFactory(); + if (connectionFactory != null) { + try (var connection = connectionFactory.getConnection()) { + connection.serverCommands().flushAll(); + } } }
43-49: MySQL 8.0에서mysql_native_password는 deprecated 상태이며, 향후 업그레이드 시 주의가 필요합니다.
mysql_native_password는 MySQL 8.0.34부터 deprecated되었으며, MySQL 8.4에서는 기본적으로 비활성화되고, MySQL 9.0에서는 완전히 제거됩니다. 테스트 컨테이너는 현재 MySQL 8.0을 사용하므로 기능상 문제는 없지만, 향후 MySQL 버전 업그레이드 시를 대비하여caching_sha2_password로 마이그레이션하는 것이 권장됩니다.또한
withReuse(true)가 작동하려면~/.testcontainers.properties파일(사용자 홈 디렉토리)에testcontainers.reuse.enable=true설정이 필요합니다. 이 설정이 없으면 매번 새로운 컨테이너가 생성됩니다. 단, 컨테이너 재사용은 로컬 개발 환경용이므로 CI 환경에서는 이를 비활성화하는 것이 좋습니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.javasrc/test/resources/application-test.yml
🚧 Files skipped from review as they are similar to previous changes (1)
- src/test/resources/application-test.yml
🧰 Additional context used
🧬 Code graph analysis (1)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (1)
src/test/java/com/gpt/geumpumtabackend/unit/config/BaseUnitTest.java (1)
SpringBootTest(6-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (1)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (1)
33-41: Testcontainers 기반 통합 테스트 인프라 구성이 잘 되어 있습니다.정적 컨테이너를 사용하여 테스트 클래스 간 재사용을 활성화하고,
@DynamicPropertySource로 동적 속성 바인딩을 구현한 것은 좋은 패턴입니다.create-dropDDL 전략과@AfterEach정리 로직을 통해 테스트 격리가 잘 보장됩니다.
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (2)
38-44: MySQL 인증 플러그인 설정 확인 필요
--default-authentication-plugin=mysql_native_password는 MySQL 8.0.34부터 deprecated되었습니다. MySQL 8.0 기본 인증은caching_sha2_password이며, MySQL Connector/J 8.0.x는 이를 지원합니다.현재 테스트 환경에서는 동작하지만, 향후 호환성을 위해 해당 옵션 제거를 고려해보세요.
🔎 제안
@Container static final MySQLContainer<?> mysqlContainer = new MySQLContainer<>("mysql:8.0") .withDatabaseName("test_geumpumta") .withUsername("test") .withPassword("test") - .withReuse(true) - .withCommand("--default-authentication-plugin=mysql_native_password"); + .withReuse(true);
83-125: H2 분기 로직 필요성 검토이 클래스는 Testcontainers의 MySQL 컨테이너를 사용하는 통합 테스트 베이스입니다. H2 관련 분기 로직(lines 93-94, 100-104, 117-118)은 실제로 실행되지 않을 것으로 보입니다.
단위 테스트는 별도의
BaseUnitTest를 사용하므로, 코드 간소화를 위해 H2 분기를 제거하는 것을 고려해보세요. 다만, 로컬 개발 환경에서 H2 fallback이 필요한 경우라면 현재 구조를 유지하셔도 됩니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java
🧰 Additional context used
🧬 Code graph analysis (1)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (1)
src/test/java/com/gpt/geumpumtabackend/unit/config/BaseUnitTest.java (1)
SpringBootTest(6-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (4)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (4)
28-36: LGTM!테스트 설정이 적절합니다.
spring.test.database.replace=NONE으로 DataSource 자동 교체를 비활성화하고 Testcontainers의 MySQL을 사용하도록 올바르게 구성되었습니다.
51-69: LGTM!컨테이너 속성을 Spring 환경에 동적으로 바인딩하는 방식이 적절합니다. WiFi 테스트 설정도 함께 포함되어 있어 통합 테스트에서 필요한 모든 속성이 구성됩니다.
77-81: LGTM!
@AfterEach를 통한 테스트 후 정리 로직이 테스트 격리를 보장합니다. 테이블 truncate 후 Redis 캐시를 정리하는 순서도 적절합니다.
127-132: LGTM!Redis 연결을 try-with-resources로 관리하여 리소스 누수를 방지합니다.
flushAll()을 통해 테스트 간 Redis 데이터 격리가 보장됩니다.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
.github/workflows/prod-ci.yml (1)
18-27: 워크플로우에서 정의된 Redis 서비스 컨테이너를 제거하세요.18-27번 라인의 Redis 서비스는 실제 테스트에서 사용되지 않습니다. 통합 테스트는 Testcontainers를 통해 자체 Redis 컨테이너를 생성하고
@DynamicPropertySource로 해당 컨테이너의 연결 정보를 주입받으므로, 워크플로우에 정의된 서비스는 불필요한 리소스 낭비입니다. 이 서비스 정의를 제거하면 CI 성능을 개선할 수 있습니다..github/workflows/dev-ci.yml (1)
20-28: 워크플로우의 Redis 서비스는 사용되지 않습니다.BaseIntegrationTest는 Testcontainers를 통해 Redis를 동적으로 프로비저닝하며, @DynamicPropertySource로 Redis 연결 정보를 주입합니다(redisContainer.getMappedPort(6379) 사용). 워크플로우 레벨의 redis 서비스는 빌드 중 실행되는 어떤 테스트에서도 참조되지 않습니다. 단위 테스트(unit-test 프로필)는 Redis를 완전히 비활성화합니다.
이 서비스 정의를 제거하면 CI 빌드 시간을 단축하고 리소스를 절약할 수 있습니다.
🧹 Nitpick comments (2)
src/test/java/com/gpt/geumpumtabackend/GeumpumtaBackendApplicationTests.java (1)
6-10: 단순 컨텍스트 로드 테스트에 통합 테스트 인프라 사용을 재고하세요.
BaseIntegrationTest를 상속하면 MySQL과 Redis Testcontainers가 시작되므로, 단순히 Spring 컨텍스트가 로드되는지 확인하는 이 smoke test의 실행 시간이 크게 증가합니다.빠른 피드백을 위해 다음 방안을 고려해보세요:
- 이 테스트는
BaseIntegrationTest상속을 제거하고@SpringBootTest(webEnvironment = WebEnvironment.NONE)같은 가벼운 설정을 사용- 또는 실제 통합 테스트 검증이 필요한 경우에만
BaseIntegrationTest사용🔎 제안: 가벼운 smoke test로 리팩토링
package com.gpt.geumpumtabackend; -import com.gpt.geumpumtabackend.integration.config.BaseIntegrationTest; import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -class GeumpumtaBackendApplicationTests extends BaseIntegrationTest { +@SpringBootTest(webEnvironment = WebEnvironment.NONE) +class GeumpumtaBackendApplicationTests { @Test void contextLoads() { } }.github/workflows/dev-ci.yml (1)
54-56: CI 환경에서 Ryuk 비활성화를 고려하세요.Testcontainers의 Ryuk 컨테이너는 테스트 후 정리 작업을 수행하지만, GitHub Actions와 같은 CI 환경에서는 Docker-in-Docker 권한 문제로 인해 간헐적으로 실패할 수 있습니다. GitHub Actions는 작업 완료 후 자동으로 컨테이너를 정리하므로, CI 환경에서는
TESTCONTAINERS_RYUK_DISABLED: true로 설정하는 것이 일반적인 모범 사례입니다.🔎 권장 수정사항
env: - TESTCONTAINERS_RYUK_DISABLED: false + TESTCONTAINERS_RYUK_DISABLED: true TESTCONTAINERS_CHECKS_DISABLE: false
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
.github/workflows/dev-ci.yml.github/workflows/prod-ci.ymlsrc/test/java/com/gpt/geumpumtabackend/GeumpumtaBackendApplicationTests.javasrc/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
- src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
There was a problem hiding this comment.
Actionable comments posted: 1
Fix all issues with AI Agents 🤖
In
@src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java:
- Around line 80-85: Remove the @Transactional annotation from the cleanUp()
method in BaseIntegrationTest (the method named cleanUp() annotated with
@AfterEach) because TRUNCATE (DDL) implicitly commits in MySQL and cannot be
rolled back; leave @AfterEach, keep calling truncateAllTables() and
cleanRedisCache(), and ensure any required transaction management for test
setup/teardown is handled separately (e.g., in the methods truncateAllTables or
test utilities) rather than using @Transactional on cleanUp().
🧹 Nitpick comments (1)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (1)
87-129: H2 데이터베이스 지원 로직이 불필요할 수 있습니다.이 클래스는 MySQL TestContainers를 사용하는 통합 테스트 전용입니다. H2 처리 로직(lines 94, 97-98, 104-108, 121-122)은 복잡도를 증가시키지만 실제로 사용되지 않습니다. 단위 테스트는 별도의
BaseUnitTest를 통해 H2를 사용합니다.또한 디버깅을 위해 로거 추가를 권장합니다.
🔎 리팩토링 제안
1단계: H2 처리 로직 제거
private void truncateAllTables() { try { - // Connection을 try-with-resources로 자동 close - String dbProductName; - try (var connection = jdbcTemplate.getDataSource().getConnection()) { - dbProductName = connection.getMetaData().getDatabaseProductName(); - } - boolean isH2 = "H2".equalsIgnoreCase(dbProductName); - // 외래 키 제약 조건 비활성화 - if (isH2) { - jdbcTemplate.execute("SET REFERENTIAL_INTEGRITY FALSE"); - } else { - jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0"); - } + jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0"); - List<String> tableNames; - if (isH2) { - tableNames = jdbcTemplate.queryForList( - "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PUBLIC' AND TABLE_TYPE = 'BASE TABLE'", - String.class - ); - } else { - tableNames = jdbcTemplate.queryForList( - "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE'", - String.class - ); - } + List<String> tableNames = jdbcTemplate.queryForList( + "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE'", + String.class + ); for (String tableName : tableNames) { jdbcTemplate.execute("TRUNCATE TABLE `" + tableName + "`"); } // 외래 키 제약 조건 재활성화 - if (isH2) { - jdbcTemplate.execute("SET REFERENTIAL_INTEGRITY TRUE"); - } else { - jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1"); - } + jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1"); } catch (Exception e) { throw new RuntimeException("Failed to truncate tables", e); } }2단계: 로거 추가 (선택사항)
클래스 상단에 로거 추가:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class BaseIntegrationTest { private static final Logger log = LoggerFactory.getLogger(BaseIntegrationTest.class);그런 다음 예외 처리에서 로깅 사용:
} catch (Exception e) { + log.error("Failed to truncate tables", e); throw new RuntimeException("Failed to truncate tables", e); }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
build.gradlesrc/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
- build.gradle
🧰 Additional context used
🧬 Code graph analysis (1)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (1)
src/test/java/com/gpt/geumpumtabackend/unit/config/BaseUnitTest.java (1)
SpringBootTest(6-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (2)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (2)
41-72: TestContainers 설정이 잘 구성되어 있습니다.컨테이너 재사용을 위한 static 필드, CI 환경을 고려한 충분한 타임아웃 설정, 그리고 동적 프로퍼티 바인딩이 적절하게 구현되었습니다.
131-136: Redis 캐시 정리 로직이 올바르게 구현되었습니다.try-with-resources를 사용한 연결 관리와
flushAll()호출이 적절합니다.
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java
Show resolved
Hide resolved
src/test/java/com/gpt/geumpumtabackend/unit/study/service/StudySessionServiceTest.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
.github/workflows/dev-ci.yml (2)
1-1: 워크플로우 이름이 실제 동작과 일치하지 않습니다.이제 테스트가 별도의 job으로 추가되었으므로, 워크플로우 이름을
"Dev - CI (Test, Build & Push)"로 업데이트하는 것이 적절합니다. PR 설명에도 이렇게 명시되어 있습니다.🔎 제안하는 수정
-name: Dev - CI (Build & Push) +name: Dev - CI (Test, Build & Push)
15-18: GRADLE_OPTS 설정이 docker job에도 영향을 줍니다.전역 환경 변수로
workers.max=1을 설정하면dockerjob의bootJar빌드에도 적용되어 빌드 속도가 저하될 수 있습니다. 테스트 안정성이 필요한testjob에만 적용하는 것이 더 효율적입니다.🔎 제안하는 수정
전역 env 블록을 제거하고 test job의 Run tests 단계에만 적용:
-env: - # CI에서 Testcontainers 안정성 확보(테스트 병렬 워커 제한) - GRADLE_OPTS: "-Dorg.gradle.workers.max=1" - jobs: test:- name: Run tests - run: ./gradlew clean test --no-daemon --info + run: ./gradlew clean test --no-daemon --info -Dorg.gradle.workers.max=1
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.github/workflows/dev-ci.ymlsrc/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.javasrc/test/resources/application-test.yml
🚧 Files skipped from review as they are similar to previous changes (2)
- src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java
- src/test/resources/application-test.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test (Gradle + Testcontainers)
🔇 Additional comments (3)
.github/workflows/dev-ci.yml (3)
43-72: 테스트 인프라 구성이 잘 되어 있습니다.Docker sanity check, 이미지 사전 pull, 실패 시 상태 덤프 등 Testcontainers 기반 테스트 환경을 위한 적절한 설정입니다. 특히 실패 시 디버깅을 위한 로그 덤프가 유용합니다.
74-104: Docker job 구성이 적절합니다.
needs: test로 테스트 통과 후에만 빌드가 진행되고,-x test로 테스트 재실행을 방지하는 것이 효율적입니다. PR 이벤트에서 빌드/푸시를 건너뛰는 것도 적절합니다.
127-137: 멀티 아키텍처 빌드 및 캐시 설정이 잘 되어 있습니다.
linux/amd64,linux/arm64플랫폼 지원과 GHA 캐시(cache-from,cache-to)를 통한 빌드 최적화가 적절하게 구성되어 있습니다.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @.github/workflows/dev-ci.yml:
- Around line 20-22: Add a timeout to the GitHub Actions jobs to avoid
long-running CI: set the timeout-minutes field on the test job (the job named
"test") to a sensible limit (e.g., 30–60) and likewise add timeout-minutes to
the Docker-related job (e.g., the job named "docker" or any job running
container setup/testcontainers) so both jobs will be automatically cancelled
after the specified minutes.
- Around line 55-58: Remove the CI step titled "Setup TestContainers
configuration" that creates ~/.testcontainers and writes
"testcontainers.reuse.enable=true"; delete that entire step from the workflow so
CI does not enable Testcontainers reuse (avoid testcontainers.reuse.enable=true)
and ensure no other workflow steps recreate that file or property.
🧹 Nitpick comments (1)
.github/workflows/dev-ci.yml (1)
15-17: Gradle 워커 제한의 필요성 검증 및 성능 영향 평가가 필요합니다.현재
GRADLE_OPTS: "-Dorg.gradle.workers.max=1"은 의도적으로 Testcontainers 안정성을 위해 추가되었으나, 단일 워커로 제한할 경우 테스트 실행 시간이 크게 증가할 수 있습니다.다음 항목의 검증을 권장합니다:
- 실제 CI 환경에서 이 제한이 필수적인지 확인 (과거 안정성 문제가 해결되었을 가능성)
--max-workers=2또는--max-workers=4로 제한적 병렬화를 시도하여 성능/안정성 균형 평가- Testcontainers 1.19.3 버전에서 병렬 실행 관련 알려진 문제가 있는지 확인
참고:
src/test/resources/testcontainers.properties의 주석에 "CI environments will ignore this setting"이라고 명시되어 있으나, 워크플로우 스크립트에서~/.testcontainers.properties를 다시 생성하므로 이 설정은 무시됩니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/dev-ci.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test (Gradle + Testcontainers)
🔇 Additional comments (2)
.github/workflows/dev-ci.yml (2)
80-88: Docker 작업 구조가 적절합니다.Docker 빌드 및 푸시 작업이 올바르게 구성되었습니다:
- 테스트 통과 후에만 실행 (
needs: test)- PR 이벤트에서는 실행되지 않음 (불필요한 이미지 푸시 방지)
- 테스트와 빌드/배포의 관심사 분리가 명확함
49-52: 사전 다운로드하는 이미지 버전이 테스트 코드와 일치합니다.
BaseIntegrationTest.java에서 테스트에 사용되는 이미지 버전은mysql:8.0과redis:7.0-alpine으로, CI 워크플로우에서 사전 다운로드하는 버전과 정확히 일치합니다. 이 단계는 적절하고 효과적입니다.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
@src/test/java/com/gpt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.java:
- Around line 124-141: The DepartmentRankController.getDailyRanking method is
missing the @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) annotation on
its LocalDateTime request parameter, causing deserialization failures; update
the method signature in DepartmentRankController.getDailyRanking to add
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) on the date parameter
(matching PersonalRankController.getDailyRanking) so the incoming date query
param parses correctly.
🧹 Nitpick comments (3)
.github/workflows/prod-ci.yml (1)
60-64: TestContainers 설정 파일 경로 불일치
~/.testcontainers디렉토리를 생성하지만 설정 파일은~/.testcontainers.properties로 홈 디렉토리에 직접 생성됩니다. TestContainers의 기본 설정 파일 위치는~/.testcontainers.properties가 맞으므로 디렉토리 생성은 불필요합니다.🔎 수정 제안
# TestContainers 설정 파일 생성 (컨테이너 재사용 활성화) - name: Setup TestContainers configuration run: | - mkdir -p ~/.testcontainers echo "testcontainers.reuse.enable=true" > ~/.testcontainers.propertiessrc/test/java/com/gpt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.java (1)
39-40: 사용되지 않는 ObjectMapper 필드
objectMapper필드가 autowired 되어 있지만 이 테스트 클래스에서 사용되지 않습니다. 불필요한 의존성 주입을 제거하세요.🔎 수정 제안
@Autowired private MockMvc mockMvc; - @Autowired - private ObjectMapper objectMapper; - @Autowired private JwtHandler jwtHandler;src/test/java/com/gpt/geumpumtabackend/integration/study/controller/StudySessionControllerIntegrationTest.java (1)
37-37: 클래스 선언에 추가 공백
class StudySessionControllerIntegrationTest에 불필요한 공백이 있습니다.🔎 수정 제안
-class StudySessionControllerIntegrationTest extends BaseIntegrationTest { +class StudySessionControllerIntegrationTest extends BaseIntegrationTest {
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
.github/workflows/prod-ci.ymlsrc/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.javasrc/test/java/com/gpt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.javasrc/test/java/com/gpt/geumpumtabackend/integration/study/controller/StudySessionControllerIntegrationTest.javasrc/test/resources/application-test.yml
🧰 Additional context used
🧬 Code graph analysis (3)
src/test/resources/application-test.yml (5)
src/main/java/com/gpt/geumpumtabackend/global/config/redis/RedisConfig.java (1)
Configuration(13-52)src/main/java/com/gpt/geumpumtabackend/wifi/config/CampusWiFiProperties.java (1)
ConfigurationProperties(11-61)src/main/java/com/gpt/geumpumtabackend/global/config/image/CloudinaryProperties.java (1)
Getter(7-14)src/main/java/com/gpt/geumpumtabackend/global/oauth/user/KakaoProperties.java (1)
ConfigurationProperties(5-17)src/main/java/com/gpt/geumpumtabackend/global/config/image/CloudinaryConfig.java (1)
Configuration(12-27)
src/test/java/com/gpt/geumpumtabackend/integration/study/controller/StudySessionControllerIntegrationTest.java (3)
src/main/java/com/gpt/geumpumtabackend/global/jwt/JwtHandler.java (1)
JwtHandler(22-110)src/main/java/com/gpt/geumpumtabackend/study/service/StudySessionService.java (1)
Service(27-122)src/main/java/com/gpt/geumpumtabackend/study/repository/StudySessionRepository.java (1)
Repository(17-537)
src/test/java/com/gpt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.java (1)
src/main/java/com/gpt/geumpumtabackend/global/jwt/JwtHandler.java (1)
JwtHandler(22-110)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test (Gradle + Testcontainers)
🔇 Additional comments (14)
.github/workflows/prod-ci.yml (1)
18-27: Redis 이미지 버전 불일치 확인 필요CI 서비스에서는
redis:alpine을 사용하고, pre-pull 단계에서는redis:7.0-alpine을 가져옵니다. TestContainers 테스트에서redis:7.0-alpine을 사용하므로 pre-pull은 올바르지만, 서비스 컨테이너의 Redis 버전과 일치시키는 것이 좋습니다.Also applies to: 54-58
src/test/java/com/gpt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.java (2)
96-122: 일간 랭킹 조회 테스트 LGTM테스트 시나리오가 명확하고, 다양한 학과의 학습 기록을 생성한 후 랭킹 조회 및 내 학과 정보를 잘 검증하고 있습니다.
254-297: 전체 흐름 테스트 LGTMController-Service-Repository 전체 흐름을 검증하는 테스트가 잘 구성되어 있습니다. 다른 사용자 토큰으로 조회하여 내 학과 정보가 다르게 표시되는 것을 검증하는 로직이 적절합니다.
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (3)
91-133: truncateAllTables 구현 LGTMH2와 MySQL 양쪽을 지원하는 테이블 truncate 로직이 잘 구현되어 있습니다. 외래 키 제약 조건을 비활성화/활성화하는 로직과 테이블명을 백틱으로 감싸는 처리가 적절합니다.
135-140: Redis 연결 리소스 관리 개선됨try-with-resources를 사용하여 Redis 연결을 자동으로 close하는 것이 좋습니다.
41-55: TestContainers 컨테이너 설정 LGTMMySQL 8.0과 Redis 7.0-alpine 컨테이너가 적절한 타임아웃과 재사용 옵션으로 구성되어 있습니다. CI 환경을 고려한 90초/60초 startup timeout 설정이 적절합니다.
src/test/java/com/gpt/geumpumtabackend/integration/study/controller/StudySessionControllerIntegrationTest.java (4)
77-103: 공부 시작 API 테스트 LGTMWi-Fi IP 정보를 포함한 요청, 응답 검증, 그리고 DB 상태 검증까지 포괄적으로 테스트하고 있습니다.
139-164: 공부 종료 테스트 시간 계산 검증테스트에서
startTime을 2시간 전으로 설정하고 종료 시totalMillis > 0을 검증합니다. 더 정확한 검증을 위해 대략적인 시간 범위(예: 약 2시간 = 7,200,000ms)를 확인하는 것이 좋을 수 있습니다. 현재 구현도 기능적으로는 충분합니다.
205-235: 다른 사용자 기록 격리 테스트 LGTM다른 사용자의 공부 기록이 조회되지 않는 것을 검증하는 테스트가 적절합니다. 사용자 격리가 올바르게 동작하는지 확인하는 중요한 보안 테스트입니다.
242-288: 전체 흐름 테스트 LGTM공부 시작부터 종료까지의 전체 흐름을 검증하며, API 응답과 DB 상태를 모두 확인하는 포괄적인 E2E 테스트입니다.
src/test/resources/application-test.yml (4)
6-17: Datasource 및 HikariCP 설정 LGTMTestContainers가 URL을 동적으로 설정한다는 주석이 명확하고, HikariCP 풀 설정이 테스트 환경에 적합합니다.
94-98: JWT 시크릿 키 보안 개선됨기존 짧은 플레이스홀더에서 256비트 이상의 시크릿 키로 변경되어 HS256 알고리즘 요구사항을 충족합니다.
107-110: Cloudinary 더미 설정테스트용 더미 값으로 설정되어 있습니다. 테스트에서 실제 Cloudinary API 호출이 발생하지 않도록 해당 서비스를 mock 처리하거나, 이 설정으로 인해 테스트 실패가 발생하지 않는지 확인하세요.
112-124: 캠퍼스 WiFi 테스트 설정 LGTM
BaseIntegrationTest의@DynamicPropertySource에서도 WiFi 설정을 제공하지만, 이 YAML 설정과 동적 설정이 함께 사용될 때 동적 설정이 우선합니다. 설정 간 일관성을 유지하는 것이 좋습니다.
...pt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @src/test/resources/application-test.yml:
- Around line 79-83: The Hibernate dialect setting properties.hibernate.dialect
currently uses the deprecated value org.hibernate.dialect.MySQL8Dialect; update
properties.hibernate.dialect to org.hibernate.dialect.MySQLDialect or remove the
properties.hibernate.dialect entry entirely so Hibernate 6.x can auto-detect the
DB version from the JDBC connection.
🧹 Nitpick comments (8)
.github/workflows/dev-ci.yml (4)
15-17: 워커 제한이 필요한지 검토하세요.
org.gradle.workers.max=1설정은 테스트 병렬성을 완전히 제거하여 CI 실행 시간을 크게 증가시킵니다. Testcontainers는 적절한 격리로 2~4개의 워커를 처리할 수 있는 경우가 많습니다.안정성 문제가 실제로 발생하지 않는다면, 더 높은 값(예: 2 또는 4)으로 실험해보는 것을 고려하세요.
54-55:--info플래그가 필요한지 고려하세요.
--info플래그는 상세한 로그를 생성하여 CI 출력을 복잡하게 만들 수 있습니다. 디버깅 시에만 필요하다면 기본 로그 레벨을 사용하거나, 실패 시에만 재실행하도록 고려하세요.
67-72: 로그 추출 로직을 단순화하는 것을 고려하세요.복잡한 파이프라인은 일치하는 컨테이너가 없을 때 조용히 실패할 수 있습니다. 더 명확한 오류 처리를 위해 다음과 같이 단순화할 수 있습니다:
🔧 제안하는 개선 사항
- echo "==== mysql logs (tail) ====" - docker ps -a --format "{{.ID}} {{.Image}}" | grep mysql | awk '{print $1}' | xargs -r -n1 docker logs --tail=200 - echo "==== redis logs (tail) ====" - docker ps -a --format "{{.ID}} {{.Image}}" | grep redis | awk '{print $1}' | xargs -r -n1 docker logs --tail=200 + echo "==== mysql logs (tail) ====" + for container in $(docker ps -a --filter "ancestor=mysql" --format "{{.ID}}"); do + echo "Container: $container" + docker logs --tail=200 "$container" 2>&1 || echo "Failed to get logs for $container" + done + echo "==== redis logs (tail) ====" + for container in $(docker ps -a --filter "ancestor=redis" --format "{{.ID}}"); do + echo "Container: $container" + docker logs --tail=200 "$container" 2>&1 || echo "Failed to get logs for $container" + done
135-135: 멀티 아키텍처 빌드가 필요한지 확인하세요.
linux/amd64,linux/arm64빌드는 CI 시간을 크게 증가시킵니다. 두 플랫폼 모두 필요하지 않다면, 주요 아키텍처(amd64)만 빌드하거나 태그된 릴리스에서만 멀티 아키텍처를 활성화하는 것을 고려하세요.src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (3)
11-11: 사용되지 않는 import를 제거하세요.
@Transactional어노테이션이 제거되었으므로 해당 import 문도 함께 제거해야 합니다.🧹 제안된 수정
-import org.springframework.transaction.annotation.Transactional;
92-101:System.err.println대신 SLF4J 로거 사용을 권장합니다.테스트 코드에서도 적절한 로깅 프레임워크를 사용하면 로그 레벨 제어와 CI 환경에서의 로그 관리가 용이해집니다.
🧹 제안된 수정
클래스 상단에 로거 선언 추가:
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BaseIntegrationTest.class);그리고
System.err.println호출을 다음과 같이 변경:- System.err.println("Cleanup failed, but continuing: " + e.getMessage()); + log.warn("Cleanup failed, but continuing: {}", e.getMessage());- System.err.println("Redis cleanup failed: " + e.getMessage()); + log.warn("Redis cleanup failed: {}", e.getMessage());Also applies to: 147-158
46-50:mysql_native_password인증 플러그인은 MySQL 8.0.34부터 deprecated 상태입니다.
--default-authentication-plugin=mysql_native_password옵션은 MySQL 8.0.34부터 deprecation 경고를 발생시키며, MySQL 8.4에서는 기본값에서 제외되고, MySQL 9.0에서 완전히 제거될 예정입니다. 향후 MySQL 버전 업그레이드 시 호환성 문제를 피하기 위해caching_sha2_password로 마이그레이션을 권장합니다.마이그레이션 시 주의: MySQL Connector/J는 8.0.9 이상에서
caching_sha2_password를 지원하지만, 보안 연결(TLS/SSL)을 사용하거나 RSA 공개 키 교환(AllowPublicKeyRetrieval)을 활성화해야 합니다. 현재 테스트 환경에서는 TLS 없이 사용 중이므로, 전환 시 JDBC 연결 설정 변경이 필요합니다.src/test/resources/application-test.yml (1)
113-125: WiFi 네트워크 설정이@DynamicPropertySource에 의해 오버라이드됩니다.이 YAML 설정은
BaseIntegrationTest의@DynamicPropertySource에서 다른 값(KUMOH_TEST)으로 오버라이드됩니다. 혼란을 방지하기 위해 값을 일치시키거나, 오버라이드된다는 주석을 추가하는 것을 고려해 보세요.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.github/workflows/dev-ci.ymlsrc/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.javasrc/test/resources/application-test.yml
🧰 Additional context used
🧬 Code graph analysis (1)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (1)
src/test/java/com/gpt/geumpumtabackend/unit/config/BaseUnitTest.java (1)
SpringBootTest(6-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test (Gradle + Testcontainers)
🔇 Additional comments (5)
.github/workflows/dev-ci.yml (1)
49-53: CI 워크플로우의 미리 pull한 Docker 이미지 버전이 테스트 설정과 일치합니다.Testcontainers 설정을 확인한 결과, 다음과 같이 정확히 일치합니다:
- MySQL: CI에서
mysql:8.0미리 pull → 테스트에서mysql:8.0사용 ✓- Redis: CI에서
redis:7.0-alpine미리 pull → 테스트에서redis:7.0-alpine사용 ✓이 최적화는 예상대로 작동하며 추가 수정이 필요하지 않습니다.
Likely an incorrect or invalid review comment.
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (2)
103-145: LGTM!
truncateAllTables메서드가 잘 구현되었습니다. H2와 MySQL 모두를 처리하고,try-with-resources를 사용하여 connection을 안전하게 관리하며, 테이블명에 백틱을 사용하여 예약어 문제를 방지합니다.
41-84: TestContainers 설정이 잘 구성되었습니다.MySQL과 Redis 컨테이너 설정, 대기 전략, 타임아웃 설정이 적절합니다.
@DynamicPropertySource를 통한 프로퍼티 오버라이드도 올바르게 구현되었습니다.src/test/resources/application-test.yml (2)
6-18: HikariCP 설정이 테스트 환경에 적합합니다.테스트용으로 적절한 pool size와 timeout 값이 설정되어 있습니다.
leak-detection-threshold를 통해 connection leak도 감지할 수 있습니다.
95-99: JWT secret key가 256-bit 요구사항을 충족합니다.테스트용 secret key가 적절한 길이로 설정되었습니다.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/test/java/com/gpt/geumpumtabackend/integration/study/controller/StudySessionControllerIntegrationTest.java (2)
37-38: @DirtiesContext 사용 및 포맷팅 이슈
class와 클래스명 사이에 공백이 두 개 있습니다.@DirtiesContext는 테스트 후 Spring 컨텍스트를 재생성하므로 테스트 속도가 느려집니다.BaseIntegrationTest에서 DB TRUNCATE와 Redis FLUSH를 수행한다면 불필요할 수 있습니다.DepartmentRankControllerIntegrationTest에서는 이 어노테이션을 사용하지 않습니다.♻️ 제안된 수정
-@org.springframework.test.annotation.DirtiesContext(classMode = org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_CLASS) -class StudySessionControllerIntegrationTest extends BaseIntegrationTest { +class StudySessionControllerIntegrationTest extends BaseIntegrationTest {
99-103:findAll().get(0)패턴은 취약합니다
findAll().get(0)은 테스트 격리가 실패하거나 다른 테스트에서 세션이 생성된 경우 잘못된 세션을 가져올 수 있습니다. 더 명확한 조회 방식을 사용하세요.♻️ 제안된 수정
- // DB 검증 - StudySession savedSession = studySessionRepository.findAll().get(0); - assertThat(savedSession.getUser().getId()).isEqualTo(testUser.getId()); + // DB 검증 + var sessions = studySessionRepository.findAll(); + assertThat(sessions).hasSize(1); + StudySession savedSession = sessions.get(0); + assertThat(savedSession.getUser().getId()).isEqualTo(testUser.getId());
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/test/java/com/gpt/geumpumtabackend/integration/study/controller/StudySessionControllerIntegrationTest.java
🧰 Additional context used
🧬 Code graph analysis (1)
src/test/java/com/gpt/geumpumtabackend/integration/study/controller/StudySessionControllerIntegrationTest.java (2)
src/main/java/com/gpt/geumpumtabackend/global/jwt/JwtHandler.java (1)
JwtHandler(22-110)src/test/java/com/gpt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.java (5)
DisplayName(32-338)Nested(96-168)Nested(170-210)Nested(212-252)Nested(254-337)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test (Gradle + Testcontainers)
🔇 Additional comments (5)
src/test/java/com/gpt/geumpumtabackend/integration/study/controller/StudySessionControllerIntegrationTest.java (5)
58-76: LGTM!설정 메서드가
DepartmentRankControllerIntegrationTest와 동일한 패턴을 따르며, 테스트 사용자 생성 및 JWT 토큰 생성이 올바르게 구현되어 있습니다.
140-165: LGTM!종료 테스트가 도메인 메서드를 올바르게 사용하고, DB 상태 검증(FINISHED, endTime, totalMillis)이 적절합니다.
172-192: 시간 기반 테스트의 잠재적 flakiness
LocalDateTime.now().withHour(10)사용 시, 테스트가 오전 10시 이전에 실행되면 미래 시간의 세션이 생성됩니다. 이는 "오늘의 세션" 로직에 따라 예상치 못한 결과를 초래할 수 있습니다. 자정 근처에서 테스트가 실행될 경우 날짜 경계 문제도 발생할 수 있습니다.현재
getTodayStudySessionAPI가 미래 시간의 세션도 포함하는지 확인이 필요합니다. 만약 현재 시간 이전의 세션만 조회한다면 테스트가 실패할 수 있습니다.
206-236: LGTM!사용자 데이터 격리 테스트가 잘 구현되어 있습니다. 다른 사용자의 공부 기록이 조회되지 않는 것을 검증하는 것은 중요한 보안 테스트입니다.
243-289: LGTM!전체 흐름 테스트가 훌륭하게 구현되어 있습니다. 공부 시작부터 종료까지의 전체 라이프사이클을 커버하며, DB 검증도 철저합니다.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/test/java/com/gpt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.java (1)
88-94: 타임스탬프 정밀도를 명시적으로 설정하는 것을 고려하세요.테스트 데이터 생성 시
.withHour(10).withMinute(0)만 설정하고 초와 나노초는 설정하지 않으면, 호출 시점의 초/나노초 값이 그대로 유지되어 테스트 실행마다 미묘하게 다른 타임스탬프가 생성될 수 있습니다. 테스트의 일관성을 위해.withSecond(0).withNano(0)추가를 고려해보세요.♻️ 제안하는 개선안
private void createStudySession(User user, LocalDateTime startTime, long durationHours) { LocalDateTime endTime = startTime.plusHours(durationHours); StudySession session = new StudySession(); - session.startStudySession(startTime, user); + session.startStudySession(startTime.withSecond(0).withNano(0), user); session.endStudySession(endTime); studySessionRepository.save(session); }또는 더 나은 방법으로, 호출하는 쪽에서 타임스탬프를 생성할 때 명시적으로 설정:
LocalDateTime today = LocalDateTime.now() .withHour(10).withMinute(0).withSecond(0).withNano(0);
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/test/java/com/gpt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test (Gradle + Testcontainers)
🔇 Additional comments (2)
src/test/java/com/gpt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.java (2)
259-298: 우수한 종단 간 통합 테스트입니다.학습 기록 생성부터 일간/주간/월간 랭킹 조회까지 전체 흐름을 체계적으로 검증하는 포괄적인 테스트입니다. 특히 여러 학과의 학생 데이터를 생성하고, 서로 다른 사용자 관점에서
topRanks와myDepartmentRanking이 올바르게 반환되는지 확인하는 부분이 훌륭합니다.
300-337: 사용자 간 랭킹 일관성 검증이 탁월합니다.서로 다른 사용자가 동일한
topRanks순위를 보지만myDepartmentRanking은 각자의 학과 정보로 달라지는지 명확하게 검증합니다. 이는 랭킹 알고리즘의 공정성과 개인화가 동시에 올바르게 작동하는지 확인하는 중요한 테스트입니다.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (1)
11-11: 로거 사용을 권장합니다.현재
Transactionalimport가 사용되지 않고 있으며(Line 11), 에러 로깅에System.err.println을 사용하고 있습니다(Lines 100, 157). 테스트 환경에서도 구조화된 로깅을 사용하면 디버깅과 CI/CD 파이프라인에서의 추적이 용이합니다.♻️ 제안하는 개선사항
package com.gpt.geumpumtabackend.integration.config; import org.junit.jupiter.api.AfterEach; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; -import org.springframework.transaction.annotation.Transactional; import org.testcontainers.containers.GenericContainer;클래스 내부에 로거 추가:
@Testcontainers @org.springframework.test.annotation.DirtiesContext(classMode = org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_CLASS) public abstract class BaseIntegrationTest { + + private static final Logger log = LoggerFactory.getLogger(BaseIntegrationTest.class);System.err를 로거로 교체:
} catch (Exception e) { // 테스트 실패 시 cleanup도 실패할 수 있으므로 무시 - System.err.println("Cleanup failed, but continuing: " + e.getMessage()); + log.warn("Cleanup failed, but continuing: {}", e.getMessage()); }} catch (Exception e) { - System.err.println("Redis cleanup failed: " + e.getMessage()); + log.warn("Redis cleanup failed: {}", e.getMessage()); }Also applies to: 100-100, 157-157
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.javasrc/test/java/com/gpt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.javasrc/test/java/com/gpt/geumpumtabackend/integration/study/controller/StudySessionControllerIntegrationTest.java
🧰 Additional context used
🧬 Code graph analysis (2)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (1)
src/test/java/com/gpt/geumpumtabackend/unit/config/BaseUnitTest.java (1)
SpringBootTest(6-13)
src/test/java/com/gpt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.java (1)
src/main/java/com/gpt/geumpumtabackend/global/jwt/JwtHandler.java (1)
JwtHandler(22-110)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test (Gradle + Testcontainers)
🔇 Additional comments (8)
src/test/java/com/gpt/geumpumtabackend/integration/config/BaseIntegrationTest.java (2)
42-64: 컨테이너 설정이 적절합니다.MySQL과 Redis 컨테이너의 타임아웃 설정(120초, 90초)과 대기 전략(로그 메시지 기반)이 CI 환경을 고려하여 잘 구성되어 있습니다. 컨테이너 재사용 비활성화도 CI 환경에서의 격리성을 보장하기에 적절합니다.
104-146: 테이블 정리 로직이 H2와 MySQL을 모두 지원합니다.외래 키 제약 조건 처리와 테이블 목록 조회가 H2와 MySQL의 차이를 적절히 고려하고 있으며, try-with-resources를 사용한 리소스 관리도 올바릅니다.
src/test/java/com/gpt/geumpumtabackend/integration/study/controller/StudySessionControllerIntegrationTest.java (3)
57-75: 테스트 설정이 적절합니다.사용자 생성과 JWT 토큰 생성이 올바르게 구성되어 있으며,
@BeforeEach를 사용한 테스트 격리가 잘 되어 있습니다.
77-133: 인증 시나리오 테스트가 포괄적입니다.정상 케이스뿐만 아니라 인증 누락(403)과 잘못된 토큰(401) 케이스까지 검증하고 있어, 보안 관련 엣지 케이스를 충분히 커버하고 있습니다.
243-288: 전체 흐름 테스트가 가치 있습니다.시작부터 종료까지의 전체 라이프사이클을 검증하며, 중간 상태 조회와 최종 DB 상태 검증을 모두 포함하고 있어 통합 테스트로서 의미가 큽니다.
src/test/java/com/gpt/geumpumtabackend/integration/rank/controller/DepartmentRankControllerIntegrationTest.java (3)
74-94: 헬퍼 메서드가 테스트 가독성을 향상시킵니다.
createUser와createStudySession헬퍼 메서드를 통해 테스트 데이터 생성 로직을 재사용하고 있어, 테스트 코드의 중복을 줄이고 유지보수성을 높였습니다.
152-167: 빈 랭킹 시나리오 테스트가 중요합니다.학습 기록이 없을 때 빈
topRanks와 기본값(rank=1, totalMillis=0)을 가진myDepartmentRanking을 반환하는지 검증하여, 엣지 케이스를 적절히 커버하고 있습니다.
299-336: 사용자 간 랭킹 일관성 검증이 탁월합니다.서로 다른 사용자가 조회했을 때
topRanks는 동일하지만myDepartmentRanking은 각자의 학과를 반영하는지 검증하여, 비즈니스 로직의 정확성을 효과적으로 테스트하고 있습니다.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @.github/workflows/dev-ci.yml:
- Around line 75-79: The Docker job ("docker" job with name "Docker Build &
Push") lacks a timeout; add a timeout-minutes setting (e.g., timeout-minutes:
30) to the docker job definition—just like the test job—to prevent indefinite
runs during long multi-arch builds by inserting the timeout-minutes key at the
same level as runs-on/needs/if in the docker job stanza.
🧹 Nitpick comments (8)
src/main/java/com/gpt/geumpumtabackend/user/domain/User.java (1)
16-24: 테이블명 백틱 처리 - 좋은 방어적 코딩 관행입니다.
user는 MySQL 예약어이므로 백틱으로 감싸는 것이 충돌을 방지하는 모범 사례입니다. soft-delete 로직도 적절히 구현되어 있습니다.다만, 코드베이스 전체의 일관성을 위해 다른
@Entity클래스들의@SQLDelete어노테이션에서도 동일하게 테이블명을 백틱으로 감싸고 있는지 확인하는 것을 권장합니다.🔍 일관성 검증 스크립트
다른 엔티티들도 동일한 패턴을 사용하는지 확인:
#!/bin/bash # Description: @SQLDelete 어노테이션에서 테이블명 백틱 사용 여부 확인 # @SQLDelete 어노테이션을 사용하는 모든 파일 검색 rg -n -A 5 '@SQLDelete' --type java.github/workflows/dev-ci.yml (1)
64-73: 실패 시 디버깅 덤프가 유용하게 구성되었습니다.
xargs -r플래그로 컨테이너가 없을 때 안전하게 처리됩니다. 다만, 로그 수집 실패가 워크플로우 전체 실패로 이어지지 않도록 각 명령어 끝에|| true를 추가하는 것을 고려해 보세요.♻️ 권장 수정사항
- name: Dump docker state on failure if: failure() run: | echo "==== docker ps -a ====" - docker ps -a + docker ps -a || true echo "==== mysql logs (tail) ====" - docker ps -a --format "{{.ID}} {{.Image}}" | grep mysql | awk '{print $1}' | xargs -r -n1 docker logs --tail=200 + docker ps -a --format "{{.ID}} {{.Image}}" | grep mysql | awk '{print $1}' | xargs -r -n1 docker logs --tail=200 || true echo "==== redis logs (tail) ====" - docker ps -a --format "{{.ID}} {{.Image}}" | grep redis | awk '{print $1}' | xargs -r -n1 docker logs --tail=200 + docker ps -a --format "{{.ID}} {{.Image}}" | grep redis | awk '{print $1}' | xargs -r -n1 docker logs --tail=200 || true.github/workflows/prod-ci.yml (1)
48-52: TestContainers 디버깅을 위한 좋은 추가입니다!Docker 환경 진단 단계는 TestContainers 기반 테스트의 CI 실패를 디버깅할 때 유용합니다.
선택적 제안: 순수 진단 목적이라면 continue-on-error 고려
만약 이 단계가 순수하게 정보 수집 목적이라면
continue-on-error: true를 추가하여 Docker 명령 실패 시에도 빌드를 계속할 수 있습니다. 다만 TestContainers가 Docker를 필수로 요구하므로 현재 방식(실패 시 중단)도 합리적입니다.# Docker 환경 확인 - name: Docker sanity check + continue-on-error: true run: | docker version docker infosrc/test/java/com/gpt/geumpumtabackend/unit/wifi/service/CampusWiFiValidationServiceTest.java (3)
44-91: 엣지 케이스 테스트 추가를 검토해주세요.현재 캐시 히트/미스의 정상 시나리오는 잘 검증되고 있습니다. 다만 이전 리뷰어(kon28289)의 지적처럼, 다음 엣지 케이스에 대한 테스트가 누락되어 있습니다:
null또는 빈 문자열 입력값 처리 (gatewayIp, clientIp)- Redis 연결 오류 시 동작 (valueOperations.get()이 예외를 던지는 경우)
- 캐시에 잘못된 형식의 값이 있는 경우
이러한 시나리오를 추가하면 서비스의 견고성을 더 잘 검증할 수 있습니다.
다음 예외 시나리오 테스트를 추가하시겠습니까?
@Test @DisplayName("null_입력값은_예외를_발생시킨다") void null입력값_예외발생() { // Given String gatewayIp = null; String clientIp = "192.168.1.100"; // When & Then assertThatThrownBy(() -> wifiValidationService.validateFromCache(gatewayIp, clientIp)) .isInstanceOf(IllegalArgumentException.class); } @Test @DisplayName("Redis_오류_시_실제_검증으로_폴백한다") void Redis오류_폴백동작() { // Given String gatewayIp = "192.168.1.1"; String clientIp = "192.168.1.100"; given(redisTemplate.opsForValue()).willReturn(valueOperations); given(valueOperations.get(anyString())).willThrow(new RuntimeException("Redis connection failed")); // 실제 검증 로직 설정 CampusWiFiProperties.WiFiNetwork network = mock(CampusWiFiProperties.WiFiNetwork.class); given(wifiProperties.networks()).willReturn(List.of(network)); given(wifiProperties.validation()).willReturn(new CampusWiFiProperties.ValidationConfig(5)); given(network.active()).willReturn(true); given(network.isValidGatewayIP(gatewayIp)).willReturn(true); given(network.isValidIP(clientIp)).willReturn(true); // When WiFiValidationResult result = wifiValidationService.validateFromCache(gatewayIp, clientIp); // Then assertThat(result.isValid()).isTrue(); }
79-79: ValidationConfig의 TTL 값 중복을 상수로 추출하는 것을 고려해주세요.
new CampusWiFiProperties.ValidationConfig(5)가 여러 테스트(lines 79, 106, 130)에서 반복됩니다. 테스트 가독성과 유지보수를 위해 클래스 레벨 상수로 추출하는 것을 고려해보세요.♻️ 제안하는 리팩토링
@ExtendWith(MockitoExtension.class) @ActiveProfiles("unit-test") @DisplayName("CampusWiFiValidationService 단위 테스트") class CampusWiFiValidationServiceTest { + + private static final int TEST_CACHE_TTL_MINUTES = 5; @Mock private CampusWiFiProperties wifiProperties;그리고 각 테스트에서:
- given(wifiProperties.validation()).willReturn(new CampusWiFiProperties.ValidationConfig(5)); + given(wifiProperties.validation()).willReturn(new CampusWiFiProperties.ValidationConfig(TEST_CACHE_TTL_MINUTES));
89-89: 캐시 TTL 검증을 구체적으로 수행하는 것을 고려해주세요.Line 89에서
any()를 사용하여 캐시 TTL 파라미터를 검증하고 있습니다. 더 엄격한 테스트를 위해 실제ValidationConfig에서 설정한 TTL 값과 일치하는지 확인할 수 있습니다.♻️ 제안하는 개선
+ import java.time.Duration; + import static org.mockito.ArgumentMatchers.eq; // Then assertThat(result.isValid()).isTrue(); - verify(valueOperations).set(eq(cacheKey), eq("true"), any()); + verify(valueOperations).set(eq(cacheKey), eq("true"), eq(Duration.ofMinutes(5)));src/main/java/com/gpt/geumpumtabackend/rank/controller/DepartmentRankController.java (1)
33-33: 현재 구현은 정상 작동하지만 날짜 타입으로 변경을 권장합니다통합 테스트에서
.toString()으로 생성된 전체 datetime 문자열(2024-01-15T10:00:00형식)을 date 파라미터로 전송하고 있어, 현재ISO.DATE_TIME형식은 의도대로 작동합니다.다만, 매개변수명(
startDay,weekFirstDay,monthFirstDay)이 날짜 기반 쿼리를 시사하는데 비해 실제로는 전체 datetime을 요구하므로, 다음과 같이 개선하면 API 의도를 더 명확하게 나타낼 수 있습니다:
LocalDate+@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)사용- 서비스 메서드에서
date.atStartOfDay()로 변환- 클라이언트는 날짜만 전달 가능 (예:
2024-01-15)이 변경은 필수가 아니지만, 일간/주간/월간 랭킹 조회의 의도를 더 정확히 반영합니다.
src/test/java/com/gpt/geumpumtabackend/unit/rank/service/PersonalRankServiceTest.java (1)
131-218: 주간/월간 랭킹 테스트의 커버리지를 확장하는 것을 고려해보세요.
GetCompletedDaily,GetCurrentWeekly,GetCurrentMonthly클래스는 각각 정상 케이스만 테스트하고 있습니다.GetCurrentDaily에서 테스트한 fallback 시나리오나 예외 케이스가 이 메서드들에도 적용된다면, 유사한 테스트를 추가하는 것이 테스트 완결성을 높일 수 있습니다.다만 구현체에서 동일한 로직을 재사용하고 있다면 현재 수준의 테스트로도 충분할 수 있습니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
.github/workflows/dev-ci.yml.github/workflows/prod-ci.ymlsrc/main/java/com/gpt/geumpumtabackend/rank/controller/DepartmentRankController.javasrc/main/java/com/gpt/geumpumtabackend/user/domain/User.javasrc/test/java/com/gpt/geumpumtabackend/unit/rank/service/DepartmentRankServiceTest.javasrc/test/java/com/gpt/geumpumtabackend/unit/rank/service/PersonalRankServiceTest.javasrc/test/java/com/gpt/geumpumtabackend/unit/study/service/StudySessionServiceTest.javasrc/test/java/com/gpt/geumpumtabackend/unit/wifi/service/CampusWiFiValidationServiceTest.javasrc/test/resources/application-test.yml
🚧 Files skipped from review as they are similar to previous changes (2)
- src/test/java/com/gpt/geumpumtabackend/unit/study/service/StudySessionServiceTest.java
- src/test/java/com/gpt/geumpumtabackend/unit/rank/service/DepartmentRankServiceTest.java
🔇 Additional comments (16)
.github/workflows/dev-ci.yml (5)
15-17: Gradle 워커 제한 설정 확인Testcontainers 안정성을 위해 워커를 1개로 제한하는 것은 리소스 경쟁을 줄이지만, 테스트 실행 시간이 크게 증가할 수 있습니다. 현재 테스트 규모가 작다면 괜찮지만, 테스트가 늘어날 경우 2-4개로 조정을 고려해 보세요.
20-23: LGTM!테스트 작업에 30분 타임아웃이 적절하게 추가되었습니다. 이전 리뷰 피드백이 반영되었습니다.
44-56: 테스트 인프라 설정이 잘 구성되었습니다.
- Docker sanity check로 환경 검증
- 이미지 미리 pull로 네트워크 간헐적 이슈 감소
--no-daemon --info옵션으로 디버깅 용이
103-105: LGTM!테스트를 건너뛰고 bootJar만 빌드하는 것은 올바른 접근입니다. 이전 test 작업에서 이미 테스트를 완료했으므로 중복 실행을 방지합니다.
128-138: 멀티 아키텍처 빌드 및 캐싱 설정이 적절합니다.
linux/amd64,linux/arm64플랫폼 지원- GHA 캐시 활용으로 빌드 시간 단축
- 메타데이터 기반 태깅
.github/workflows/prod-ci.yml (1)
54-58: DockerImage 버전이 TestContainers 설정과 정확히 일치합니다.검증 결과:
- MySQL: Pre-pull의
mysql:8.0과 BaseIntegrationTest의 MySQLContainer 설정이 일치 ✓- Redis: Pre-pull의
redis:7.0-alpine과 BaseIntegrationTest의 GenericContainer 설정이 일치 ✓- Redis 버전 차이는 의도된 설계입니다: 서비스의
redis:alpine(일반 테스트용)과 TestContainers의redis:7.0-alpine(통합 테스트용)이 서로 다른 목적으로 분리되어 있습니다.TestContainers 이미지를 미리 pull하여 테스트 실행 시간을 단축하는 최적화 방식이 올바르게 구현되었습니다.
src/test/java/com/gpt/geumpumtabackend/unit/wifi/service/CampusWiFiValidationServiceTest.java (1)
28-38: Mock 설정이 올바르게 수정되었습니다.이전 리뷰에서 지적된 문제(서비스 자체를 Mock하던 것)가 수정되어, 이제 서비스의 의존성(
CampusWiFiProperties,RedisTemplate,ValueOperations)을 Mock하고@InjectMocks로 실제 서비스 로직을 테스트하는 구조로 변경되었습니다.src/test/resources/application-test.yml (3)
106-106: Apple 인증 테스트 시 private-key 형식을 확인해주세요.현재
private-key: dummy-private-key는 단일 라인 문자열로 설정되어 있습니다. Apple 인증이 실제로 동작해야 하는 통합 테스트에서는 이 형식이 유효하지 않을 수 있습니다. 만약 Apple 인증 로직을 테스트할 계획이라면, 유효한 형식의 개인키(PEM 형식 등)를 사용하거나 해당 테스트를 Mock으로 처리하는 것을 고려해주세요.단순히 더미 설정으로 충분하다면 현재 상태로 문제없습니다.
113-125: 캠퍼스 WiFi 테스트 설정이 잘 구성되었습니다.WiFi 네트워크 설정이 명확하게 구조화되어 있습니다:
- CIDR 표기법(
172.30.64.0/18)이 올바르게 사용되었습니다- 테스트용으로 적절한 짧은 캐시 TTL(5분) 설정
- 네트워크 활성화 및 설명이 명시되어 있습니다
이 설정은
CampusWiFiValidationServiceTest의 단위 테스트 및 통합 테스트와 잘 연동될 것입니다.
76-84: JPA 설정이 이전 리뷰를 반영하여 올바르게 수정되었습니다.
- Line 81:
MySQLDialect로 변경되어 Hibernate 6.x의 deprecation 경고가 해결되었습니다- Line 83: 테스트 환경에서
show-sql: false는 로그 노이즈를 줄이는 적절한 선택입니다src/main/java/com/gpt/geumpumtabackend/rank/controller/DepartmentRankController.java (1)
10-10: LGTM!
@DateTimeFormat어노테이션 사용을 위해 필요한 import입니다.src/test/java/com/gpt/geumpumtabackend/unit/rank/service/PersonalRankServiceTest.java (5)
1-44: 테스트 설정이 잘 구성되어 있습니다.Mockito를 사용한 단위 테스트 설정이 적절하며, 필요한 의존성들이 올바르게 모킹되어 있습니다.
46-129: 현재 일간 랭킹 조회 테스트가 포괄적으로 작성되었습니다.정상 케이스, fallback 로직, 예외 처리를 모두 테스트하고 있으며, given-when-then 구조가 명확합니다.
220-368: Fallback 로직 테스트가 매우 포괄적이고 잘 작성되었습니다.다양한 edge case(빈 랭킹, 부서 없는 사용자, 랭킹 외 사용자 등)를 꼼꼼히 검증하고 있습니다. Line 278의 주석도 이전 리뷰 피드백을 반영하여 "3명의 랭킹 데이터 생성"으로 수정되어 실제 데이터와 일치합니다.
370-395: 예외 처리 테스트가 적절합니다.사용자가 랭킹과 DB 모두에 존재하지 않을 때
USER_NOT_FOUND예외가 정확히 발생하는지 검증하고 있습니다.
397-434: 헬퍼 메서드가 잘 구성되어 있습니다.테스트 데이터 생성 로직이 재사용 가능하도록 잘 분리되어 있습니다. Reflection을 사용한 ID 설정(lines 409-416)은 JPA 엔티티 테스트에서 흔히 사용되는 패턴이며 현재 방식으로 충분합니다. Line 425의 한글 주석도 stubbing 제거 이유를 명확히 설명하고 있어 좋습니다.
| } | ||
|
|
||
| @Test | ||
| @DisplayName("긴 세션(12시간)도 올바르게 계산된다") |
There was a problem hiding this comment.
서비스에서 정한 한 번에 측정할 수 있는 최대 시간은 3시간인데 12시간을 측정하는 테스트는 적절하지 않은 것 같아요.
kon28289
left a comment
There was a problem hiding this comment.
수고하셨습니다! 커멘트 달아둔거 읽어봐주세요
🚀 1. 개요
📝 2. 주요 변경 사항
✅ 테스트 인프라 개선
BaseIntegrationTest공통 베이스 클래스 추가TRUNCATE+ RedisFLUSH ALL자동 수행test: 통합 테스트 (MySQL + Redis)unit-test: 단위 테스트 (H2 + Redis 비활성화)✅ 단위 테스트 추가
StudySessionServiceTestDepartmentRankServiceTestPersonalRankServiceTestCampusWiFiValidationServiceTest✅ 통합 테스트 추가
StudySessionIntegrationTestDepartmentRankServiceIntegrationTestSummary by CodeRabbit
버그 수정
새로운 기능
제거된 기능
테스트·설정
리팩터
✏️ Tip: You can customize this high-level summary in your review settings.