Skip to content

fix: update stripe unit tests for stripe v19+#1695

Closed
ToriChanIntegration wants to merge 2 commits intoCap-go:mainfrom
ToriChanIntegration:fix/stripe-redirects-test
Closed

fix: update stripe unit tests for stripe v19+#1695
ToriChanIntegration wants to merge 2 commits intoCap-go:mainfrom
ToriChanIntegration:fix/stripe-redirects-test

Conversation

@ToriChanIntegration
Copy link

@ToriChanIntegration ToriChanIntegration commented Feb 25, 2026

The stripe library v19+ uses createFetchHttpClient() which wasn't being mocked. This caused tests to fail with: 'Cannot read properties of undefined (reading 'createFetchHttpClient')'

This fix:

  • Mocks the stripe module with createFetchHttpClient as a static method
  • Mocks hono/adapter with getRuntimeKey function
  • Uses shared mock functions that can be configured per-test
  • Adds beforeEach to reset mocks between tests

Summary by CodeRabbit

  • Tests
    • Refactored payment-related tests to use centralized, hoisted Stripe mocks instead of per-test setups.
    • Consolidated environment/runtime mocking and added reset before each test for consistency.
    • Updated test assertions to rely on shared mocks while preserving existing success and error path coverage.

The stripe library v19+ uses createFetchHttpClient() which wasn't being
mocked. This caused tests to fail with:
'Cannot read properties of undefined (reading 'createFetchHttpClient')'

This fix:
- Mocks the stripe module with createFetchHttpClient as a static method
- Mocks hono/adapter with getRuntimeKey function
- Uses shared mock functions that can be configured per-test
- Adds beforeEach to reset mocks between tests
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

The test file for Stripe redirects was refactored to replace ad-hoc Stripe client construction and spy-based interactions with centralized mock implementations. Shared mock handlers and a beforeEach reset were added; tests now use pre-configured mocks for billingPortal.sessions.create, checkout.sessions.create, and prices.search.

Changes

Cohort / File(s) Summary
Stripe Test Mock Refactoring
tests/stripe-redirects.unit.test.ts
Hoisted vi.mock for Stripe supplying shared mocks (mockBillingPortalSessionsCreate, mockCheckoutSessionsCreate, mockPricesSearch); added getRuntimeKey mock and moved env mocking; added beforeEach to reset mocks; removed per-test inline client/spies; updated tests to assert against shared mocks and preserved error paths.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I tunneled through tests with a nimble little hop,
Collected loose spies and made mock piles stop,
Now sessions and prices all dance in a row,
beforeEach resets keep the pathways aglow,
Hop—clean tests, neat burrows—onward I go! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: updating stripe unit tests to be compatible with stripe v19+.
Description check ✅ Passed The description clearly explains the problem, the root cause (stripe v19+ introducing createFetchHttpClient), and the specific solutions implemented (mocking strategy and test structure changes).

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
Contributor

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/stripe-redirects.unit.test.ts`:
- Around line 13-29: The MockStripe class violates the
style/lines-between-class-members rule—add blank lines between the class members
to satisfy ESLint: insert a blank line after the static createFetchHttpClient
declaration, between the billingPortal and checkout property initialisers, and
between checkout and prices property initialisers in the MockStripe class so
each member is separated by a single empty line.
- Around line 4-6: The shared module-level mocks
(mockBillingPortalSessionsCreate, mockCheckoutSessionsCreate, mockPricesSearch)
cause race conditions with concurrent tests; move each vi.fn() declaration and
any mockResolvedValue/mockImplementation calls into the body of each
it.concurrent() test so every test gets its own isolated mocks, remove reliance
on the shared beforeEach reset, and ensure any test-specific mock behavior is
set inline in the test before exercising the code.
- Around line 8-41: Move the import of stripe (import * as stripe from
'../supabase/functions/_backend/utils/stripe.ts') to the top of the test file
immediately after the vitest imports and remove or replace the misleading
comment that says mocks must be before imports; keep the existing
vi.mock('stripe', ...) and vi.mock('hono/adapter', ...) calls (they are hoisted
by Vitest) and update the comment to state that vi.mock() calls are hoisted
automatically by Vitest so explicit ordering is unnecessary; ensure the mocked
symbols (MockStripe, mockBillingPortalSessionsCreate,
mockCheckoutSessionsCreate, mockPricesSearch) remain unchanged.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0540a4b and 8e8b040.

📒 Files selected for processing (1)
  • tests/stripe-redirects.unit.test.ts

- Move import to top of file
- Add blank lines between class members
- Update comment about vi.mock hoisting
Copy link
Contributor

@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: 1

♻️ Duplicate comments (1)
tests/stripe-redirects.unit.test.ts (1)

63-68: ⚠️ Potential issue | 🟠 Major

Shared beforeEach reset + it() instead of it.concurrent() — unresolved from previous review

All five tests (lines 75, 87, 100, 120, 140) still use it() instead of it.concurrent(), violating the project guideline. Additionally, the shared beforeEach mock-reset pattern is fundamentally incompatible with concurrent execution: one test's beforeEach or mid-test mockResolvedValue call can corrupt another test's state.

The fix is either to keep it.concurrent() and move mock setup inline per test (removing the shared beforeEach), or to scope mock declarations inside each test body entirely, as suggested in the prior review.

As per coding guidelines: "ALL test files run in parallel; use it.concurrent() instead of it() to run tests within the same file in parallel for faster CI/CD."

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

In `@tests/stripe-redirects.unit.test.ts` around lines 63 - 68, Change tests to
run concurrently and avoid shared beforeEach mock resets: replace each it() with
it.concurrent() and remove the shared beforeEach() that calls
mockBillingPortalSessionsCreate.mockReset(),
mockCheckoutSessionsCreate.mockReset(), and mockPricesSearch.mockReset();
instead, initialize and reset those mocks inside each test body (or
declare/initialize fresh mocks scoped to each it.concurrent() block) so no
test-mutating setup is shared across tests (locate usage by the symbols
beforeEach, mockBillingPortalSessionsCreate, mockCheckoutSessionsCreate,
mockPricesSearch, and the test cases currently using it()).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/stripe-redirects.unit.test.ts`:
- Around line 5-7: The three mock variables (mockBillingPortalSessionsCreate,
mockCheckoutSessionsCreate, mockPricesSearch) are declared with const and cause
a temporal dead zone when used inside the vi.mock factory; replace their plain
const declarations with hoisted declarations by assigning them via vi.hoisted()
so they are available to the hoisted vi.mock factory and test scope, and update
any references inside the vi.mock factory and tests to use these hoisted symbols
(ensure the names match exactly).

---

Duplicate comments:
In `@tests/stripe-redirects.unit.test.ts`:
- Around line 63-68: Change tests to run concurrently and avoid shared
beforeEach mock resets: replace each it() with it.concurrent() and remove the
shared beforeEach() that calls mockBillingPortalSessionsCreate.mockReset(),
mockCheckoutSessionsCreate.mockReset(), and mockPricesSearch.mockReset();
instead, initialize and reset those mocks inside each test body (or
declare/initialize fresh mocks scoped to each it.concurrent() block) so no
test-mutating setup is shared across tests (locate usage by the symbols
beforeEach, mockBillingPortalSessionsCreate, mockCheckoutSessionsCreate,
mockPricesSearch, and the test cases currently using it()).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e8b040 and 8e1c09e.

📒 Files selected for processing (1)
  • tests/stripe-redirects.unit.test.ts

Comment on lines +5 to +7
const mockBillingPortalSessionsCreate = vi.fn()
const mockCheckoutSessionsCreate = vi.fn()
const mockPricesSearch = vi.fn()
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Vitest vi.hoisted() vi.mock factory temporal dead zone

💡 Result:

In Vitest, vi.mock() is hoisted to the top of the file and runs before all static imports (Vitest rewrites imports to make this possible). That also means the mock factory is evaluated before your file’s top-level const/let initializers run, so referencing a later-declared binding from inside the factory can throw a TDZ error like:

ReferenceError: Cannot access 'x' before initialization (temporal dead zone) [1].

Why it happens

This fails because mySpy is still in TDZ when the hoisted mock factory runs:

const mySpy = vi.fn()

vi.mock('./dep', () => ({ fn: mySpy })) // TDZ -> ReferenceError

Vitest explicitly warns that you “cannot use any variables inside the factory that are defined outside the factory” because of hoisting [1].

Fix option A (recommended): vi.hoisted()

Define the values you need for the factory inside vi.hoisted() (declared before vi.mock) and then reference them:

import { vi } from 'vitest'

const mocks = vi.hoisted(() => ({
  mySpy: vi.fn(),
}))

vi.mock('./dep', () => ({
  fn: mocks.mySpy,
}))

This is the documented way to “reference some variables outside of its scope” for a hoisted vi.mock() [1] and is a common solution in practice [2].

Fix option B: vi.doMock() (not hoisted)

If you need to reference normal top-level variables, use vi.doMock() (it is not hoisted), with the tradeoff that it only affects subsequent imports [1]:

const mySpy = vi.fn()

vi.doMock('./dep', () => ({ fn: mySpy }))
const { fn } = await import('./dep') // import after doMock

Sources: Vitest vi.mock / vi.hoisted API docs [1], example solution pattern [2].

[1] (vitest.dev)
[2] (stackoverflow.com)

Citations:


🏁 Script executed:

cd /tmp && find . -name "stripe-redirects.unit.test.ts" 2>/dev/null | head -5

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

find . -type f -name "stripe-redirects.unit.test.ts" 2>/dev/null

Repository: Cap-go/capgo

Length of output: 93


🏁 Script executed:

cat -n ./tests/stripe-redirects.unit.test.ts | head -80

Repository: Cap-go/capgo

Length of output: 2613


Fix temporal dead zone error by wrapping mock variables in vi.hoisted()

vi.mock() is hoisted by Vitest before module-level code executes, causing the const declarations at lines 5–7 to be in their temporal dead zone when the factory closure runs. This triggers a ReferenceError: Cannot access 'x' before initialization. Vitest's documentation explicitly prescribes vi.hoisted() as the solution for variables shared between a vi.mock factory and test scope.

Fix using `vi.hoisted()`
+const { mockBillingPortalSessionsCreate, mockCheckoutSessionsCreate, mockPricesSearch } = vi.hoisted(() => ({
+  mockBillingPortalSessionsCreate: vi.fn(),
+  mockCheckoutSessionsCreate: vi.fn(),
+  mockPricesSearch: vi.fn(),
+}))
+
-// Shared mock functions that can be accessed by tests
-const mockBillingPortalSessionsCreate = vi.fn()
-const mockCheckoutSessionsCreate = vi.fn()
-const mockPricesSearch = vi.fn()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const mockBillingPortalSessionsCreate = vi.fn()
const mockCheckoutSessionsCreate = vi.fn()
const mockPricesSearch = vi.fn()
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import * as stripe from '../supabase/functions/_backend/utils/stripe.ts'
const { mockBillingPortalSessionsCreate, mockCheckoutSessionsCreate, mockPricesSearch } = vi.hoisted(() => ({
mockBillingPortalSessionsCreate: vi.fn(),
mockCheckoutSessionsCreate: vi.fn(),
mockPricesSearch: vi.fn(),
}))
// vi.mock is hoisted by Vitest before all imports, so MockStripe is in place
// when stripe.ts is evaluated — no manual ordering needed.
vi.mock('stripe', () => {
return {
__esModule: true,
default: class MockStripe {
static createFetchHttpClient = vi.fn().mockReturnValue({})
billingPortal = {
sessions: {
create: mockBillingPortalSessionsCreate,
},
}
checkout = {
sessions: {
create: mockCheckoutSessionsCreate,
},
}
prices = {
search: mockPricesSearch,
}
},
}
})
vi.mock('hono/adapter', () => ({
env: () => ({
WEBAPP_URL: 'https://capgo.test',
STRIPE_SECRET_KEY: 'sk_test_123',
}),
getRuntimeKey: () => 'node',
}))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/stripe-redirects.unit.test.ts` around lines 5 - 7, The three mock
variables (mockBillingPortalSessionsCreate, mockCheckoutSessionsCreate,
mockPricesSearch) are declared with const and cause a temporal dead zone when
used inside the vi.mock factory; replace their plain const declarations with
hoisted declarations by assigning them via vi.hoisted() so they are available to
the hoisted vi.mock factory and test scope, and update any references inside the
vi.mock factory and tests to use these hoisted symbols (ensure the names match
exactly).

@sonarqubecloud
Copy link

@riderx riderx closed this Mar 3, 2026
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.

2 participants