teqo/tests/int/payloadTransaction.int.spec.ts
Francisco Ignácio Fontoura Solla 0e4e6855dc Add the tooling gate: knip, CI, zero-warning lint, as-never ban
- knip (files/deps at error level; unused exports ledgered as warn) with
  config for Payload/Next entries; deleted the dead modules it confirmed:
  RecentlyVisited + UpcomingActionPlansCard + actionPlanUpcomingPreview,
  lazy-dialog helpers, tseZone, federalCandidateTotalsAggregate (test-only),
  unused ui kit files, one-shot rename script; pruned unused radix deps and
  typescript-eslint; added pg/@types/pg (pinned to drizzle's 8.10.2)
- GitHub Actions CI on the mirror: lint, typecheck, knip, unit, migrate, int
  against a Postgres 17 service
- eslint: unused-vars with ^_ ignore patterns (migrations args exempt),
  repo-wide ban on `as never` (no-restricted-syntax), lint gate at
  --max-warnings=0; fixed all 54 pre-existing warnings (dead imports,
  hooks deps, tracking-pixel img disable with impeccable config exception)
- tsconfig: include consolidated from 219 lines to 5 (wildcard covers all
  .next dist variants), noUnusedLocals on
- typed replacements for every as-never: hookFilledCreateData (actions
  create data completed by collection hooks), drizzle scopeColumn narrowing,
  tests' stub<T> helper (property-level checking kept, completeness waived)
- test infra: leased-consent generalization used by onda0 spec (shared
  leases over the stable consent keys; window race ledgered), purge fix
- docs: IMPROVE-CODE-QUALITY-PLAN.md tracker, TECH-DEBT.md ledger,
  TESTING.md safety-net map (package.json also gains the Phase 2
  build:election-aggregates script entry; the script lands next commit)
2026-07-23 23:06:12 -03:00

134 lines
4.4 KiB
TypeScript

// @vitest-environment node
import type { Payload } from 'payload'
import { describe, expect, it, vi } from 'vitest'
import { withPayloadTransaction } from '@/utilities/payloadTransaction'
import { stub } from '../helpers/stub'
type DatabaseSession = NonNullable<Payload['db']['sessions']>[string]
const createPayload = ({
transactionID = 17,
commitError,
rollbackError,
hasSession = true,
}: {
transactionID?: number | string | null
commitError?: Error
rollbackError?: Error
hasSession?: boolean
} = {}) => {
const database = { execute: vi.fn() }
const beginTransaction = vi.fn().mockResolvedValue(transactionID)
const commitTransaction = vi.fn().mockImplementation(async () => {
if (commitError) throw commitError
})
const rollbackTransaction = vi.fn().mockImplementation(async () => {
if (rollbackError) throw rollbackError
})
return {
database,
payload: {
db: stub<Payload['db']>({
beginTransaction,
commitTransaction,
rollbackTransaction,
sessions:
transactionID === null || !hasSession
? {}
: { [String(transactionID)]: stub<DatabaseSession>({ db: database }) },
}),
},
beginTransaction,
commitTransaction,
rollbackTransaction,
}
}
describe('withPayloadTransaction', () => {
it('commits a successful callback and propagates one req object and session database', async () => {
const fixture = createPayload()
const callback = vi.fn(async ({ req }) => {
expect(req).toEqual({ transactionID: 17 })
return { req }
})
const result = await withPayloadTransaction(fixture.payload, callback)
expect(result.req).toBe(callback.mock.calls[0]![0].req)
expect(fixture.commitTransaction).toHaveBeenCalledWith(17)
expect(fixture.rollbackTransaction).not.toHaveBeenCalled()
})
it('does not run the callback when Payload cannot start a transaction', async () => {
const fixture = createPayload({ transactionID: null })
const callback = vi.fn()
await expect(
withPayloadTransaction(fixture.payload, callback, {
beginFailureMessage: 'falha específica',
}),
).rejects.toThrow('falha específica')
expect(callback).not.toHaveBeenCalled()
expect(fixture.commitTransaction).not.toHaveBeenCalled()
expect(fixture.rollbackTransaction).not.toHaveBeenCalled()
})
it('rolls back and preserves a callback failure', async () => {
const fixture = createPayload()
const original = new Error('callback falhou')
await expect(
withPayloadTransaction(fixture.payload, async () => {
throw original
}),
).rejects.toBe(original)
expect(fixture.rollbackTransaction).toHaveBeenCalledWith(17)
expect(fixture.commitTransaction).not.toHaveBeenCalled()
})
it('rolls back and preserves a commit failure', async () => {
const original = new Error('commit falhou')
const fixture = createPayload({ commitError: original })
await expect(withPayloadTransaction(fixture.payload, async () => 'result')).rejects.toBe(
original,
)
expect(fixture.commitTransaction).toHaveBeenCalledWith(17)
expect(fixture.rollbackTransaction).toHaveBeenCalledWith(17)
})
it('aggregates rollback failure after callback failure with the original as cause', async () => {
const original = new Error('callback falhou')
const rollback = new Error('rollback falhou')
const fixture = createPayload({ rollbackError: rollback })
const failure = await withPayloadTransaction(fixture.payload, async () => {
throw original
}).catch((error: unknown) => error)
expect(failure).toBeInstanceOf(AggregateError)
expect((failure as AggregateError).errors).toEqual([original, rollback])
expect((failure as AggregateError & { cause?: unknown }).cause).toBe(original)
})
it('aggregates rollback failure after commit failure with the commit error first', async () => {
const commit = new Error('commit falhou')
const rollback = new Error('rollback falhou')
const fixture = createPayload({ commitError: commit, rollbackError: rollback })
const failure = await withPayloadTransaction(fixture.payload, async () => 'result').catch(
(error: unknown) => error,
)
expect(failure).toBeInstanceOf(AggregateError)
expect((failure as AggregateError).errors).toEqual([commit, rollback])
expect((failure as AggregateError & { cause?: unknown }).cause).toBe(commit)
})
})