test(OUT-3708): unit + integration tests for invoice.created webhook#244
Draft
SandipBajracharya wants to merge 19 commits into
Draft
test(OUT-3708): unit + integration tests for invoice.created webhook#244SandipBajracharya wants to merge 19 commits into
SandipBajracharya wants to merge 19 commits into
Conversation
Canary test verifying the shared infra (seeders, fixture, mocks, setup helper) wires up correctly. Asserts QB customer + invoice creation, mapping rows persisted, and a SUCCESS sync log written. Mock defaults required no adjustment — flow is greener than expected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The per-file vi.mock factory in expiringSweep.test.ts didn't survive
Vitest's shared module registry under pool:forks + fileParallelism:false +
isolate:false. Once any earlier test transitively loaded @/utils/intuit
(via auth.service.ts), the per-file mock no longer applied.
Moves the module mock to test/integration/setup.ts so it's installed
before any test-file imports run, matching the existing pattern for
CopilotAPI/IntuitAPI/Sentry. expiringSweep.test.ts wires its per-test
behavior via vi.mocked(Intuit.getInstance) inside beforeEach.
The mock singleton is pinned on globalThis. Vitest's setupFiles get
evaluated more than once per run when separate test files spin up
fresh module-graph contexts under this config — a naive
`vi.mock(..., () => ({ default: { getInstance: vi.fn() } }))` produces
a different vi.fn() per factory invocation, so the test-file wiring
ends up on a different mock than the one tokenRefresh.ts closed over.
Storing the singleton on globalThis (single process, since
fileParallelism is false) makes every factory invocation return the
same getInstance mock, so beforeEach wiring is what the runtime sees.
Verified: 10 consecutive full integration-suite runs pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…Exists comment - happyPath: default getAnItem(undefined, id, true) mock returned undefined, collapsing the mapped-product path into the Assembly Service fallback. Both branches happened to yield Id '999' via createItem default. The ItemRef assertion was passing via the wrong path. Override getAnItem to return a real item when queried by id and pin the mapped-product branch via two new assertions: createItem is invoked exactly once for 'Assembly Service' (handleServiceItem still runs because seedHealthyPortal does not set serviceItemRef) and the line Description comes from copilot.getProduct, which only happens on the mapped path. - invoiceAlreadyExists: corrected the comment claiming the existence check runs after customer resolution. It runs first, before any service call. Added an explicit qb_customers count = 0 assertion to make the invariant load-bearing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
invoice.createdwebhook handler.priceCreated/integration suite shape (testcontainers + module-mocked Copilot/Intuit).expiringSweep.test.tssurfaced by the higher integration-file count.Linear: OUT-3708
What's covered
Integration (
test/integration/quickbooks/invoiceCreated/):happyPath— OPEN status, mapped product, new customer (asserts mapped-product path viaDescriptionfromcopilot.getProduct)draftSkip— DRAFT gate sits before claimidempotency— pre-existing CREATED log blocks second deliveryinvoiceAlreadyExists—getInvoiceByNumbershort-circuits at the top ofwebhookInvoiceCreated, before customer resolutionqbCreateInvoiceFails— claim row flipped to FAILED witherrorMessagestatusPaidCreatesPayment— PAID branch creates Payment withLinkedTxnlinked to the just-created invoiceoneOffLineItem— line item without productId/priceId routes through the Assembly Service refcreateNewProductFlagOff— flag-off + unmapped line item falls back to one-off (noqb_product_syncrow)useCompanyNameFlag— company-only payload + flag-on creates customer withcustomerType='company'Unit (
test/unit/api/quickbooks/webhook/handleInvoiceCreated.test.ts):Side fix
expiringSweep.test.tswas using a per-filevi.mock('@/utils/intuit', …)factory. Under integration'spool:forks + fileParallelism:false + isolate:falseconfig, that mock didn't survive across files once any earlier test transitively loaded the real@/utils/intuit(every webhook test does, viaauth.service.ts). With master's 7 integration files this never landed in the bad slot; with this branch's 16, it landed ~50% of the time.Fix: moved the module mock to
test/integration/setup.tsand pinned the singleton onglobalThis(Vitest re-evaluates the setupFiles factory more than once per session under these flags — a naive shared mock still produces multiplevi.fn()instances). Verified with 10 consecutive full-suite runs all green. Full rationale in commit2664c98and in the inline comment insetup.ts.Out of scope (separate tickets)
delayMs)Test plan
test.yml+lint.yml)yarn test— 22 files / 101 tests passing (verified 10/10 consecutive runs locally)yarn lint:check— cleanyarn prettier:check— cleanglobalThismock pin intest/integration/setup.tsis intelligible🤖 Generated with Claude Code