Skip to content

🧪 Add tests for handleRateLimit#451

Open
is0692vs wants to merge 2 commits into
mainfrom
test-apiutils-rate-limit-13419055119297966609
Open

🧪 Add tests for handleRateLimit#451
is0692vs wants to merge 2 commits into
mainfrom
test-apiutils-rate-limit-13419055119297966609

Conversation

@is0692vs

@is0692vs is0692vs commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

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

Filename Overview
src/lib/tests/apiUtils.test.ts 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)"
Loading
%%{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)"
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
src/lib/__tests__/apiUtils.test.ts:1
`afterEach` がインポートされていないため、テスト実行時に `ReferenceError: afterEach is not defined` が発生します。ファイル先頭の import 文に `afterEach` が含まれていませんが、vitestのグローバルモードを有効にしていない場合(他のユーティリティを明示的にインポートしていることからその可能性が高い)、この参照はランタイムエラーになります。

```suggestion
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
```

### Issue 2 of 3
src/lib/__tests__/apiUtils.test.ts:84-89
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);
      }
```

### Issue 3 of 3
src/lib/__tests__/apiUtils.test.ts:82-89
**catch ブロックのアサーションがサイレントスキップされる可能性**

各テストで `handleRateLimit(res)` を2回呼び出しています(`expect().toThrow()` で1回、`try/catch` でもう1回)。`try` ブロックが何らかの理由でスローしない場合、catch 内の `expect(error.resetAt.getTime())...` は完全に実行されず、テストが誤ってグリーンになります。`resetAt` の値検証はこのパターンが唯一担っているため、`expect.assertions(N)` でアサーション実行数を保証することを検討してください。同パターンが3テスト全てに存在します。

Reviews (1): Last reviewed commit: "test: add tests for handleRateLimit in a..." | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 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 @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
github-user-summary Ignored Ignored Jul 10, 2026 7:15am

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@is0692vs, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 24 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 73a16367-a3da-45b1-b02c-e2f466ce7bfa

📥 Commits

Reviewing files that changed from the base of the PR and between 05bc250 and d9ef1a2.

📒 Files selected for processing (1)
  • src/lib/__tests__/apiUtils.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test-apiutils-rate-limit-13419055119297966609

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Comment thread src/lib/__tests__/apiUtils.test.ts Outdated
Comment on lines +62 to +72
let originalDateNow;
const mockNow = 1700000000000; // 2023-11-14T22:13:20.000Z

beforeEach(() => {
originalDateNow = Date.now;
Date.now = vi.fn(() => mockNow);
});

afterEach(() => {
Date.now = originalDateNow;
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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
  1. In TypeScript, ensure functions and mock implementations have explicit return types to maintain type safety and readability.

Comment on lines +82 to +89
expect(() => handleRateLimit(res)).toThrow(RateLimitError);

try {
handleRateLimit(res);
} catch (error) {
expect(error).toBeInstanceOf(RateLimitError);
expect(error.resetAt.getTime()).toBe(resetTimestamp * 1000);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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);

Comment on lines +95 to +103
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);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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);

Comment on lines +113 to +121
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);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Comment thread src/lib/__tests__/apiUtils.test.ts Outdated
@@ -1,5 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 afterEach がインポートされていないため、テスト実行時に ReferenceError: afterEach is not defined が発生します。ファイル先頭の import 文に afterEach が含まれていませんが、vitestのグローバルモードを有効にしていない場合(他のユーティリティを明示的にインポートしていることからその可能性が高い)、この参照はランタイムエラーになります。

Suggested change
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.

Comment on lines +84 to +89
try {
handleRateLimit(res);
} catch (error) {
expect(error).toBeInstanceOf(RateLimitError);
expect(error.resetAt.getTime()).toBe(resetTimestamp * 1000);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 TypeScript のストリクトモードでは catch ブロックの変数は unknown 型になります。error.resetAterror.resetAt.getTime() へのアクセスはコンパイルエラーとなります(TS18046: 'error' is of type 'unknown')。同パターンが3テスト全てに存在します。

Suggested change
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.

Comment on lines +82 to +89
expect(() => handleRateLimit(res)).toThrow(RateLimitError);

try {
handleRateLimit(res);
} catch (error) {
expect(error).toBeInstanceOf(RateLimitError);
expect(error.resetAt.getTime()).toBe(resetTimestamp * 1000);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant