import * as TestApp from '../../../test/App.js' import * as TestFunding from '../../../test/Funding.js' import * as WebhookQueueEvents from '../../db/tables/webhookQueueEvents.js' import * as Webhooks from '../Webhooks.js' import * as Deposit from './Deposit.js' import * as FundingDepositAddress from './DepositAddress.js' import * as DepositWebhook from './DepositWebhook.js' const db = TestApp.database() describe('stage', () => { test('matches owner, environment, project, and filters with stable event ids', async () => { const orgId = 'org_funding_deposit_webhook' const projectId = 'prj_funding_deposit_webhook' const address = await FundingDepositAddress.create(db, { apiKeyId: 'key_funding_deposit_webhook', deliveryStrategy: 'provider', environment: 'production', now: new Date('2026-08-12T00:00:00.000Z'), orgId, projectId, providerOutputToken: TestFunding.providerOutputToken(), snapshot: TestFunding.depositAddressSnapshot({ address: 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE', recipient: `0x${'ab'.repeat(20)}`, }), }) const deposit = await Deposit.create(db, { now: new Date('2026-08-12T00:01:00.000Z'), providerRequestId: 'relay_deposit_webhook', snapshot: TestFunding.depositSnapshot(address.id), sourceTransactionHash: `0x${'aa'.repeat(32)}`, sourceTransferIndex: 0, }) const completed = await Deposit.transition(db, { expectedVersion: deposit.version, id: deposit.id, status: 'completed', }) if (completed.type !== 'applied') throw new Error('Expected deposit transition to apply.') const filters = { depositAddressId: address.id, recipient: address.recipient.toLowerCase(), status: 'completed', } const org = await subscription({ filters, orgId }) const project = await subscription({ filters, orgId, projectId }) await subscription({ orgId, projectId: 'prj_funding_deposit_sibling' }) await subscription({ environment: 'sandbox', orgId }) await subscription({ filters: { status: 'refunded' }, orgId }) const first = await DepositWebhook.stage(db, { records: [completed.record] }) const second = await DepositWebhook.stage(db, { records: [completed.record] }) expect({ created: first.created, repeated: second.created }).toEqual({ created: 2, repeated: 0, }) expect(first.references.map((reference) => reference.subscriptionId)).toEqual([ org.id, project.id, ]) const event = await WebhookQueueEvents.get(db, first.references[0]!) expect(event?.envelope).toMatchObject({ chainId: 4217, data: { id: completed.record.id, status: 'completed' }, id: first.references[0]!.eventId, subscriptionId: org.id, type: 'funding:deposit.updated', }) }) }) describe('transition', () => { test('commits the deposit version with its webhook obligation', async () => { const orgId = 'org_funding_deposit_transition' const address = await FundingDepositAddress.create(db, { apiKeyId: 'key_funding_deposit_transition', deliveryStrategy: 'provider', environment: 'production', now: new Date('2026-08-12T01:00:00.000Z'), orgId, providerOutputToken: TestFunding.providerOutputToken(), snapshot: TestFunding.depositAddressSnapshot(), }) const deposit = await Deposit.create(db, { now: new Date('2026-08-12T01:01:00.000Z'), providerRequestId: 'relay_deposit_transition', snapshot: TestFunding.depositSnapshot(address.id), sourceTransactionHash: `0x${'bb'.repeat(32)}`, sourceTransferIndex: 0, }) await subscription({ filters: {}, orgId }) const transitioned = await DepositWebhook.transition(db, { expectedVersion: deposit.version, id: deposit.id, status: 'bridging', }) if (transitioned.result.type !== 'applied') throw new Error('Expected applied transition.') expect(transitioned.references).toHaveLength(1) expect(await WebhookQueueEvents.get(db, transitioned.references[0]!)).toMatchObject({ envelope: { data: { id: deposit.id, status: 'bridging' }, id: transitioned.references[0]!.eventId, }, }) }) }) function subscription(options: subscription.Options) { return Webhooks.createSubscription(db, { chainId: 4217, destination: { type: 'url', url: 'https://example.com/webhooks/tempo' }, environment: options.environment ?? 'production', eventType: 'funding:deposit.updated', filters: options.filters ?? { recipient: `0x${'ab'.repeat(20)}`, status: 'completed' }, owner: { orgId: options.orgId, type: 'api_key' }, ...(options.projectId === undefined ? {} : { projectId: options.projectId }), }) } declare namespace subscription { type Options = { environment?: 'production' | 'sandbox' | undefined filters?: Record | undefined orgId: string projectId?: string | undefined } }