Skip to content

Commit 6fe064e

Browse files
test(OUT-3953): assert invoice.deleted creates the missing xero invoice then deletes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 2ab120a commit 6fe064e

1 file changed

Lines changed: 56 additions & 0 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { buildDeletedInvoiceWebhook } from '@test/fixtures/deletedInvoice.webhook'
2+
import { buildInvoiceCreatedWebhook } from '@test/fixtures/invoiceCreated.webhook'
3+
import { TEST_INVOICE, TEST_XERO_INVOICE } from '@test/helpers/constants'
4+
import { enableDeleteSyncForTest } from '@test/helpers/deleteSyncFlag'
5+
import { createMockCopilotAPI } from '@test/helpers/mocks'
6+
import { seedConnectedPortal, seedSyncedInvoice } from '@test/helpers/seed'
7+
import { postWebhook } from '@test/helpers/webhook'
8+
import { setupWebhookTest } from '@test/helpers/webhookTestSetup'
9+
import { and, eq } from 'drizzle-orm'
10+
import { describe, expect, it, vi } from 'vitest'
11+
import db from '@/db'
12+
import { syncedInvoices } from '@/db/schema/syncedInvoices.schema'
13+
import { SyncEventType, SyncStatus, syncLogs } from '@/db/schema/syncLogs.schema'
14+
15+
describe('POST /api/webhook — invoice.deleted missing Xero invoice', () => {
16+
// No xeroInvoiceId, so the service re-fetches the Copilot invoice and creates
17+
// it in Xero before voiding and deleting.
18+
const apis = setupWebhookTest(() => ({
19+
copilot: createMockCopilotAPI({
20+
getInvoice: vi.fn().mockResolvedValue(buildInvoiceCreatedWebhook().data),
21+
}),
22+
}))
23+
enableDeleteSyncForTest()
24+
25+
it('creates the missing Xero invoice, voids it, then deletes it', async () => {
26+
await seedConnectedPortal()
27+
await seedSyncedInvoice({ status: 'pending', xeroInvoiceId: null })
28+
29+
const res = await postWebhook(buildDeletedInvoiceWebhook())
30+
expect(res.status).toBe(200)
31+
32+
// Missing invoice is created, then voided, then deleted.
33+
expect(apis.copilot.getInvoice).toHaveBeenCalledTimes(1)
34+
expect(apis.xero.createInvoice).toHaveBeenCalledTimes(1)
35+
expect(apis.xero.voidInvoice).toHaveBeenCalledTimes(1)
36+
expect(apis.xero.deleteInvoice).toHaveBeenCalledTimes(1)
37+
38+
// Invoice row now mapped to Xero and marked success.
39+
const invoices = await db.select().from(syncedInvoices)
40+
expect(invoices).toHaveLength(1)
41+
expect(invoices[0]).toMatchObject({
42+
copilotInvoiceId: TEST_INVOICE.id,
43+
xeroInvoiceId: TEST_XERO_INVOICE.id,
44+
status: 'success',
45+
})
46+
47+
// A deleted success log written.
48+
const deletedLogs = await db
49+
.select()
50+
.from(syncLogs)
51+
.where(
52+
and(eq(syncLogs.eventType, SyncEventType.DELETED), eq(syncLogs.status, SyncStatus.SUCCESS)),
53+
)
54+
expect(deletedLogs).toHaveLength(1)
55+
})
56+
})

0 commit comments

Comments
 (0)