Files
core2026/packages/infra/test/playAuditBestEffort.test.ts
T

51 lines
2.1 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import type { GamePrisma } from '../src/gamePrisma.js';
import { withPlayAuditSavepoint } from '../src/playAuditBestEffort.js';
describe('optional gameplay audit transaction', () => {
it('propagates broken transaction recovery instead of claiming a safe gameplay commit', async () => {
const db = {
$queryRaw: vi.fn().mockResolvedValue([{ statement: '0', lock: '0' }]),
$executeRaw: vi.fn().mockImplementation(async (sql: TemplateStringsArray) => {
if (sql[0]!.startsWith('ROLLBACK')) throw new Error('connection lost');
return 0;
}),
} as unknown as GamePrisma.TransactionClient;
await expect(
withPlayAuditSavepoint(db, async () => {
throw new Error('audit insert');
})
).rejects.toThrow('connection lost');
});
it('stops subsequent batches when the whole audit budget is spent', async () => {
let now = 0;
const timer = vi.spyOn(performance, 'now').mockImplementation(() => now);
const write = vi.fn().mockImplementation(async () => {
now += 600;
return { count: 1 };
});
const db = {
$queryRaw: vi.fn().mockResolvedValue([{ statement: '0', lock: '0' }]),
$executeRaw: vi.fn().mockResolvedValue(0),
playAuditPolicy: { createMany: write },
} as unknown as GamePrisma.TransactionClient;
try {
const result = await withPlayAuditSavepoint(db, async (auditDb) => {
for (let i = 0; i < 100; i++) await auditDb.playAuditPolicy.createMany({ data: [] });
});
expect(result.ok).toBe(false);
expect(write).toHaveBeenCalledTimes(2);
expect(
vi
.mocked(db.$executeRaw)
.mock.calls.some(
([sql]) => Array.isArray(sql) && sql[0] === 'ROLLBACK TO SAVEPOINT play_audit_optional'
)
).toBe(true);
} finally {
timer.mockRestore();
}
});
});