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
112 lines
4.5 KiB
TypeScript
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)
|
|
})
|
|
})
|
|
})
|