Files
simstudioai--sim/apps/sim/lib/webhooks/polling/canonical.test.ts
T
wehub-resource-sync d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

112 lines
4.5 KiB
TypeScript

/**
* @vitest-environment node
*/
import { describe, expect, it } from 'vitest'
import { readCanonicalTriggerValue } from '@/lib/webhooks/polling/canonical'
describe('readCanonicalTriggerValue', () => {
it('returns the canonical value when present', () => {
expect(readCanonicalTriggerValue('canon', 'basic', 'advanced')).toBe('canon')
})
it('falls back basic-first when the canonical key is absent (transitional)', () => {
expect(readCanonicalTriggerValue(undefined, 'basic', 'advanced')).toBe('basic')
})
it('falls back to the advanced value when canonical and basic are absent', () => {
expect(readCanonicalTriggerValue(undefined, undefined, 'advanced')).toBe('advanced')
})
it('treats empty strings as unset', () => {
expect(readCanonicalTriggerValue('', '', 'advanced')).toBe('advanced')
expect(readCanonicalTriggerValue('', '')).toBeUndefined()
})
it('ignores non-string members', () => {
expect(readCanonicalTriggerValue(null, 42 as unknown, 'advanced')).toBe('advanced')
expect(readCanonicalTriggerValue(undefined, undefined)).toBeUndefined()
})
/**
* No-repoint invariant: for every config shape the previous app version could
* have stored, the new canonical-first read must resolve to the SAME resource
* the old basic-first read returned — whether the row has been backfilled
* (canonical key present) or not (transitional fallback). The legacy
* basic-first read is reproduced inline as the source of truth.
*/
describe('no-repoint across legacy and backfilled shapes', () => {
interface GoogleDriveConfig {
folderId?: string
manualFolderId?: string
}
// The exact pre-change Google Drive poller read (basic key === canonical key).
const legacyDriveRead = (c: GoogleDriveConfig) => c.folderId || c.manualFolderId
// Mirror of the migration 0253 backfill: fill the canonical key basic-first
// ONLY when absent. For Drive the canonical key IS the basic key (folderId).
const backfillDrive = (c: GoogleDriveConfig): GoogleDriveConfig =>
c.folderId ? c : { ...c, folderId: c.folderId || c.manualFolderId }
const driveShapes: GoogleDriveConfig[] = [
{ folderId: 'basic-only' },
{ manualFolderId: 'advanced-only' },
// Drift: stale basic + active advanced both stored. Current poller reads basic.
{ folderId: 'STALE', manualFolderId: 'ACTIVE' },
{},
]
it.each(driveShapes)('Drive legacy config %o reads same resource as before', (config) => {
const before = legacyDriveRead(config)
const after = readCanonicalTriggerValue(config.folderId, config.manualFolderId)
expect(after).toBe(before || undefined)
})
it.each(driveShapes)('Drive backfilled config %o reads same resource as before', (config) => {
const before = legacyDriveRead(config)
const backfilled = backfillDrive(config)
const after = readCanonicalTriggerValue(backfilled.folderId, backfilled.manualFolderId)
expect(after).toBe(before || undefined)
})
interface TableConfig {
tableId?: string
tableSelector?: string
manualTableId?: string
}
// The exact pre-change table reader (already canonical-first, but tableId was
// never written, so it fell through to the raw keys).
const legacyTableRead = (c: TableConfig) => c.tableId ?? c.tableSelector ?? c.manualTableId
// Mirror of the migration 0253 backfill for table (canonical key is distinct).
const backfillTable = (c: TableConfig): TableConfig =>
c.tableId ? c : { ...c, tableId: c.tableSelector ?? c.manualTableId }
const tableShapes: TableConfig[] = [
{ tableSelector: 'basic-only' },
{ manualTableId: 'advanced-only' },
{ tableSelector: 'STALE', manualTableId: 'ACTIVE' },
{ tableId: 'collapsed', tableSelector: 'STALE', manualTableId: 'ACTIVE' },
{},
]
it.each(tableShapes)('table legacy config %o reads same resource as before', (config) => {
const before = legacyTableRead(config)
const after = readCanonicalTriggerValue(
config.tableId,
config.tableSelector,
config.manualTableId
)
expect(after).toBe(before ?? undefined)
})
it.each(tableShapes)('table backfilled config %o reads same resource as before', (config) => {
const before = legacyTableRead(config)
const backfilled = backfillTable(config)
const after = readCanonicalTriggerValue(
backfilled.tableId,
backfilled.tableSelector,
backfilled.manualTableId
)
expect(after).toBe(before ?? undefined)
})
})
})