OUT-3586: integration tests for price.created with testcontainers - #221
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
e3080f0 to
e1e48e2
Compare
ab0cf7f to
0c5f5c3
Compare
priosshrsth
left a comment
There was a problem hiding this comment.
I think we should use a factory helpers to seed the data. But the PR looks good to me.
Greptile SummaryThis PR introduces a reusable Vitest integration test harness for Confidence Score: 5/5Safe to merge — all findings are P2 style suggestions with no correctness or data-integrity impact The harness architecture is sound: try/finally on migration client (addressing the previously flagged concern), deterministic test ordering via fileParallelism: false, clean per-test state via TRUNCATE + installMockApis, and well-typed mock factories. The two remaining comments are maintenance notes, not bugs. All 28 tests are green locally per the PR author. No files require special attention — the hardcoded table list in testDb.ts and the isolate: false footgun are documented trade-offs, not blocking issues. Important Files Changed
Sequence DiagramsequenceDiagram
participant V as Vitest Runner
participant GS as globalSetup.ts
participant TC as testcontainers Postgres
participant SF as setup.ts (setupFiles)
participant T as Test File
participant W as postWebhook helper
participant R as Next.js Route (NTARH)
participant DB as Test DB
V->>GS: run globalSetup
GS->>TC: start PostgreSqlContainer
TC-->>GS: connection URI
GS->>DB: drizzle migrate (try/finally)
GS->>V: inject DATABASE_URL into process.env
V->>SF: load setupFiles (vi.mock CopilotAPI, IntuitAPI, Sentry)
V->>T: evaluate test file
loop each test
T->>DB: truncateAllTestTables (beforeEach)
T->>T: installMockApis sets mockImplementation
T->>DB: seed portal, settings, optional product sync
T->>W: postWebhook(payload)
W->>R: NTARH POST webhook route with auth stub
R->>T: mock CopilotAPI.getTokenPayload returns portalId
R->>DB: lookup portal connection + settings
R->>T: mock CopilotAPI.getProduct / IntuitAPI calls
R->>DB: insert qb_product_sync + qb_sync_logs or rollback
W-->>T: Response
T->>DB: assert rows in QBProductSync, QBSyncLog
T->>T: vi.clearAllMocks afterEach resets counts not impl
end
V->>GS: teardown container.stop()
Reviews (5): Last reviewed commit: "fix(OUT-3586): seed tokenSetTime so test..." | Re-trigger Greptile |
… Postgres
Adds a reusable integration-test harness that spins up an ephemeral Postgres
via testcontainers, applies Drizzle migrations, and wires it to the Next.js
route handlers through next-test-api-route-handler. The harness is split
from the existing unit project via Vitest `projects` so unit tests keep
running fast without Docker.
Key pieces:
- globalSetup.ts starts the container, sets DATABASE_URL, runs migrations
- setup.ts provides shared module mocks (CopilotAPI, IntuitAPI, Sentry)
that avoid loading copilot-node-sdk's broken ESM directory import
- helpers/{testDb,seed,mocks}.ts expose truncate + seed + mock-install utilities
- .env.test holds non-secret stubs for src/config env vars
- Integration project runs single-fork, no file parallelism, shares the
container across files (TRUNCATE between tests)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Integration tests were returning 400 because addSyncBreadcrumb in src/utils/sentry.ts calls Sentry.addBreadcrumb, which wasn't stubbed in the shared @sentry/nextjs mock. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the six price.created integration tests into a priceCreated/ subfolder and extract shared setup into test/helpers/webhook.ts and test/helpers/priceCreatedTestSetup.ts so subsequent webhook suites can reuse the same scaffolding without duplicating mock-wiring and request boilerplate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
src/config/index.ts unconditionally loaded .env on import, which backfilled any env var not stubbed in .env.test from the developer's local .env file during test runs. Harmless for mocked integration tests but a real risk for the upcoming nightly smoke tests (OUT-3649) that hit real QuickBooks and Copilot APIs. Guard the dotenv call on NODE_ENV !== 'test' so tests only see what globalSetup loads explicitly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the stale singleFork=true reference with the actual mechanism — pool: 'forks' + fileParallelism: false — so a future maintainer searching the config for singleFork doesn't come up empty. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
f99ce28 to
968aa29
Compare
Without tokenSetTime, isTokenFresh() returns false and getValidQbTokens triggers a real HTTP call to Intuit's OAuth endpoint, which rejects the stub INTUIT_CLIENT_ID with invalid_client and fails every price.created integration test with a 400. Seeding tokenSetTime keeps the token in the fresh window so the refresh path is skipped entirely. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
testcontainers, real Drizzle migrations, real Next.js route vianext-test-api-route-handler, mockedCopilotAPI/IntuitAPI/Sentryprice.createdwebhook scenarios: happy path, flag-off early return, idempotency, multi-price naming suffix, QB createItem failure + tx rollback, Copilot product 404What's covered
priceCreated.happyPathqb_product_syncrow + SUCCESS log writtenpriceCreated.flagOffcreateNewProductFlag=false→ no API calls, no rowspriceCreated.idempotencypriceCreated.multiPrice" (1)"priceCreated.qbFailurecreateItemthrows → tx rolled back, FAILED log outside the txpriceCreated.copilotNotFoundgetProductundefined →APIError(404)→ FAILED logAll 28 tests green locally (
yarn test).Architecture notes
pool: 'forks'+fileParallelism: false+isolate: falseso all integration tests share one container with deterministic orderglobalSetup.tssetsDATABASE_URLfrom the container before anysrc/import — workers inherit env via forkvi.mock(..., factory)— explicit factories avoid loadingcopilot-node-sdk(which has a broken ESM directory import)installMockApis,seedHealthyPortal,truncateAllTestTables) are reusable for the upcominginvoice.created/payment.succeededtestsFollow-ups (not in this PR — tracked in OUT-3586, to be split into their own tickets)
.github/workflows/test.yml) runningyarn teston every PR + push tomasterinvoice.created,payment.succeeded, etc.price.created— income-account creation path, special-character handling, auth-token-expired branch, 429 retry behaviorProductService#webhookPriceCreatedcallsunsetTransaction()inside thedb.transactioncallback; cleanup is skipped on any throw. Same pattern likely exists in otherBaseServicesubclasses.Test plan
yarn test --project unit— unit tests still pass without Dockeryarn test --project integration— all 6 scenarios green (requires Docker)yarn test— both projects, sequential, all greenLinear
OUT-3586 (sub-issue of OUT-3546): https://linear.app/assemblycom/issue/OUT-3586
🤖 Generated with Claude Code