OUT-3946: add integration tests for product.created webhook - #65
Conversation
Cover the product.created flow end-to-end on the testcontainers harness: happy path (Xero item created, synced_items row, success sync_log), both sync-disabled gates (workspace isSyncEnabled + automatic product sync), already-mapped idempotency, and Xero createItems failure (FAILED sync_log + failed_syncs, 500). Adds a per-flow setup helper, a webhook fixture, a seedSyncedItem seeder, and a createItems mock default. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Greptile SummaryAdds integration tests for the
Confidence Score: 5/5Test-only change; no production code is touched. All five branches of the webhook flow are covered by real DB assertions against a containerised Postgres instance. Every changed file is a test helper or test case. The previous round's feedback (unique mock IDs for batch calls, failedSyncs assertion on happy path and idempotency) has been fully addressed. UUID constants are now valid v4 format so schema validation passes. No logic, no migrations, no production risk. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[POST /api/webhook] --> B{isSyncEnabled?}
B -- false --> C[200 no DB writes]
B -- true --> D{syncProductsAutomatically?}
D -- false --> E[200 no DB writes]
D -- true --> F{product already in synced_items?}
F -- yes --> G[200 row unchanged]
F -- no --> H[XeroAPI.createItems]
H -- throws --> I[500 sync_log FAILED failed_syncs row]
H -- success --> J[insert synced_items sync_log SUCCESS 200]
%%{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"}}}%%
flowchart TD
A[POST /api/webhook] --> B{isSyncEnabled?}
B -- false --> C[200 no DB writes]
B -- true --> D{syncProductsAutomatically?}
D -- false --> E[200 no DB writes]
D -- true --> F{product already in synced_items?}
F -- yes --> G[200 row unchanged]
F -- no --> H[XeroAPI.createItems]
H -- throws --> I[500 sync_log FAILED failed_syncs row]
H -- success --> J[insert synced_items sync_log SUCCESS 200]
Reviews (2): Last reviewed commit: "docs(OUT-3946): simplify test helper and..." | Re-trigger Greptile |
createItems now gives each batch item its own uuid (first stays TEST_XERO_ITEM_ID), mirroring real Xero instead of repeating one id. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Happy-path and already-mapped tests now check failed_syncs has no rows, so a stray failure record can't slip through, matching the negative-path tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Shorten verbose block comments to plain one-liners. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| // Valid v4 uuids (version nibble 4, variant nibble 8) — z.uuid() validates both. | ||
| export const TEST_TENANT_ID = '11111111-1111-4111-8111-111111111111' | ||
| export const TEST_INTERNAL_USER_ID = '22222222-2222-4222-8222-222222222222' | ||
| export const TEST_WEBHOOK_TOKEN = 'test-token-xyz' | ||
| export const TEST_ACCESS_TOKEN = 'test-access-token' | ||
| export const TEST_REFRESH_TOKEN = 'test-refresh-token' | ||
| // Copilot product id and the Xero item id it maps to (both stored in uuid columns). | ||
| export const TEST_PRODUCT_ID = '33333333-3333-4333-8333-333333333333' | ||
| export const TEST_XERO_ITEM_ID = '44444444-4444-4444-8444-444444444444' | ||
| // A second Xero item id, for asserting a pre-existing mapping is left untouched. | ||
| export const TEST_OTHER_XERO_ITEM_ID = '99999999-9999-4999-8999-999999999999' |
There was a problem hiding this comment.
I think exporting all these constants individually will only make it harder. We need to understand each thing. What if we had single config or const object. Or even multiple objects that holds similiar data together? Might be more intuitive to use them. wdyt?
There was a problem hiding this comment.
Yes it make sense. I will create a separate config file and export objects with similar data. Thanks.
Move the scattered TEST_* constants out of seed.ts into test/helpers/constants.ts, grouped by domain (TEST_PORTAL, TEST_TOKENS, TEST_PRODUCT, TEST_XERO_ITEM). Files that only need an id no longer pull in the DB-heavy seed module. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Integration tests for the
product.createdwebhook flow, built on the testcontainers harness from OUT-3933 (#64).Cases covered
synced_items, writes a successsync_log.syncProductsAutomatically=false— service-level gate: 200, no Xero call, no rows.isSyncEnabled=false— controller-level gate (short-circuits before dispatch): 200, no Xero call, no rows.createItemsfails — no mapping row, FAILEDsync_log+failed_syncsrecord, returns 500.Harness additions
productCreatedTestSetup.ts— per-flowbeforeEach(truncate + mock install) returning a live{ copilot, xero }handle.productCreated.webhook.tsfixture,seedSyncedItemseeder, and acreateItemsdefault on the Xero mock.Notes
onConflictDoNothingorphan-cleanup race branch is intentionally not covered — it only fires on a true insert-time race and can't be triggered deterministically (documented inidempotency.test.ts).Verification
pnpm typecheck(src + test) — cleanbiome check— cleanpnpm test— 8/8 passing (6 files)🤖 Generated with Claude Code