🧪 Add tests for handleRateLimit#451
Conversation
Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
|
Warning Review limit reached
Next review available in: 24 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
✨ Finishing Touches🧪 Generate unit tests (beta)
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.
Code Review
This pull request adds unit tests for the handleRateLimit utility function in src/lib/__tests__/apiUtils.test.ts, covering scenarios with valid, missing, and invalid X-RateLimit-Reset headers. The review feedback recommends using Vitest's built-in fake timers instead of manually monkey-patching Date.now to avoid implicit any types. Additionally, the reviewer suggests removing redundant function calls in the tests and handling the caught unknown error in a type-safe manner to prevent TypeScript compilation errors under strict mode.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| let originalDateNow; | ||
| const mockNow = 1700000000000; // 2023-11-14T22:13:20.000Z | ||
|
|
||
| beforeEach(() => { | ||
| originalDateNow = Date.now; | ||
| Date.now = vi.fn(() => mockNow); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| Date.now = originalDateNow; | ||
| }); |
There was a problem hiding this comment.
Instead of manually monkey-patching Date.now and storing it in a variable with an implicit any type, use Vitest's built-in fake timers (vi.useFakeTimers and vi.setSystemTime). Ensure the callback functions have explicit return types to maintain TypeScript type safety.
const mockNow = 1700000000000; // 2023-11-14T22:13:20.000Z
beforeEach((): void => {
vi.useFakeTimers();
vi.setSystemTime(mockNow);
});
afterEach((): void => {
vi.useRealTimers();
});References
- In TypeScript, ensure functions and mock implementations have explicit return types to maintain type safety and readability.
| expect(() => handleRateLimit(res)).toThrow(RateLimitError); | ||
|
|
||
| try { | ||
| handleRateLimit(res); | ||
| } catch (error) { | ||
| expect(error).toBeInstanceOf(RateLimitError); | ||
| expect(error.resetAt.getTime()).toBe(resetTimestamp * 1000); | ||
| } |
There was a problem hiding this comment.
Calling handleRateLimit twice is redundant. Additionally, in TypeScript strict mode, the caught error is typed as unknown, so accessing error.resetAt directly will cause a compilation error. Capturing the thrown error and asserting on it is cleaner and type-safe.
let thrownError: unknown;
try {
handleRateLimit(res);
} catch (error) {
thrownError = error;
}
expect(thrownError).toBeInstanceOf(RateLimitError);
expect((thrownError as RateLimitError).resetAt.getTime()).toBe(resetTimestamp * 1000);| expect(() => handleRateLimit(res)).toThrow(RateLimitError); | ||
|
|
||
| try { | ||
| handleRateLimit(res); | ||
| } catch (error) { | ||
| expect(error).toBeInstanceOf(RateLimitError); | ||
| const expectedResetTimestamp = Math.floor(mockNow / 1000) + 3600; | ||
| expect(error.resetAt.getTime()).toBe(expectedResetTimestamp * 1000); | ||
| } |
There was a problem hiding this comment.
Calling handleRateLimit twice is redundant. Additionally, in TypeScript strict mode, the caught error is typed as unknown, so accessing error.resetAt directly will cause a compilation error. Capturing the thrown error and asserting on it is cleaner and type-safe.
let thrownError: unknown;
try {
handleRateLimit(res);
} catch (error) {
thrownError = error;
}
expect(thrownError).toBeInstanceOf(RateLimitError);
const expectedResetTimestamp = Math.floor(mockNow / 1000) + 3600;
expect((thrownError as RateLimitError).resetAt.getTime()).toBe(expectedResetTimestamp * 1000);| expect(() => handleRateLimit(res)).toThrow(RateLimitError); | ||
|
|
||
| try { | ||
| handleRateLimit(res); | ||
| } catch (error) { | ||
| expect(error).toBeInstanceOf(RateLimitError); | ||
| const expectedResetTimestamp = Math.floor(mockNow / 1000) + 3600; | ||
| expect(error.resetAt.getTime()).toBe(expectedResetTimestamp * 1000); | ||
| } |
There was a problem hiding this comment.
Calling handleRateLimit twice is redundant. Additionally, in TypeScript strict mode, the caught error is typed as unknown, so accessing error.resetAt directly will cause a compilation error. Capturing the thrown error and asserting on it is cleaner and type-safe.
let thrownError: unknown;
try {
handleRateLimit(res);
} catch (error) {
thrownError = error;
}
expect(thrownError).toBeInstanceOf(RateLimitError);
const expectedResetTimestamp = Math.floor(mockNow / 1000) + 3600;
expect((thrownError as RateLimitError).resetAt.getTime()).toBe(expectedResetTimestamp * 1000);
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
| @@ -1,5 +1,6 @@ | |||
| import { describe, it, expect, vi, beforeEach } from 'vitest'; | |||
There was a problem hiding this comment.
afterEach がインポートされていないため、テスト実行時に ReferenceError: afterEach is not defined が発生します。ファイル先頭の import 文に afterEach が含まれていませんが、vitestのグローバルモードを有効にしていない場合(他のユーティリティを明示的にインポートしていることからその可能性が高い)、この参照はランタイムエラーになります。
| import { describe, it, expect, vi, beforeEach } from 'vitest'; | |
| import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/__tests__/apiUtils.test.ts
Line: 1
Comment:
`afterEach` がインポートされていないため、テスト実行時に `ReferenceError: afterEach is not defined` が発生します。ファイル先頭の import 文に `afterEach` が含まれていませんが、vitestのグローバルモードを有効にしていない場合(他のユーティリティを明示的にインポートしていることからその可能性が高い)、この参照はランタイムエラーになります。
```suggestion
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
```
How can I resolve this? If you propose a fix, please make it concise.| try { | ||
| handleRateLimit(res); | ||
| } catch (error) { | ||
| expect(error).toBeInstanceOf(RateLimitError); | ||
| expect(error.resetAt.getTime()).toBe(resetTimestamp * 1000); | ||
| } |
There was a problem hiding this comment.
TypeScript のストリクトモードでは
catch ブロックの変数は unknown 型になります。error.resetAt や error.resetAt.getTime() へのアクセスはコンパイルエラーとなります(TS18046: 'error' is of type 'unknown')。同パターンが3テスト全てに存在します。
| try { | |
| handleRateLimit(res); | |
| } catch (error) { | |
| expect(error).toBeInstanceOf(RateLimitError); | |
| expect(error.resetAt.getTime()).toBe(resetTimestamp * 1000); | |
| } | |
| try { | |
| handleRateLimit(res); | |
| } catch (error) { | |
| expect(error).toBeInstanceOf(RateLimitError); | |
| expect((error as RateLimitError).resetAt.getTime()).toBe(resetTimestamp * 1000); | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/__tests__/apiUtils.test.ts
Line: 84-89
Comment:
TypeScript のストリクトモードでは `catch` ブロックの変数は `unknown` 型になります。`error.resetAt` や `error.resetAt.getTime()` へのアクセスはコンパイルエラーとなります(`TS18046: 'error' is of type 'unknown'`)。同パターンが3テスト全てに存在します。
```suggestion
try {
handleRateLimit(res);
} catch (error) {
expect(error).toBeInstanceOf(RateLimitError);
expect((error as RateLimitError).resetAt.getTime()).toBe(resetTimestamp * 1000);
}
```
How can I resolve this? If you propose a fix, please make it concise.| expect(() => handleRateLimit(res)).toThrow(RateLimitError); | ||
|
|
||
| try { | ||
| handleRateLimit(res); | ||
| } catch (error) { | ||
| expect(error).toBeInstanceOf(RateLimitError); | ||
| expect(error.resetAt.getTime()).toBe(resetTimestamp * 1000); | ||
| } |
There was a problem hiding this comment.
catch ブロックのアサーションがサイレントスキップされる可能性
各テストで handleRateLimit(res) を2回呼び出しています(expect().toThrow() で1回、try/catch でもう1回)。try ブロックが何らかの理由でスローしない場合、catch 内の expect(error.resetAt.getTime())... は完全に実行されず、テストが誤ってグリーンになります。resetAt の値検証はこのパターンが唯一担っているため、expect.assertions(N) でアサーション実行数を保証することを検討してください。同パターンが3テスト全てに存在します。
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/__tests__/apiUtils.test.ts
Line: 82-89
Comment:
**catch ブロックのアサーションがサイレントスキップされる可能性**
各テストで `handleRateLimit(res)` を2回呼び出しています(`expect().toThrow()` で1回、`try/catch` でもう1回)。`try` ブロックが何らかの理由でスローしない場合、catch 内の `expect(error.resetAt.getTime())...` は完全に実行されず、テストが誤ってグリーンになります。`resetAt` の値検証はこのパターンが唯一担っているため、`expect.assertions(N)` でアサーション実行数を保証することを検討してください。同パターンが3テスト全てに存在します。
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com>
Added tests for handleRateLimit covering successful parses, missing headers, and invalid values.
PR created automatically by Jules for task 13419055119297966609 started by @is0692vs
Greptile Summary
handleRateLimit関数のユニットテストを追加するPRです。正常系(X-RateLimit-Resetヘッダあり)、ヘッダなし、無効値の3ケースをカバーしようとしていますが、現状ではビルドエラーおよびランタイムエラーが発生する問題があります。afterEachが vitest からインポートされていないため、テスト実行時にReferenceErrorが発生し全テストが失敗します。catchブロック内のerror変数がunknown型のまま.resetAtへアクセスしており、TypeScript のコンパイルが通りません(3テスト共通)。handleRateLimitを各テストで2回呼び出すパターンにより、resetAt値を検証する catch ブロックのアサーションがサイレントスキップされるリスクがあります。Confidence Score: 3/5
テストコードのみの変更ですがビルドが通らない状態のため、マージ前に修正が必要です。
afterEachのインポート漏れとunknown型の未キャストアクセスにより、このままではコンパイルおよびテスト実行自体が失敗します。プロダクションコードへの影響はありませんが、テストの信頼性を確保するには修正が必要です。src/lib/__tests__/apiUtils.test.ts— インポート漏れと型エラーの修正が必要です。Important Files Changed
handleRateLimitのテストを追加。afterEachのインポート漏れによるランタイムエラー、unknown型のerrorへの未キャストアクセスによるコンパイルエラー、およびcatchブロック内アサーションがサイレントスキップされる可能性の3点に問題あり。Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant Test as テストケース participant HRL as handleRateLimit() participant RLE as RateLimitError Note over Test: X-RateLimit-Reset ヘッダあり Test->>HRL: handleRateLimit(res) HRL->>HRL: res.headers.get("X-RateLimit-Reset") → "1700003600" HRL->>HRL: parseInt("1700003600", 10) → 1700003600 HRL->>RLE: new RateLimitError(1700003600) HRL-->>Test: "throw RateLimitError (resetAt = 2023-11-14T23:13:20.000Z)" Note over Test: ヘッダなし / 無効値 Test->>HRL: handleRateLimit(res) HRL->>HRL: res.headers.get("X-RateLimit-Reset") → null or NaN HRL->>HRL: Math.floor(Date.now() / 1000) + 3600 → フォールバック値 HRL->>RLE: new RateLimitError(fallback) HRL-->>Test: "throw RateLimitError (resetAt = now + 1h)"%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant Test as テストケース participant HRL as handleRateLimit() participant RLE as RateLimitError Note over Test: X-RateLimit-Reset ヘッダあり Test->>HRL: handleRateLimit(res) HRL->>HRL: res.headers.get("X-RateLimit-Reset") → "1700003600" HRL->>HRL: parseInt("1700003600", 10) → 1700003600 HRL->>RLE: new RateLimitError(1700003600) HRL-->>Test: "throw RateLimitError (resetAt = 2023-11-14T23:13:20.000Z)" Note over Test: ヘッダなし / 無効値 Test->>HRL: handleRateLimit(res) HRL->>HRL: res.headers.get("X-RateLimit-Reset") → null or NaN HRL->>HRL: Math.floor(Date.now() / 1000) + 3600 → フォールバック値 HRL->>RLE: new RateLimitError(fallback) HRL-->>Test: "throw RateLimitError (resetAt = now + 1h)"Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "test: add tests for handleRateLimit in a..." | Re-trigger Greptile