Skip to content

test: 핵심 피드백 루프 unit 테스트 보강#89

Merged
alexization merged 3 commits into
developfrom
test/88-core-loop-unit-tests
Apr 16, 2026
Merged

test: 핵심 피드백 루프 unit 테스트 보강#89
alexization merged 3 commits into
developfrom
test/88-core-loop-unit-tests

Conversation

@alexization
Copy link
Copy Markdown
Owner

@alexization alexization commented Apr 16, 2026

요약

  • 기존 domain/service 테스트의 예외 분기, 경계값, fallback, side effect 검증을 보강했습니다.
  • batch feedback loop 관련 strategy, processor, listener, tasklet, reader/writer, scheduler, metrics unit test를 추가했습니다.
  • GitHub infra 계층의 service, mapper, error handler, GraphQL client, token, query builder, dto 테스트를 추가했습니다.

검증

  • ./gradlew test --tests 'com.gitranker.api.batch.*'
  • ./gradlew test --tests 'com.gitranker.api.infrastructure.github.*'
  • ./gradlew test

Closes #88

Summary by CodeRabbit

릴리스 노트

  • Tests
    • 배치 처리, 도메인 모델, GitHub 통합, 사용자 관리 등 전반적인 시스템 컴포넌트에 대한 테스트 커버리지 대폭 확대
    • 다양한 정상 동작 시나리오와 에러 처리 경로에 대한 종합적인 테스트 추가
    • 시스템 안정성 및 신뢰성 향상을 위한 테스트 인프라 강화

- 기존 domain/service 테스트의 예외 분기와 경계값 검증 보강
- batch 피드백 루프 unit 테스트 신규 추가
- GitHub infra 및 전체 테스트 스위트 검증 추가
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: be5d99aa-3e43-491c-9de6-afad7a8935e6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

핵심 피드백 루프의 회귀 위험을 줄이기 위해 배치, 도메인, GitHub 인프라 계층에 걸쳐 광범위한 단위 테스트를 추가했습니다. 기존 테스트의 assertion과 예외 분기도 강화했으며, 값 객체 및 오케스트레이션 단위 테스트도 새로 도입했습니다.

Changes

Cohort / File(s) Summary
배치 리스너 및 메트릭스 테스트
src/test/java/com/gitranker/api/batch/listener/BatchProgressListenerTest.java, GitHubCostListenerTest.java, UserScoreCalculationSkipListenerTest.java
src/test/java/com/gitranker/api/batch/metrics/BatchMetricsTest.java
배치 진행 상황 추적, 비용 집계, 스킵 로깅, 메트릭 기록 동작을 검증하는 리스너 및 메트릭스 테스트 추가
배치 프로세서 및 전략 테스트
src/test/java/com/gitranker/api/batch/processor/ScoreRecalculationProcessorTest.java
src/test/java/com/gitranker/api/batch/strategy/ActivityUpdateContextTest.java, FullActivityUpdateStrategyTest.java, IncrementalActivityUpdateStrategyTest.java
점수 재산정 처리, 기준선/전체 활동 업데이트 전략, GitHub 사용자명 변경 복구 흐름 검증
배치 리더, 작성자, 태스클릿, 스케줄러 테스트
src/test/java/com/gitranker/api/batch/reader/UserItemReaderTest.java, writer/UserItemWriterTest.java
src/test/java/com/gitranker/api/batch/tasklet/RankingRecalculationTaskletTest.java
src/test/java/com/gitranker/api/batch/scheduler/BatchSchedulerTest.java
배치 입출력 처리, 랭킹 재산정, 배치 작업 스케줄링 및 오류 처리 검증
도메인 서비스 테스트 강화
src/test/java/com/gitranker/api/domain/auth/service/AuthServiceTest.java
src/test/java/com/gitranker/api/domain/user/service/UserDeletionServiceTest.java, UserPersistenceServiceTest.java, UserQueryServiceTest.java, UserRefreshServiceTest.java, UserRegistrationServiceTest.java
인증 로그아웃 경로, 사용자 프로필 업데이트, 활동 통계 갱신, 기준선 없음 처리 등 부수 효과 및 경계값 검증
값 객체 및 오케스트레이션 테스트
src/test/java/com/gitranker/api/domain/user/vo/ActivityStatisticsTest.java, RankInfoTest.java, ScoreTest.java
src/test/java/com/gitranker/api/domain/log/ActivityLogTest.java, ActivityLogOrchestratorTest.java, ActivityLogServiceTest.java
src/test/java/com/gitranker/api/domain/ranking/RankingRecalculationServiceTest.java, RankingServiceTest.java
src/test/java/com/gitranker/api/domain/user/UserTest.java
점수 계산, 랭킹 계층 분류, 활동 로그 생성/갱신, 사용자 프로필/통계 초기화 및 업데이트 흐름 검증
배지 렌더링 테스트 강화
src/test/java/com/gitranker/api/domain/badge/BadgeFormatterTest.java, BadgeServiceTest.java, SvgBadgeRendererTest.java
배지 글꼴 크기 경계값, 메트릭 기록, 영 활동(diff=0) 렌더링 검증
GitHub API 서비스 및 클라이언트 테스트
src/test/java/com/gitranker/api/infrastructure/github/GitHubActivityServiceTest.java
src/test/java/com/gitranker/api/infrastructure/github/GitHubApiErrorHandlerTest.java, GitHubApiMetricsTest.java
src/test/java/com/gitranker/api/infrastructure/github/GitHubGraphQLClientTest.java
활동 조회, 속도 제한 처리, GraphQL 오류 매핑, 토큰 상태 업데이트, 메트릭 기록 검증
GitHub DTO 및 유틸리티 테스트
src/test/java/com/gitranker/api/infrastructure/github/dto/GitHubActivitySummaryTest.java, GitHubAllActivitiesResponseTest.java, GitHubGraphQLRequestTest.java, GitHubNodeUserResponseTest.java, GitHubUserInfoResponseTest.java
src/test/java/com/gitranker/api/infrastructure/github/token/GitHubTokenPoolTest.java, TokenStateTest.java
src/test/java/com/gitranker/api/infrastructure/github/util/GraphQLQueryBuilderTest.java
src/test/java/com/gitranker/api/domain/user/vo/ActivityStatisticsTest.java, src/test/java/com/gitranker/api/global/util/TimeUtilsTest.java
GitHub 데이터 매핑, 토큰 풀 회전 및 소진, GraphQL 쿼리 생성, null 처리, 데이터 변환 검증

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement

Poem

토끼가 뛰어다니며 테스트를 심고,
배치에서 전략까지, GitHub까지 퍼져,
회귀의 위험을 잡아내리! 🐰
값 객체는 검증되고, 오케스트레이션도 견고,
피드백 루프는 이제 더욱 튼튼하네. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 풀 리퀘스트의 핵심 변경점을 명확하게 요약하고 있습니다. 제목 '핵심 피드백 루프 unit 테스트 보강'은 주요 목표와 변경사항을 정확하게 반영합니다.
Linked Issues check ✅ Passed 코드 변경사항이 연결된 이슈 #88의 모든 코딩 관련 요구사항을 충족합니다: 기존 domain/service 테스트 보강, value object 테스트 추가, batch 계층 단위 테스트 추가, GitHub 인프라 테스트 추가가 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 이슈 #88의 범위 내에 포함됩니다. 테스트 코드 추가만 포함되어 있으며 비즈니스 로직 변경이나 범위 외 수정은 없습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/88-core-loop-unit-tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f27868510d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (8)
src/test/java/com/gitranker/api/batch/listener/UserScoreCalculationSkipListenerTest.java (1)

33-34: 중복 리터럴은 상수화하면 테스트 유지보수가 더 쉬워집니다.

"DailyScoreRecalculationJob"와 사용자 식별자 문자열이 반복되어 변경 시 수정 지점이 늘어납니다. 테스트 클래스 내부 상수로 모아두는 정도는 가독성/유지보수에 도움이 됩니다.

예시 diff
 class UserScoreCalculationSkipListenerTest {
+    private static final String JOB_NAME = "DailyScoreRecalculationJob";
+    private static final String USER_ALICE = "alice";
+    private static final String UNKNOWN_USER = "UNKNOWN_USER";

@@
         verify(batchFailureLogService).saveFailureLog(
-                "DailyScoreRecalculationJob",
-                "UNKNOWN_USER",
+                JOB_NAME,
+                UNKNOWN_USER,
                 ErrorType.GITHUB_API_TIMEOUT,
                 "[READ_PHASE] error.github.api-timeout: timeout"
         );
@@
-        User user = TestFixtures.user("alice");
+        User user = TestFixtures.user(USER_ALICE);
@@
         verify(batchFailureLogService).saveFailureLog(
-                "DailyScoreRecalculationJob",
-                "alice",
+                JOB_NAME,
+                USER_ALICE,
                 ErrorType.DEFAULT_ERROR,
                 "[PROCESS_PHASE] boom"
         );
@@
-        User user = TestFixtures.user("alice");
+        User user = TestFixtures.user(USER_ALICE);
@@
         verify(batchFailureLogService).saveFailureLog(
-                "DailyScoreRecalculationJob",
-                "alice",
+                JOB_NAME,
+                USER_ALICE,
                 ErrorType.GITHUB_USER_NOT_FOUND,
                 "[WRITE_PHASE] error.github.user-not-found"
         );

Also applies to: 48-49, 63-64

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/com/gitranker/api/batch/listener/UserScoreCalculationSkipListenerTest.java`
around lines 33 - 34, Replace repeated string literals in
UserScoreCalculationSkipListenerTest by introducing private static final
constants (e.g., JOB_NAME_DAILY_SCORE = "DailyScoreRecalculationJob" and
UNKNOWN_USER_ID = "UNKNOWN_USER") at the top of the test class and use those
constants wherever the literals appear (including the other occurrences
currently used in assertions/setup), ensuring all occurrences in methods that
reference the job name or user id (the repeated uses around the existing
assertions and test setups) are updated to the new constants for easier
maintenance.
src/test/java/com/gitranker/api/domain/user/service/UserRegistrationServiceTest.java (1)

108-123: 테스트 의도는 맞지만 null email 보존 단언을 추가해 주세요.

현재는 updateProfile 미호출만 검증하고, 응답의 email이 기존 값으로 유지되는지는 고정하지 않습니다. 해당 단언을 추가하면 이 분기의 회귀 방지력이 더 좋아집니다.

권장 보강 예시
         RegisterUserResponse response = userRegistrationService.register(attributes);

         assertThat(response.username()).isEqualTo("alice");
+        assertThat(response.email()).isEqualTo("alice@example.com");
+        assertThat(response.isNewUser()).isFalse();
         verify(userPersistenceService, never()).updateProfile(any(User.class), any(String.class), any(String.class), any());
         verify(gitHubActivityService, never()).fetchRawAllActivities(org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/com/gitranker/api/domain/user/service/UserRegistrationServiceTest.java`
around lines 108 - 123, Add an assertion that the returned RegisterUserResponse
preserves the existing user's email: after calling
userRegistrationService.register(attributes),
assertThat(response.email()).isEqualTo(existingUser.getEmail()) (or
assertThat(response.email()).isNull() if the fixture sets null), so the test
verifies the email is unchanged in UserRegistrationServiceTest alongside the
existing checks for updateProfile and gitHubActivityService calls.
src/test/java/com/gitranker/api/domain/auth/service/AuthServiceTest.java (1)

131-140: 예외 검증에 부수효과 미발생 검증도 함께 넣는 것을 권장합니다.

현재는 예외 타입만 확인해서, 추후 구현 변경으로 삭제/쿠키 정리가 잘못 호출되어도 테스트가 통과할 수 있습니다.

테스트 보강 예시
     assertThatThrownBy(() -> authService.logout(savedUser(1L, "alice"), "missing", mock(HttpServletRequest.class), mock(HttpServletResponse.class)))
             .isInstanceOf(BusinessException.class)
             .extracting(exception -> ((BusinessException) exception).getErrorType())
             .isEqualTo(ErrorType.INVALID_REFRESH_TOKEN);
+    verify(refreshTokenRepository, never()).deleteByToken("missing");
+    verifyNoInteractions(authCookieManager);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/com/gitranker/api/domain/auth/service/AuthServiceTest.java`
around lines 131 - 140, The test currently only asserts the thrown
BusinessException for authService.logout; also verify no side effects occur by
asserting that refresh token removal and cookie/header cleanup are not invoked:
after calling logout(...) in the throwsWhenLogoutTokenDoesNotExist test, add
Mockito verifications to ensure refreshTokenRepository.delete and/or
refreshTokenRepository.deleteById are never called and
HttpServletResponse.addCookie and HttpServletResponse.setHeader are never called
(use the mocked HttpServletResponse passed into authService.logout) so the test
fails if any deletion or cookie/header modifications happen unexpectedly.
src/test/java/com/gitranker/api/infrastructure/github/token/TokenStateTest.java (1)

26-27: 시간값을 고정하면 테스트 안정성이 더 좋아집니다.

Instant.now() 대신 고정 Instant 상수를 쓰면 실행 시점에 덜 민감해져서 테스트 재현성이 올라갑니다.

예시 변경안
-        Instant resetAt = Instant.now().plusSeconds(300);
+        Instant resetAt = Instant.parse("2026-04-16T18:00:00Z");
...
-        state.update(0, Instant.now().minusSeconds(1));
+        state.update(0, Instant.parse("2026-04-16T17:59:59Z"));

Also applies to: 39-41

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/com/gitranker/api/infrastructure/github/token/TokenStateTest.java`
around lines 26 - 27, In TokenStateTest, replace the time-dependent
Instant.now() usage (the resetAt variable and the other Instant.now() usages
around lines 39–41) with a fixed Instant constant (e.g. a private static final
Instant FIXED_INSTANT) and derive offsets from that constant (use
FIXED_INSTANT.plusSeconds(...) where offsets are needed) so tests become
deterministic; update any assertions that referenced the previous dynamic times
to use the fixed Instant-based values.
src/test/java/com/gitranker/api/infrastructure/github/GitHubApiMetricsTest.java (1)

19-26: recordRateLimit 인자 의미를 변수로 드러내 주세요.

현재 리터럴 호출은 비용/잔여량 의미를 읽는 사람이 혼동하기 쉽습니다. 테스트 의도를 고정하려면 명시 변수로 분리하는 편이 안전합니다.

가독성 개선 예시
-        LocalDateTime resetAt = LocalDateTime.of(2026, 4, 16, 18, 0);
-
-        metrics.recordRateLimit(3, 120, resetAt);
+        LocalDateTime resetAt = LocalDateTime.of(2026, 4, 16, 18, 0);
+        int cost = 3;
+        int remaining = 120;
+
+        metrics.recordRateLimit(cost, remaining, resetAt);

-        assertThat(metrics.getRemaining()).isEqualTo(120);
+        assertThat(metrics.getRemaining()).isEqualTo(remaining);
         assertThat(metrics.getResetAtFormatted()).isEqualTo("2026-04-16T18:00");
-        assertThat(registry.get("github_api_cost_total").counter().count()).isEqualTo(3.0);
-        assertThat(registry.get("github_api_remaining").gauge().value()).isEqualTo(120.0);
+        assertThat(registry.get("github_api_cost_total").counter().count()).isEqualTo((double) cost);
+        assertThat(registry.get("github_api_remaining").gauge().value()).isEqualTo((double) remaining);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/com/gitranker/api/infrastructure/github/GitHubApiMetricsTest.java`
around lines 19 - 26, In GitHubApiMetricsTest, make the meaning of the literals
passed to metrics.recordRateLimit(...) explicit by introducing named local
variables (e.g., int cost = 3; int remaining = 120;) and use those when calling
recordRateLimit(cost, remaining, resetAt) and in the subsequent assertions; this
clarifies the intent of the test and avoids ambiguity when referencing
metrics.getRemaining(), registry.get("github_api_cost_total") and
registry.get("github_api_remaining") in the assertions.
src/test/java/com/gitranker/api/domain/ranking/RankingServiceTest.java (1)

85-95: 신규 empty-page 테스트에도 저장소 상호작용 검증을 맞춰주세요.

동일 파일의 다른 테스트와 일관되게 repository 호출/추가 상호작용 부재까지 확인하면 회귀 탐지력이 더 좋아집니다.

일관성 보강 예시
         assertThat(rankingList.pageInfo().totalElements()).isEqualTo(41);
         assertThat(rankingList.pageInfo().totalPages()).isEqualTo(3);
         assertThat(rankingList.pageInfo().isLast()).isTrue();
+        verify(userRepository).findAllByOrderByScoreValueDesc(pageRequest);
+        verifyNoMoreInteractions(userRepository);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/com/gitranker/api/domain/ranking/RankingServiceTest.java`
around lines 85 - 95, The test in RankingServiceTest that calls
rankingService.getRankingList(2, null) sets up an empty PageImpl but does not
verify repository interactions; update the test to assert the repository was
called and no extra calls were made by adding verification for
userRepository.findAllByOrderByScoreValueDesc(pageRequest) (using the same
pageRequest used in the when) and a subsequent verifyNoMoreInteractions or
verifyNoInteractions for other mocks to match the style of the other tests and
catch regressions.
src/test/java/com/gitranker/api/domain/log/ActivityLogServiceTest.java (1)

148-163: diff 유지 검증을 전체 필드로 확장하면 테스트 의도가 더 명확해집니다.

Line 161-162는 일부 diff 필드만 확인합니다. 테스트명/의도(“diff 유지”)와 맞추려면 diffIssueCount, diffPrCount, diffMergedPrCount도 함께 검증하는 편이 안전합니다.

🔍 제안 diff
         assertThat(baselineLog.getDiffCommitCount()).isZero();
+        assertThat(baselineLog.getDiffIssueCount()).isZero();
+        assertThat(baselineLog.getDiffPrCount()).isZero();
+        assertThat(baselineLog.getDiffMergedPrCount()).isZero();
         assertThat(baselineLog.getDiffReviewCount()).isZero();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/com/gitranker/api/domain/log/ActivityLogServiceTest.java`
around lines 148 - 163, The test updatesBaselineLogWithoutTouchingDiff in
ActivityLogServiceTest only asserts diffCommitCount and diffReviewCount remain
zero; extend the "diff 유지" verification to assert
baselineLog.getDiffIssueCount(), baselineLog.getDiffPrCount(), and
baselineLog.getDiffMergedPrCount() are also zero so all diff-related fields are
validated after calling activityLogService.updateBaselineLog.
src/test/java/com/gitranker/api/domain/log/ActivityLogOrchestratorTest.java (1)

55-55: 날짜 검증 시 LocalDate.now() 재호출은 플래키 가능성이 있습니다.

Line 55는 검증 시점에 LocalDate.now()를 다시 호출합니다. 테스트 본문에서 생성한 today 변수를 재사용하면 자정 경계에서의 간헐 실패를 줄일 수 있습니다.

🧪 제안 diff
     void skipsBaselineLogWhenNotProvidedForNewUser() {
         User user = user("alice");
         ActivityStatistics totalStats = stats(10, 2, 3, 4, 5);
+        LocalDate today = LocalDate.now();

         activityLogOrchestrator.createLogsForNewUser(user, totalStats, null);

         verify(activityLogService, never()).saveBaselineLog(any(User.class), any(ActivityStatistics.class), any(LocalDate.class));
-        verify(activityLogService).saveActivityLog(user, totalStats, ActivityStatistics.empty(), LocalDate.now());
+        verify(activityLogService).saveActivityLog(user, totalStats, ActivityStatistics.empty(), today);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/com/gitranker/api/domain/log/ActivityLogOrchestratorTest.java`
at line 55, The test re-calls LocalDate.now() in the verify assertion which can
be flaky around midnight; update the assertion in ActivityLogOrchestratorTest to
reuse the test's existing today variable (the one created earlier in the test)
instead of calling LocalDate.now() again so the verified call to
activityLogService.saveActivityLog(user, totalStats, ActivityStatistics.empty(),
...) uses the stable today value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/test/java/com/gitranker/api/batch/metrics/BatchMetricsTest.java`:
- Around line 39-50: The test currently only calls recordItemsProcessed and
recordItemsSkipped once so it won't detect non-accumulating implementations;
update the test in BatchMetricsTest to call BatchMetrics.recordItemsProcessed
and recordItemsSkipped multiple times (e.g., two calls with different values)
and then assert that the registry counters "batch_items_processed_total" and
"batch_items_skipped_total" reflect the summed totals (verify
registry.get(...).counter().count() equals the combined values) to ensure true
accumulation behavior.

In
`@src/test/java/com/gitranker/api/batch/processor/ScoreRecalculationProcessorTest.java`:
- Around line 79-82: The test calls LocalDate.now() repeatedly which can yield
different values around midnight/year boundaries; capture LocalDate today (and
currentYear via today.getYear()) once at the top of the test and reuse it for
all stubs and assertions (e.g. replace repeated
LocalDate.of(LocalDate.now().getYear(), 1, 1) calls used in
activityLogRepository.findTopByUserAndActivityDateLessThanOrderByActivityDateDesc
stubs and the other occurrences referenced in the comment ranges) so both the
stubbed inputs and expected values use the same deterministic date instance.

In `@src/test/java/com/gitranker/api/batch/scheduler/BatchSchedulerTest.java`:
- Around line 50-53: The test expectations in BatchSchedulerTest are mismatched
with BatchScheduler: the scheduler sets a "startTime" (Long) parameter but the
test asserts a "runTime" LocalDateTime; update the assertion around the
ArgumentCaptor<JobParameters> (used with
verify(jobLauncher).run(eq(dailyScoreRecalculationJob), captor.capture())) to
check for the "startTime" parameter instead (e.g., use
captor.getValue().getLong("startTime") and assert it is not null/positive) and
leave the LogContext assertion as-is; this aligns the test with the
BatchScheduler behavior.

In `@src/test/java/com/gitranker/api/domain/log/ActivityLogOrchestratorTest.java`:
- Line 93: 현재 검증은 특정 인자 조합(todayLog, totalStats)만 금지하므로 다른 인자들로
updateBaselineLog가 호출되면 테스트가 통과할 수 있습니다; ActivityLogOrchestratorTest에서
verify(activityLogService, never()).updateBaselineLog(todayLog, totalStats) 대신
updateBaselineLog 메서드 호출 자체를 금지하도록 verify(activityLogService,
never()).updateBaselineLog(any(), any()) (또는 verify(activityLogService,
times(0)).updateBaselineLog(any(), any()))로 바꿔 메서드 호출 여부만 검증하도록 수정하세요.

In `@src/test/java/com/gitranker/api/domain/user/UserTest.java`:
- Around line 33-46: Test updatesOnlyChangedProfileFields() is missing an
assertion that updatedAt remains unchanged when updateProfile is called with
identical values; modify the test to capture the timestamp before the
"unchanged" call (use User.getUpdatedAt()), call user.updateProfile(...) for the
unchanged case, then assert that user.getUpdatedAt() is equal to that pre-call
timestamp, and only after that perform the "changed" call and assert updatedAt
was advanced; reference the test method updatesOnlyChangedProfileFields(), the
User.updateProfile(...) calls, and User.getUpdatedAt() when adding these
assertions.

In
`@src/test/java/com/gitranker/api/infrastructure/github/GitHubGraphQLClientTest.java`:
- Around line 175-180: The assertions for the final token rate-limit are too
loose — replace the isIn checks with exact equality checks for the expected
post-merge values so the test fails if a stale (first-response) value is saved.
Update the assertions around remainingCaptor and resetCaptor used with
apiMetrics.recordRateLimit and tokenPool.updateTokenState to
assertThat(remainingCaptor.getValue()).isEqualTo(<expectedMergedRemaining>) and
assertThat(resetCaptor.getValue()).isEqualTo(mergedResetAt) (or the single
expected reset value), ensuring the test verifies the precise final
remaining/reset values after the second call.

In
`@src/test/java/com/gitranker/api/infrastructure/github/token/GitHubTokenPoolTest.java`:
- Around line 45-47: The test sets a token remaining value equal to the
threshold, so rotation won't occur; update the test so the scenario is truly
"below threshold" by calling pool.updateTokenState("token-a", <value less than
threshold>, LocalDateTime.now().plusMinutes(10)) (e.g., threshold-1) so that
pool.getToken() returns "token-b" as asserted; locate the call to
updateTokenState and adjust the remaining parameter accordingly, ensuring the
threshold constant used by the pool is considered.

---

Nitpick comments:
In
`@src/test/java/com/gitranker/api/batch/listener/UserScoreCalculationSkipListenerTest.java`:
- Around line 33-34: Replace repeated string literals in
UserScoreCalculationSkipListenerTest by introducing private static final
constants (e.g., JOB_NAME_DAILY_SCORE = "DailyScoreRecalculationJob" and
UNKNOWN_USER_ID = "UNKNOWN_USER") at the top of the test class and use those
constants wherever the literals appear (including the other occurrences
currently used in assertions/setup), ensuring all occurrences in methods that
reference the job name or user id (the repeated uses around the existing
assertions and test setups) are updated to the new constants for easier
maintenance.

In `@src/test/java/com/gitranker/api/domain/auth/service/AuthServiceTest.java`:
- Around line 131-140: The test currently only asserts the thrown
BusinessException for authService.logout; also verify no side effects occur by
asserting that refresh token removal and cookie/header cleanup are not invoked:
after calling logout(...) in the throwsWhenLogoutTokenDoesNotExist test, add
Mockito verifications to ensure refreshTokenRepository.delete and/or
refreshTokenRepository.deleteById are never called and
HttpServletResponse.addCookie and HttpServletResponse.setHeader are never called
(use the mocked HttpServletResponse passed into authService.logout) so the test
fails if any deletion or cookie/header modifications happen unexpectedly.

In `@src/test/java/com/gitranker/api/domain/log/ActivityLogOrchestratorTest.java`:
- Line 55: The test re-calls LocalDate.now() in the verify assertion which can
be flaky around midnight; update the assertion in ActivityLogOrchestratorTest to
reuse the test's existing today variable (the one created earlier in the test)
instead of calling LocalDate.now() again so the verified call to
activityLogService.saveActivityLog(user, totalStats, ActivityStatistics.empty(),
...) uses the stable today value.

In `@src/test/java/com/gitranker/api/domain/log/ActivityLogServiceTest.java`:
- Around line 148-163: The test updatesBaselineLogWithoutTouchingDiff in
ActivityLogServiceTest only asserts diffCommitCount and diffReviewCount remain
zero; extend the "diff 유지" verification to assert
baselineLog.getDiffIssueCount(), baselineLog.getDiffPrCount(), and
baselineLog.getDiffMergedPrCount() are also zero so all diff-related fields are
validated after calling activityLogService.updateBaselineLog.

In `@src/test/java/com/gitranker/api/domain/ranking/RankingServiceTest.java`:
- Around line 85-95: The test in RankingServiceTest that calls
rankingService.getRankingList(2, null) sets up an empty PageImpl but does not
verify repository interactions; update the test to assert the repository was
called and no extra calls were made by adding verification for
userRepository.findAllByOrderByScoreValueDesc(pageRequest) (using the same
pageRequest used in the when) and a subsequent verifyNoMoreInteractions or
verifyNoInteractions for other mocks to match the style of the other tests and
catch regressions.

In
`@src/test/java/com/gitranker/api/domain/user/service/UserRegistrationServiceTest.java`:
- Around line 108-123: Add an assertion that the returned RegisterUserResponse
preserves the existing user's email: after calling
userRegistrationService.register(attributes),
assertThat(response.email()).isEqualTo(existingUser.getEmail()) (or
assertThat(response.email()).isNull() if the fixture sets null), so the test
verifies the email is unchanged in UserRegistrationServiceTest alongside the
existing checks for updateProfile and gitHubActivityService calls.

In
`@src/test/java/com/gitranker/api/infrastructure/github/GitHubApiMetricsTest.java`:
- Around line 19-26: In GitHubApiMetricsTest, make the meaning of the literals
passed to metrics.recordRateLimit(...) explicit by introducing named local
variables (e.g., int cost = 3; int remaining = 120;) and use those when calling
recordRateLimit(cost, remaining, resetAt) and in the subsequent assertions; this
clarifies the intent of the test and avoids ambiguity when referencing
metrics.getRemaining(), registry.get("github_api_cost_total") and
registry.get("github_api_remaining") in the assertions.

In
`@src/test/java/com/gitranker/api/infrastructure/github/token/TokenStateTest.java`:
- Around line 26-27: In TokenStateTest, replace the time-dependent Instant.now()
usage (the resetAt variable and the other Instant.now() usages around lines
39–41) with a fixed Instant constant (e.g. a private static final Instant
FIXED_INSTANT) and derive offsets from that constant (use
FIXED_INSTANT.plusSeconds(...) where offsets are needed) so tests become
deterministic; update any assertions that referenced the previous dynamic times
to use the fixed Instant-based values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: f50e6a1c-1392-4523-98e7-2dcc041c3d62

📥 Commits

Reviewing files that changed from the base of the PR and between fc85948 and f278685.

📒 Files selected for processing (44)
  • src/test/java/com/gitranker/api/batch/listener/BatchProgressListenerTest.java
  • src/test/java/com/gitranker/api/batch/listener/GitHubCostListenerTest.java
  • src/test/java/com/gitranker/api/batch/listener/UserScoreCalculationSkipListenerTest.java
  • src/test/java/com/gitranker/api/batch/metrics/BatchMetricsTest.java
  • src/test/java/com/gitranker/api/batch/processor/ScoreRecalculationProcessorTest.java
  • src/test/java/com/gitranker/api/batch/reader/UserItemReaderTest.java
  • src/test/java/com/gitranker/api/batch/scheduler/BatchSchedulerTest.java
  • src/test/java/com/gitranker/api/batch/strategy/ActivityUpdateContextTest.java
  • src/test/java/com/gitranker/api/batch/strategy/FullActivityUpdateStrategyTest.java
  • src/test/java/com/gitranker/api/batch/strategy/IncrementalActivityUpdateStrategyTest.java
  • src/test/java/com/gitranker/api/batch/tasklet/RankingRecalculationTaskletTest.java
  • src/test/java/com/gitranker/api/batch/writer/UserItemWriterTest.java
  • src/test/java/com/gitranker/api/domain/auth/service/AuthServiceTest.java
  • src/test/java/com/gitranker/api/domain/badge/BadgeFormatterTest.java
  • src/test/java/com/gitranker/api/domain/badge/BadgeServiceTest.java
  • src/test/java/com/gitranker/api/domain/badge/SvgBadgeRendererTest.java
  • src/test/java/com/gitranker/api/domain/log/ActivityLogOrchestratorTest.java
  • src/test/java/com/gitranker/api/domain/log/ActivityLogServiceTest.java
  • src/test/java/com/gitranker/api/domain/log/ActivityLogTest.java
  • src/test/java/com/gitranker/api/domain/ranking/RankingRecalculationServiceTest.java
  • src/test/java/com/gitranker/api/domain/ranking/RankingServiceTest.java
  • src/test/java/com/gitranker/api/domain/user/UserTest.java
  • src/test/java/com/gitranker/api/domain/user/service/UserDeletionServiceTest.java
  • src/test/java/com/gitranker/api/domain/user/service/UserPersistenceServiceTest.java
  • src/test/java/com/gitranker/api/domain/user/service/UserQueryServiceTest.java
  • src/test/java/com/gitranker/api/domain/user/service/UserRefreshServiceTest.java
  • src/test/java/com/gitranker/api/domain/user/service/UserRegistrationServiceTest.java
  • src/test/java/com/gitranker/api/domain/user/vo/ActivityStatisticsTest.java
  • src/test/java/com/gitranker/api/domain/user/vo/RankInfoTest.java
  • src/test/java/com/gitranker/api/domain/user/vo/ScoreTest.java
  • src/test/java/com/gitranker/api/global/util/TimeUtilsTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/GitHubActivityServiceTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/GitHubApiErrorHandlerTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/GitHubApiMetricsTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/GitHubDataMapperTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/GitHubGraphQLClientTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/dto/GitHubActivitySummaryTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/dto/GitHubAllActivitiesResponseTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/dto/GitHubGraphQLRequestTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/dto/GitHubNodeUserResponseTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/dto/GitHubUserInfoResponseTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/token/GitHubTokenPoolTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/token/TokenStateTest.java
  • src/test/java/com/gitranker/api/infrastructure/github/util/GraphQLQueryBuilderTest.java

Comment thread src/test/java/com/gitranker/api/batch/metrics/BatchMetricsTest.java
Comment thread src/test/java/com/gitranker/api/domain/log/ActivityLogOrchestratorTest.java Outdated
Comment thread src/test/java/com/gitranker/api/domain/user/UserTest.java
- Asia/Seoul 고정 zone 상수를 테스트에 재사용
- threshold 회전 케이스를 현재 시각 대신 고정된 미래 시각으로 검증
- UTC 환경으로 clean test를 재현해 CI 실패 원인 제거
@alexization alexization merged commit fbc672c into develop Apr 16, 2026
2 checks passed
@alexization alexization deleted the test/88-core-loop-unit-tests branch April 16, 2026 08:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant