Skip to content

test: split integration suite by resource and tighten SDK/API contract#166

Open
Gabrielpanga wants to merge 1 commit into
masterfrom
test/integration-suite-per-resource
Open

test: split integration suite by resource and tighten SDK/API contract#166
Gabrielpanga wants to merge 1 commit into
masterfrom
test/integration-suite-per-resource

Conversation

@Gabrielpanga
Copy link
Copy Markdown
Member

Summary

Restructures the integration test suite from one 338-line tests/integration.test.ts monolith into per-resource specs under tests/integration/, modeled on plaid/plaid-node. Per-resource isolation: each spec that needs server state owns the full lifecycle of its own sandbox item (beforeAll creates and waits for UPDATED, afterAll deletes defensively).

Research note: I originally framed this as a "Stripe pattern" rewrite, but Stripe's open-source SDK doesn't actually run integration tests against the real API — they use stripe-mock (OpenAPI-driven HTTP fake) and spy-based unit tests. Plaid was the right reference for what was actually being asked for.

What's new

Helpers — tests/integration/helpers.ts

  • createClient / createSandboxItem / deleteItemSafely.
  • retry() for sandbox eventual consistency (mirrors Plaid's getTransactionsWithRetries).
  • RUN_ID + TEST_TAG — every test run is tagged with GITHUB_RUN_ID (CI) or local-<timestamp>. Resources that accept free-form identifiers (connect token's clientUserId, webhook URLs) get tagged so orphans from failed runs are identifiable.
  • captureRejection()BaseApi rejects with the parsed error response body (not a thrown Error with .statusCode), so error tests capture the body and let each test inspect it.
  • describeIntegration — auto-skip via describe.skip when PLUGGY_CLIENT_ID / PLUGGY_CLIENT_SECRET are absent.

Coverage additions vs the monolith

Spec New coverage
items.test.ts fetchItem shape + date instances
bills.test.ts fetchCreditCardBills / fetchCreditCardBill (skips silently when sandbox has no CREDIT account)
errors.test.ts 4xx responses for non-existent ids on fetchItem, fetchAccount, fetchTransaction, fetchConnector
accounts.test.ts fetchAccountStatements
transactions.test.ts updateTransactionCategory
connectors.test.ts validateParameters

Coverage retained from the monolith

Connectors list/get, accounts, transactions (cursor + by-id + dedup), investments + fetchAllInvestmentTransactions dedup, identity (by item + by id), consents, loans, categories, connect token, webhooks CRUD.

Why "no cleanup" (Plaid's choice) wasn't copied

Plaid's sandbox items are cheap; the suite assumes the platform garbage-collects them. Pluggy items are heavier — leaks pile up against the test tenant's quota. So every spec that creates an item registers afterAll with deleteItemSafely (try/catch + stderr on failure so leaks are visible but never mask test failures).

Why per-suite items (not shared via globalSetup)

You picked this option explicitly. Trade-off: ~30+ minutes of wall time per run (each suite waits a full sandbox sync) vs. true isolation between suites. Per-suite isolation means one broken test can't poison the next.

Out of scope (next PR)

PluggyPaymentsClient — recipients, requests, intents, payment customers, institutions, bulk, smart accounts, automatic PIX, scheduled, smart transfers. These need their own credentials path and live in a separate domain, so they belong in a focused follow-up.

package.json script tweaks

  • test: ignores tests/integration (directory) instead of the deleted single file.
  • test:integration: matches tests/integration explicitly.

Test plan

  • pnpm build — clean.
  • pnpm test — 11/11 passing (unit suite unchanged).
  • pnpm test:integration without credentials — 13 suites, 31 tests, all auto-skipped.
  • Nightly SDK Integration Tests workflow run against sandbox is green after merge.

🤖 Generated with Claude Code

…tract

Restructures tests/integration.test.ts (one 338-line file) into per-resource
specs under tests/integration/, modeled on plaid/plaid-node. Each spec
that needs server state owns the full lifecycle of its sandbox item:
beforeAll creates and waits for UPDATED, afterAll deletes defensively
(non-throwing). Plaid skips cleanup; Pluggy items are heavier so we keep
it.

Helpers (tests/integration/helpers.ts):
- createClient / createSandboxItem / deleteItemSafely
- retry() for sandbox eventual consistency (mirrors Plaid's
  getTransactionsWithRetries pattern)
- RUN_ID + TEST_TAG so resources created in a single run can be spotted
  later (clientUserId on connect tokens, webhook URLs, etc.)
- captureRejection() because BaseApi rejects with the parsed error body,
  not a thrown Error with .statusCode — we let each test inspect the
  body rather than fake a uniform shape

Coverage additions vs the monolith:
- items.test.ts (fetchItem shape, dates)
- bills.test.ts (fetchCreditCardBills / fetchCreditCardBill)
- errors.test.ts (4xx for fetchItem / fetchAccount / fetchTransaction /
  fetchConnector with non-existent ids)
- accounts.test.ts now exercises fetchAccountStatements
- transactions.test.ts now exercises updateTransactionCategory
- connectors.test.ts now exercises validateParameters

Coverage retained from the monolith: connectors list/get, accounts,
transactions (cursor + by-id + dedup on fetchAllTransactions),
investments (+ fetchAllInvestmentTransactions dedup), identity (by item
+ by id), consents, loans, categories, connect token, webhooks CRUD.

Out of scope (next PR): PluggyPaymentsClient (recipients, requests,
intents, smart accounts, automatic PIX, scheduled, smart transfers).

package.json:
- "test" ignores tests/integration (directory) instead of the deleted
  single file.
- "test:integration" matches tests/integration explicitly.

Local verification:
- pnpm build clean
- pnpm test still 11/11
- pnpm test:integration without creds: 13 suites skipped, 31 tests
  skipped (auto-skip via describeIntegration).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Gabrielpanga Gabrielpanga force-pushed the test/integration-suite-per-resource branch from c990d6a to efbdc32 Compare May 26, 2026 20:18
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