mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 23:25:18 +00:00
F1: purgeOrphanedConsentRenames sob lease exclusivo (autocommit) nos 3 pontos que tocam a key canônica; renames de teste migrados para chaves privadas; F1b: purge de contatos órfãos no purgeMunicipalityResidue. F2 (medição): shared-writer ABBA refutado; janela do missing-consent exclusiva + Onda0 atômico (down transacional) + provision exclusivo. Contraprova: 7 arquivos da key x 6 workers = 12/12 verdes (baseline 1/4).
681 lines
24 KiB
TypeScript
681 lines
24 KiB
TypeScript
// @vitest-environment node
|
|
|
|
import { getPayload, type Payload } from 'payload'
|
|
import { beforeAll, describe, expect, it } from 'vitest'
|
|
|
|
import type { Consent } from '@/payload-types'
|
|
import config from '@/payload.config'
|
|
import {
|
|
acquireSharedTestDatabaseLease,
|
|
acquireTestDatabaseLease,
|
|
CAMPAIGN_INVITE_CONSENT_LEASE_KEY,
|
|
ensureLeasedConsent,
|
|
fixtureConsentText,
|
|
purgeOrphanedConsentRenames,
|
|
startTestDatabaseLeaseAcquisition,
|
|
waitForAdvisoryLockWaiter,
|
|
withInviteConsent,
|
|
withMissingInviteConsentFixture,
|
|
withMutableConsentFixture,
|
|
withSharedTestDatabaseLease,
|
|
type TestDatabaseLease,
|
|
} from '../helpers/testDatabaseLease'
|
|
|
|
let payload: Payload
|
|
|
|
const expectExactWaiter = (
|
|
waiting: Awaited<ReturnType<typeof waitForAdvisoryLockWaiter>>,
|
|
pid: number,
|
|
mode: 'ExclusiveLock' | 'ShareLock',
|
|
) => {
|
|
expect(waiting).toEqual({
|
|
activityDatabaseOID: waiting.databaseOID,
|
|
classID: waiting.expectedClassID,
|
|
databaseOID: expect.any(Number),
|
|
expectedClassID: expect.stringMatching(/^\d+$/),
|
|
expectedObjectID: expect.stringMatching(/^\d+$/),
|
|
granted: false,
|
|
mode,
|
|
objectID: waiting.expectedObjectID,
|
|
objectSubID: 1,
|
|
pid,
|
|
virtualTransaction: expect.stringMatching(/^\d+\/\d+$/),
|
|
})
|
|
expect(waiting.databaseOID).toBeGreaterThan(0)
|
|
}
|
|
|
|
describe('test database lease', () => {
|
|
beforeAll(async () => {
|
|
payload = await getPayload({ config: await config })
|
|
})
|
|
|
|
it('blocks an independent transaction session until the first lease releases', async () => {
|
|
const first = await acquireTestDatabaseLease(payload, CAMPAIGN_INVITE_CONSENT_LEASE_KEY)
|
|
let secondEntered = false
|
|
const pendingSecond = startTestDatabaseLeaseAcquisition(
|
|
payload,
|
|
CAMPAIGN_INVITE_CONSENT_LEASE_KEY,
|
|
)
|
|
const secondPID = await pendingSecond.backendPID
|
|
const secondAcquisition = pendingSecond.acquisition.then((lease) => {
|
|
secondEntered = true
|
|
return lease
|
|
})
|
|
|
|
const waiting = await waitForAdvisoryLockWaiter(payload, {
|
|
key: `test:${CAMPAIGN_INVITE_CONSENT_LEASE_KEY}`,
|
|
mode: 'ExclusiveLock',
|
|
waiterPID: secondPID,
|
|
})
|
|
expectExactWaiter(waiting, secondPID, 'ExclusiveLock')
|
|
expect(secondEntered).toBe(false)
|
|
await first.release()
|
|
|
|
const second = await secondAcquisition
|
|
expect(secondEntered).toBe(true)
|
|
await second.release()
|
|
})
|
|
|
|
it('allows shared readers to overlap and blocks an exclusive writer until both release', async () => {
|
|
const firstReader = await acquireSharedTestDatabaseLease(
|
|
payload,
|
|
CAMPAIGN_INVITE_CONSENT_LEASE_KEY,
|
|
)
|
|
const secondReader = await acquireSharedTestDatabaseLease(
|
|
payload,
|
|
CAMPAIGN_INVITE_CONSENT_LEASE_KEY,
|
|
)
|
|
let writerEntered = false
|
|
const pendingWriter = startTestDatabaseLeaseAcquisition(
|
|
payload,
|
|
CAMPAIGN_INVITE_CONSENT_LEASE_KEY,
|
|
)
|
|
const writerPID = await pendingWriter.backendPID
|
|
const writerAcquisition = pendingWriter.acquisition.then((lease) => {
|
|
writerEntered = true
|
|
return lease
|
|
})
|
|
|
|
const waiting = await waitForAdvisoryLockWaiter(payload, {
|
|
key: `test:${CAMPAIGN_INVITE_CONSENT_LEASE_KEY}`,
|
|
mode: 'ExclusiveLock',
|
|
waiterPID: writerPID,
|
|
})
|
|
expectExactWaiter(waiting, writerPID, 'ExclusiveLock')
|
|
expect(writerEntered).toBe(false)
|
|
await firstReader.release()
|
|
const stillWaiting = await waitForAdvisoryLockWaiter(payload, {
|
|
key: `test:${CAMPAIGN_INVITE_CONSENT_LEASE_KEY}`,
|
|
mode: 'ExclusiveLock',
|
|
waiterPID: writerPID,
|
|
})
|
|
expectExactWaiter(stillWaiting, writerPID, 'ExclusiveLock')
|
|
expect(stillWaiting.virtualTransaction).toBe(waiting.virtualTransaction)
|
|
expect(writerEntered).toBe(false)
|
|
await secondReader.release()
|
|
|
|
const writer = await writerAcquisition
|
|
expect(writerEntered).toBe(true)
|
|
await writer.release()
|
|
})
|
|
|
|
it('blocks shared readers while an exclusive writer is active', async () => {
|
|
const writer = await acquireTestDatabaseLease(payload, CAMPAIGN_INVITE_CONSENT_LEASE_KEY)
|
|
let readerEntered = false
|
|
const pendingReader = startTestDatabaseLeaseAcquisition(
|
|
payload,
|
|
CAMPAIGN_INVITE_CONSENT_LEASE_KEY,
|
|
'shared',
|
|
)
|
|
const readerPID = await pendingReader.backendPID
|
|
const readerAcquisition = pendingReader.acquisition.then((lease) => {
|
|
readerEntered = true
|
|
return lease
|
|
})
|
|
|
|
const waiting = await waitForAdvisoryLockWaiter(payload, {
|
|
key: `test:${CAMPAIGN_INVITE_CONSENT_LEASE_KEY}`,
|
|
mode: 'ShareLock',
|
|
waiterPID: readerPID,
|
|
})
|
|
expectExactWaiter(waiting, readerPID, 'ShareLock')
|
|
expect(readerEntered).toBe(false)
|
|
await writer.release()
|
|
|
|
const reader = await readerAcquisition
|
|
expect(readerEntered).toBe(true)
|
|
await reader.release()
|
|
})
|
|
|
|
it('creates a missing configured consent only once under concurrent ensures', async () => {
|
|
// Private key/lease: this test deliberately exercises ensure-then-create
|
|
// racing the missing window, so its window stays unleased
|
|
// (serializeWindow: false — the ensures lease themselves). Running the
|
|
// race on a private key keeps the stable invite row untouched: an
|
|
// unleased window on a shared key would let other files' fixtures observe
|
|
// the committed-absent state and delete the row out from under them.
|
|
const raceKey = `ensure-race-${Date.now()}-${Math.random()}`
|
|
const raceLeaseKey = `ensure-race-lease-${Date.now()}-${Math.random()}`
|
|
await withMissingInviteConsentFixture(
|
|
payload,
|
|
async () => {
|
|
const ensured = await Promise.all([
|
|
ensureLeasedConsent(payload, { consentKey: raceKey, leaseKey: raceLeaseKey }),
|
|
ensureLeasedConsent(payload, { consentKey: raceKey, leaseKey: raceLeaseKey }),
|
|
ensureLeasedConsent(payload, { consentKey: raceKey, leaseKey: raceLeaseKey }),
|
|
])
|
|
const configured = await withSharedTestDatabaseLease(payload, raceLeaseKey, () =>
|
|
payload.find({
|
|
collection: 'consent',
|
|
where: { key: { equals: raceKey } },
|
|
depth: 0,
|
|
limit: 10,
|
|
}),
|
|
)
|
|
|
|
expect(new Set(ensured.map((document) => document.id))).toHaveLength(1)
|
|
expect(configured.docs).toHaveLength(1)
|
|
},
|
|
{ consentKey: raceKey, leaseKey: raceLeaseKey, serializeWindow: false },
|
|
)
|
|
})
|
|
|
|
it('blocks other consent users while the missing-consent operation window is open', async () => {
|
|
const before = await withInviteConsent(payload, async (consent) => consent)
|
|
|
|
let writerAcquisition: Promise<TestDatabaseLease> | undefined
|
|
let readerAcquisition: Promise<TestDatabaseLease> | undefined
|
|
try {
|
|
await withMissingInviteConsentFixture(payload, async () => {
|
|
let writerEntered = false
|
|
const pendingWriter = startTestDatabaseLeaseAcquisition(
|
|
payload,
|
|
CAMPAIGN_INVITE_CONSENT_LEASE_KEY,
|
|
)
|
|
writerAcquisition = pendingWriter.acquisition.then((lease) => {
|
|
writerEntered = true
|
|
return lease
|
|
})
|
|
const writerPID = await pendingWriter.backendPID
|
|
|
|
const waiting = await waitForAdvisoryLockWaiter(payload, {
|
|
key: `test:${CAMPAIGN_INVITE_CONSENT_LEASE_KEY}`,
|
|
mode: 'ExclusiveLock',
|
|
waiterPID: writerPID,
|
|
})
|
|
expectExactWaiter(waiting, writerPID, 'ExclusiveLock')
|
|
expect(writerEntered).toBe(false)
|
|
|
|
// D10 F2: the window is EXCLUSIVE, so even a SHARED consumer queues
|
|
// while the canonical key is committed-absent — no consumer can
|
|
// observe the transient absence (baseline load runs flaked 3/4 with
|
|
// consumers reading the absent key or referencing a row the restore
|
|
// deleted out from under them). The reader is NOT awaited here: it
|
|
// only unblocks when this window releases, which is after the
|
|
// operation returns.
|
|
let readerEntered = false
|
|
const pendingReader = startTestDatabaseLeaseAcquisition(
|
|
payload,
|
|
CAMPAIGN_INVITE_CONSENT_LEASE_KEY,
|
|
'shared',
|
|
)
|
|
readerAcquisition = pendingReader.acquisition.then((lease) => {
|
|
readerEntered = true
|
|
return lease
|
|
})
|
|
const readerPID = await pendingReader.backendPID
|
|
const readerWaiting = await waitForAdvisoryLockWaiter(payload, {
|
|
key: `test:${CAMPAIGN_INVITE_CONSENT_LEASE_KEY}`,
|
|
mode: 'ShareLock',
|
|
waiterPID: readerPID,
|
|
})
|
|
expectExactWaiter(readerWaiting, readerPID, 'ShareLock')
|
|
expect(readerEntered).toBe(false)
|
|
})
|
|
} finally {
|
|
// Release the waiter leases even when an assertion above fails, so the
|
|
// pending acquisitions never block parallel spec files until teardown.
|
|
if (writerAcquisition) {
|
|
const writer = await writerAcquisition
|
|
await writer.release()
|
|
}
|
|
if (readerAcquisition) {
|
|
const reader = await readerAcquisition
|
|
await reader.release()
|
|
}
|
|
}
|
|
|
|
const after = await withInviteConsent(payload, async (consent) => consent)
|
|
// id+key only: a concurrent legitimate writer (e.g. the Onda0 provision)
|
|
// may update the row's text between these reads — the window's contract is
|
|
// that no exclusive writer interleaves, not that the text is frozen.
|
|
expect(after).toMatchObject({
|
|
id: before.id,
|
|
key: before.key,
|
|
})
|
|
})
|
|
|
|
it('releases a shared consent scope after its callback fails', async () => {
|
|
const callbackFailure = new Error('intentional shared callback failure')
|
|
|
|
await expect(
|
|
withInviteConsent(payload, async () => {
|
|
throw callbackFailure
|
|
}),
|
|
).rejects.toBe(callbackFailure)
|
|
|
|
const writer = await acquireTestDatabaseLease(payload, CAMPAIGN_INVITE_CONSENT_LEASE_KEY)
|
|
await writer.release()
|
|
})
|
|
|
|
it('restores the exact configured consent after deletion and recreation', async () => {
|
|
// The reference is the fixture's own snapshot (the configured consent the
|
|
// callback receives), not a separate read: a concurrent legitimate writer
|
|
// (e.g. the Onda0 provision) may update the row's text between reads, and
|
|
// the restore faithfully reproduces whatever the snapshot captured.
|
|
let before: Consent | undefined
|
|
await withMutableConsentFixture(payload, async (consent) => {
|
|
before = consent
|
|
await payload.delete({ collection: 'consent', id: consent.id })
|
|
await payload.create({
|
|
collection: 'consent',
|
|
data: {
|
|
key: 'lideranca-autopreenchimento',
|
|
text: {
|
|
root: {
|
|
type: 'root',
|
|
children: [
|
|
{
|
|
type: 'paragraph',
|
|
children: [{ type: 'text', text: 'Temporary recreation', version: 1 }],
|
|
direction: null,
|
|
format: '',
|
|
indent: 0,
|
|
version: 1,
|
|
},
|
|
],
|
|
direction: null,
|
|
format: '',
|
|
indent: 0,
|
|
version: 1,
|
|
},
|
|
},
|
|
},
|
|
depth: 0,
|
|
})
|
|
})
|
|
|
|
const after = await withInviteConsent(payload, async (consent) => consent)
|
|
if (!before) throw new Error('The mutable consent fixture must have provided a snapshot.')
|
|
expect(after).toMatchObject({
|
|
id: before.id,
|
|
key: before.key,
|
|
text: before.text,
|
|
})
|
|
})
|
|
|
|
it('restores the configured consent and releases after a callback assertion failure', async () => {
|
|
const before = await withSharedTestDatabaseLease(
|
|
payload,
|
|
CAMPAIGN_INVITE_CONSENT_LEASE_KEY,
|
|
() =>
|
|
payload.find({
|
|
collection: 'consent',
|
|
where: { key: { equals: 'lideranca-autopreenchimento' } },
|
|
depth: 0,
|
|
limit: 1,
|
|
}),
|
|
)
|
|
const callbackFailure = new Error('intentional callback assertion failure')
|
|
// The reference is the fixture's own snapshot (what the callback received),
|
|
// not the `before` read: a concurrent legitimate writer (e.g. the Onda0
|
|
// provision) may update the row's text between reads, and the restore
|
|
// faithfully reproduces whatever the snapshot captured. `before` still
|
|
// pins the existence state (restored vs created-then-deleted).
|
|
let snapshot: Consent | undefined
|
|
|
|
await expect(
|
|
withMutableConsentFixture(payload, async (consent) => {
|
|
snapshot = consent
|
|
await payload.update({
|
|
collection: 'consent',
|
|
id: consent.id,
|
|
data: {
|
|
key: 'temporarily-renamed-consent',
|
|
text: {
|
|
root: {
|
|
type: 'root',
|
|
children: [
|
|
{
|
|
type: 'paragraph',
|
|
children: [{ type: 'text', text: 'Temporary text', version: 1 }],
|
|
direction: null,
|
|
format: '',
|
|
indent: 0,
|
|
version: 1,
|
|
},
|
|
],
|
|
direction: null,
|
|
format: '',
|
|
indent: 0,
|
|
version: 1,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
throw callbackFailure
|
|
}),
|
|
).rejects.toBe(callbackFailure)
|
|
|
|
const after = await withSharedTestDatabaseLease(
|
|
payload,
|
|
CAMPAIGN_INVITE_CONSENT_LEASE_KEY,
|
|
() =>
|
|
payload.find({
|
|
collection: 'consent',
|
|
where: { key: { equals: 'lideranca-autopreenchimento' } },
|
|
depth: 0,
|
|
limit: 1,
|
|
}),
|
|
)
|
|
expect(after.docs).toHaveLength(before.docs.length)
|
|
if (after.docs[0]) {
|
|
if (!snapshot) throw new Error('The mutable consent fixture must have provided a snapshot.')
|
|
expect(after.docs[0]).toMatchObject({
|
|
id: snapshot.id,
|
|
key: snapshot.key,
|
|
text: snapshot.text,
|
|
})
|
|
}
|
|
|
|
const subsequent = await acquireTestDatabaseLease(payload, CAMPAIGN_INVITE_CONSENT_LEASE_KEY)
|
|
await subsequent.release()
|
|
})
|
|
|
|
it('aggregates callback and restore lease acquisition failures in original order', async () => {
|
|
const callbackFailure = new Error('intentional callback failure')
|
|
const restoreAcquisitionFailure = new Error('intentional restore acquisition failure')
|
|
// Private key: this fault skips the restore, so the committed-absent state
|
|
// would leak for the rest of the run on a shared key (id churn in every
|
|
// other file). A private key keeps the leak invisible.
|
|
const faultKey = `restore-fault-${Date.now()}-${Math.random()}`
|
|
const faultLeaseKey = `restore-fault-lease-${Date.now()}-${Math.random()}`
|
|
|
|
const failure = await withMissingInviteConsentFixture(
|
|
payload,
|
|
async () => {
|
|
throw callbackFailure
|
|
},
|
|
{
|
|
consentKey: faultKey,
|
|
leaseKey: faultLeaseKey,
|
|
beforeRestoreLeaseAcquire: async () => {
|
|
throw restoreAcquisitionFailure
|
|
},
|
|
},
|
|
).catch((error: unknown) => error)
|
|
|
|
expect(failure).toBeInstanceOf(AggregateError)
|
|
expect((failure as AggregateError).errors).toEqual([callbackFailure, restoreAcquisitionFailure])
|
|
})
|
|
|
|
it('aggregates snapshot setup and setup lease release failures in original order', async () => {
|
|
const snapshotFailure = new Error('intentional snapshot setup failure')
|
|
const releaseFailure = new Error('intentional setup release failure')
|
|
|
|
const failure = await withMissingInviteConsentFixture(payload, async () => undefined, {
|
|
beforeSnapshotSetup: async () => {
|
|
throw snapshotFailure
|
|
},
|
|
beforeSetupLeaseRelease: async () => {
|
|
throw releaseFailure
|
|
},
|
|
}).catch((error: unknown) => error)
|
|
|
|
expect(failure).toBeInstanceOf(AggregateError)
|
|
expect((failure as AggregateError).errors).toEqual([snapshotFailure, releaseFailure])
|
|
|
|
const subsequent = await acquireTestDatabaseLease(payload, CAMPAIGN_INVITE_CONSENT_LEASE_KEY)
|
|
await subsequent.release()
|
|
})
|
|
|
|
it('rolls back a failed missing-consent setup and preserves rollback errors', async () => {
|
|
const before = await withInviteConsent(payload, async (consent) => consent)
|
|
const releaseFailure = new Error('intentional setup release failure after deletion')
|
|
const rollbackFailure = new Error('intentional setup rollback cleanup failure')
|
|
let callbackCalled = false
|
|
|
|
const failure = await withMissingInviteConsentFixture(
|
|
payload,
|
|
async () => {
|
|
callbackCalled = true
|
|
},
|
|
{
|
|
beforeSetupLeaseRelease: async () => {
|
|
throw releaseFailure
|
|
},
|
|
beforeSetupLeaseRollback: async () => {
|
|
throw rollbackFailure
|
|
},
|
|
},
|
|
).catch((error: unknown) => error)
|
|
|
|
expect(callbackCalled).toBe(false)
|
|
expect(failure).toBeInstanceOf(AggregateError)
|
|
expect((failure as AggregateError).errors).toEqual([releaseFailure, rollbackFailure])
|
|
const after = await withInviteConsent(payload, async (consent) => consent)
|
|
// The rollback preserves the exact row; the text is not part of the
|
|
// contract here (no restore ran, and a concurrent legitimate writer such
|
|
// as the Onda0 provision may update it between reads).
|
|
expect(after).toMatchObject({
|
|
id: before.id,
|
|
key: before.key,
|
|
})
|
|
|
|
const subsequent = await acquireTestDatabaseLease(payload, CAMPAIGN_INVITE_CONSENT_LEASE_KEY)
|
|
await subsequent.release()
|
|
})
|
|
|
|
it('keeps an absent consent absent when missing-consent setup release fails', async () => {
|
|
const consentKey = `absent-consent-${Date.now()}-${Math.random()}`
|
|
const leaseKey = `absent-consent-lease-${Date.now()}-${Math.random()}`
|
|
const releaseFailure = new Error('intentional absent setup release failure')
|
|
const failure = await withMissingInviteConsentFixture(
|
|
payload,
|
|
async () => {
|
|
throw new Error('callback must not run')
|
|
},
|
|
{
|
|
consentKey,
|
|
leaseKey,
|
|
beforeSetupLeaseRelease: async () => {
|
|
throw releaseFailure
|
|
},
|
|
},
|
|
).catch((error: unknown) => error)
|
|
|
|
expect(failure).toBe(releaseFailure)
|
|
const configured = await payload.find({
|
|
collection: 'consent',
|
|
where: { key: { equals: consentKey } },
|
|
depth: 0,
|
|
limit: 1,
|
|
})
|
|
expect(configured.docs).toHaveLength(0)
|
|
|
|
const subsequent = await acquireTestDatabaseLease(payload, leaseKey)
|
|
await subsequent.release()
|
|
})
|
|
|
|
it('purges orphaned consent renames left by an aborted run on ensure', async () => {
|
|
// D10 F1: every consent fixture must clear the rename shapes an aborted
|
|
// run can leave behind before snapshotting, or the next run's rename hits
|
|
// the UNIQUE constraint. Legitimate rows (the private key below) survive.
|
|
const privateKey = `purge-ensure-${Date.now()}-${Math.random()}`
|
|
const privateLeaseKey = `purge-ensure-lease-${Date.now()}-${Math.random()}`
|
|
const legitKey = `purge-legit-${Date.now()}-${Math.random()}`
|
|
const orphanIDs: number[] = []
|
|
let legitID = 0
|
|
// The orphans are planted under the key's EXCLUSIVE lease: the rename
|
|
// keys are LIVE inside other spec files' fixtures, and an unleased
|
|
// create could hit their UNIQUE constraint mid-rename (D10 F2 load
|
|
// measurement). The lease start also purges any leftover from a previous
|
|
// failed run of this very test, so the plant never collides with it.
|
|
const plantLease = await acquireTestDatabaseLease(payload, privateLeaseKey)
|
|
try {
|
|
await purgeOrphanedConsentRenames((query) => payload.db.drizzle.execute(query))
|
|
for (const key of [
|
|
'temporarily-renamed-consent',
|
|
'chave-renomeada',
|
|
'chave-temporariamente-ausente',
|
|
]) {
|
|
const orphan = await payload.create({
|
|
collection: 'consent',
|
|
data: { key, text: fixtureConsentText },
|
|
depth: 0,
|
|
})
|
|
orphanIDs.push(orphan.id)
|
|
}
|
|
const legit = await payload.create({
|
|
collection: 'consent',
|
|
data: { key: legitKey, text: fixtureConsentText },
|
|
depth: 0,
|
|
})
|
|
legitID = legit.id
|
|
} finally {
|
|
await plantLease.release()
|
|
}
|
|
|
|
try {
|
|
const configured = await ensureLeasedConsent(payload, {
|
|
consentKey: privateKey,
|
|
leaseKey: privateLeaseKey,
|
|
})
|
|
expect(configured.key).toBe(privateKey)
|
|
|
|
for (const orphanID of orphanIDs) {
|
|
const orphan = await payload.find({
|
|
collection: 'consent',
|
|
where: { id: { equals: orphanID } },
|
|
depth: 0,
|
|
limit: 1,
|
|
})
|
|
expect(orphan.docs).toHaveLength(0)
|
|
}
|
|
expect(
|
|
await payload.findByID({ collection: 'consent', id: legitID, depth: 0 }),
|
|
).toMatchObject({ key: legitKey })
|
|
} finally {
|
|
// Cleanup runs even on failure — a failed run of this very test must
|
|
// not leave the orphans it planted behind (self-healing tests).
|
|
for (const orphanID of orphanIDs) {
|
|
await payload
|
|
.delete({ collection: 'consent', id: orphanID, depth: 0 })
|
|
.catch(() => undefined)
|
|
}
|
|
if (legitID > 0) {
|
|
await payload
|
|
.delete({ collection: 'consent', id: legitID, depth: 0 })
|
|
.catch(() => undefined)
|
|
}
|
|
}
|
|
})
|
|
|
|
it('purges orphaned renames during the missing-consent fixture setup', async () => {
|
|
const privateKey = `purge-missing-${Date.now()}-${Math.random()}`
|
|
const privateLeaseKey = `purge-missing-lease-${Date.now()}-${Math.random()}`
|
|
// Planted under the exclusive lease (the rename keys are live inside
|
|
// other spec files' fixtures — an unleased create could hit their UNIQUE
|
|
// constraint mid-rename). The lease start also purges any leftover from
|
|
// a previous failed run of this very test.
|
|
const plantLease = await acquireTestDatabaseLease(payload, privateLeaseKey)
|
|
let orphanID = 0
|
|
try {
|
|
await purgeOrphanedConsentRenames((query) => payload.db.drizzle.execute(query))
|
|
orphanID = (
|
|
await payload.create({
|
|
collection: 'consent',
|
|
data: { key: 'chave-temporariamente-ausente', text: fixtureConsentText },
|
|
depth: 0,
|
|
})
|
|
).id
|
|
} finally {
|
|
await plantLease.release()
|
|
}
|
|
|
|
await withMissingInviteConsentFixture(payload, async () => undefined, {
|
|
consentKey: privateKey,
|
|
leaseKey: privateLeaseKey,
|
|
})
|
|
|
|
try {
|
|
const orphan = await payload.find({
|
|
collection: 'consent',
|
|
where: { id: { equals: orphanID } },
|
|
depth: 0,
|
|
limit: 1,
|
|
})
|
|
expect(orphan.docs).toHaveLength(0)
|
|
} finally {
|
|
// A failed run of this very test must not leave its plant behind.
|
|
await payload.delete({ collection: 'consent', id: orphanID, depth: 0 }).catch(() => undefined)
|
|
}
|
|
})
|
|
|
|
it('self-heals the canonical invite consent after an aborted rename on the next fixture run', async () => {
|
|
// The exact residue an aborted run leaves: the canonical row committed
|
|
// with a temporary key and the snapshot lost. The plant holds the
|
|
// exclusive lease so parallel spec files never observe the state; the
|
|
// fixture's own purge then heals it — the in-fixture rename must succeed
|
|
// instead of hitting the UNIQUE `consent_key_idx`. The lease start also
|
|
// purges any leftover from a previous failed run of this very test.
|
|
const plantLease = await acquireTestDatabaseLease(payload, CAMPAIGN_INVITE_CONSENT_LEASE_KEY)
|
|
try {
|
|
await purgeOrphanedConsentRenames((query) => payload.db.drizzle.execute(query))
|
|
const canonical = await payload.find({
|
|
collection: 'consent',
|
|
where: { key: { equals: 'lideranca-autopreenchimento' } },
|
|
limit: 1,
|
|
sort: 'id',
|
|
depth: 0,
|
|
})
|
|
if (!canonical.docs[0]) {
|
|
throw new Error(
|
|
'The canonical invite consent row must exist before the aborted-rename repro.',
|
|
)
|
|
}
|
|
await payload.update({
|
|
collection: 'consent',
|
|
id: canonical.docs[0].id,
|
|
data: { key: 'chave-renomeada' },
|
|
depth: 0,
|
|
})
|
|
} finally {
|
|
await plantLease.release()
|
|
}
|
|
|
|
let renamedDuringFixture = false
|
|
await withMutableConsentFixture(payload, async (consent) => {
|
|
// Without the F1 purge this rename throws "Valor deve ser único".
|
|
await payload.update({
|
|
collection: 'consent',
|
|
id: consent.id,
|
|
data: { key: 'chave-renomeada' },
|
|
depth: 0,
|
|
})
|
|
renamedDuringFixture = true
|
|
})
|
|
expect(renamedDuringFixture).toBe(true)
|
|
|
|
// Second run proves the run following the healed one stays clean.
|
|
await withMutableConsentFixture(payload, async () => undefined)
|
|
|
|
const orphans = await payload.find({
|
|
collection: 'consent',
|
|
where: { key: { equals: 'chave-renomeada' } },
|
|
depth: 0,
|
|
pagination: false,
|
|
})
|
|
expect(orphans.totalDocs).toBe(0)
|
|
})
|
|
})
|