|
| 1 | +/** |
| 2 | + * @vitest-environment node |
| 3 | + */ |
| 4 | +import { beforeEach, describe, expect, it, vi } from 'vitest' |
| 5 | + |
| 6 | +const { selectRows, deleteSpy, cleanupCalls, cleanupSpy } = vi.hoisted(() => ({ |
| 7 | + selectRows: { current: [] as Array<Record<string, unknown>> }, |
| 8 | + deleteSpy: vi.fn(() => ({ where: vi.fn(() => Promise.resolve(undefined)) })), |
| 9 | + cleanupCalls: [] as string[], |
| 10 | + cleanupSpy: vi.fn(async (wh: { id: string }) => { |
| 11 | + cleanupCalls.push(wh.id) |
| 12 | + }), |
| 13 | +})) |
| 14 | + |
| 15 | +vi.mock('@sim/db', () => ({ |
| 16 | + db: { |
| 17 | + select: () => ({ from: () => ({ where: () => Promise.resolve(selectRows.current) }) }), |
| 18 | + delete: deleteSpy, |
| 19 | + transaction: async (cb: (tx: unknown) => Promise<unknown>) => |
| 20 | + cb({ insert: () => ({ values: () => Promise.resolve(undefined) }) }), |
| 21 | + }, |
| 22 | +})) |
| 23 | +vi.mock('@sim/db/schema', () => ({ |
| 24 | + account: {}, |
| 25 | + credentialSetMember: {}, |
| 26 | + webhook: { id: 'webhook.id', workflowId: 'webhook.workflowId' }, |
| 27 | + workflowDeploymentVersion: {}, |
| 28 | +})) |
| 29 | +vi.mock('drizzle-orm', () => ({ |
| 30 | + and: vi.fn(), |
| 31 | + eq: vi.fn((field: unknown, value: unknown) => ({ field, value })), |
| 32 | + inArray: vi.fn(), |
| 33 | + isNotNull: vi.fn(), |
| 34 | + isNull: vi.fn(), |
| 35 | + or: vi.fn(), |
| 36 | +})) |
| 37 | +vi.mock('@/lib/webhooks/provider-subscriptions', () => ({ |
| 38 | + cleanupExternalWebhook: cleanupSpy, |
| 39 | + createExternalWebhookSubscription: vi.fn(), |
| 40 | + shouldRecreateExternalWebhookSubscription: vi.fn(() => false), |
| 41 | +})) |
| 42 | +vi.mock('@/lib/webhooks/providers', () => ({ getProviderHandler: vi.fn() })) |
| 43 | +vi.mock('@/lib/webhooks/utils.server', () => ({ |
| 44 | + findConflictingWebhookPathOwner: vi.fn(), |
| 45 | + syncWebhooksForCredentialSet: vi.fn(), |
| 46 | +})) |
| 47 | +vi.mock('@/lib/webhooks/pending-verification', () => ({ |
| 48 | + PendingWebhookVerificationTracker: class { |
| 49 | + async register() {} |
| 50 | + async clearAll() {} |
| 51 | + }, |
| 52 | +})) |
| 53 | +vi.mock('@/lib/oauth', () => ({ getProviderIdFromServiceId: vi.fn() })) |
| 54 | +vi.mock('@/lib/workflows/subblocks/visibility', () => ({ buildCanonicalIndex: vi.fn() })) |
| 55 | +vi.mock('@/blocks', () => ({ getBlock: vi.fn() })) |
| 56 | +vi.mock('@/triggers', () => ({ getTrigger: vi.fn(), isTriggerValid: vi.fn(() => false) })) |
| 57 | +vi.mock('@/triggers/constants', () => ({ SYSTEM_SUBBLOCK_IDS: new Set<string>() })) |
| 58 | + |
| 59 | +import { saveTriggerWebhooksForDeploy } from '@/lib/webhooks/deploy' |
| 60 | + |
| 61 | +function baseInput(deploymentVersionId?: string) { |
| 62 | + return { |
| 63 | + request: {} as never, |
| 64 | + workflowId: 'wf-1', |
| 65 | + workflow: {}, |
| 66 | + userId: 'user-1', |
| 67 | + blocks: {}, |
| 68 | + requestId: 'req-1', |
| 69 | + deploymentVersionId, |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +describe('saveTriggerWebhooksForDeploy — stale deployment-version cleanup', () => { |
| 74 | + beforeEach(() => { |
| 75 | + vi.clearAllMocks() |
| 76 | + cleanupCalls.length = 0 |
| 77 | + selectRows.current = [] |
| 78 | + }) |
| 79 | + |
| 80 | + it('tears down and deletes webhooks pinned to older deployment versions', async () => { |
| 81 | + selectRows.current = [ |
| 82 | + { id: 'stale-old', workflowId: 'wf-1', blockId: 'b1', deploymentVersionId: 'ver-old' }, |
| 83 | + { id: 'null-version', workflowId: 'wf-1', blockId: 'b2', deploymentVersionId: null }, |
| 84 | + ] |
| 85 | + |
| 86 | + const result = await saveTriggerWebhooksForDeploy(baseInput('ver-active')) |
| 87 | + |
| 88 | + expect(result.success).toBe(true) |
| 89 | + // Stale (older-version) webhook is cleaned up + deleted. |
| 90 | + expect(cleanupCalls).toContain('stale-old') |
| 91 | + expect(deleteSpy).toHaveBeenCalledTimes(1) |
| 92 | + // Null-version webhook is left untouched (matches CLEANUP_INACTIVE semantics). |
| 93 | + expect(cleanupCalls).not.toContain('null-version') |
| 94 | + }) |
| 95 | + |
| 96 | + it('does nothing to other-version webhooks when no deploymentVersionId is given', async () => { |
| 97 | + selectRows.current = [ |
| 98 | + { id: 'some-version', workflowId: 'wf-1', blockId: 'b1', deploymentVersionId: 'ver-x' }, |
| 99 | + ] |
| 100 | + |
| 101 | + const result = await saveTriggerWebhooksForDeploy(baseInput(undefined)) |
| 102 | + |
| 103 | + expect(result.success).toBe(true) |
| 104 | + expect(cleanupCalls).toHaveLength(0) |
| 105 | + expect(deleteSpy).not.toHaveBeenCalled() |
| 106 | + }) |
| 107 | +}) |
0 commit comments