51 lines
2.1 KiB
TypeScript
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();
|
|
}
|
|
});
|
|
});
|