const { lockBudgetPoolsForUpdate, resolveApplicableBudgetPoolByContext } = require('../../../src/services/budgetPool'); describe('budget pool write protection', () => { it('locks unique pools in stable order with parameters and rejects a root client', async () => { const tx = { $queryRaw: vi.fn().mockResolvedValue([]), $transaction: vi.fn() }; await lockBudgetPoolsForUpdate(tx, ['b', 'a', 'b', null]); const query = tx.$queryRaw.mock.calls[0][0]; expect(query.sql).toMatch(/ORDER BY id FOR NO KEY UPDATE/); expect(query.values).toEqual(['a', 'b']); await expect(lockBudgetPoolsForUpdate({ ...tx, $connect: vi.fn() }, ['a'])).rejects.toThrow(/transaction/); }); it('does not lock preview reads but refreshes usage after a submission lock', async () => { const first = { id: 'pool', used_amount: '0.00' }; const latest = { ...first, used_amount: '60.00' }; const tx = { $queryRaw: vi.fn().mockResolvedValue([]), budgetPool: { findMany: vi.fn().mockResolvedValueOnce([first]).mockResolvedValueOnce([first]).mockResolvedValueOnce([latest]) } }; const context = { effectiveDate: new Date('2026-09-19') }; expect(await resolveApplicableBudgetPoolByContext(tx, context)).toEqual(first); expect(tx.$queryRaw).not.toHaveBeenCalled(); expect(await resolveApplicableBudgetPoolByContext(tx, context, { forUpdate: true })).toEqual(latest); expect(tx.budgetPool.findMany.mock.invocationCallOrder[1]).toBeLessThan(tx.$queryRaw.mock.invocationCallOrder[0]); expect(tx.$queryRaw.mock.invocationCallOrder[0]).toBeLessThan(tx.budgetPool.findMany.mock.invocationCallOrder[2]); }); it.each([{ records: [] }, { records: [{ id: 'replacement' }] }])('rejects a candidate changed during the wait: %j', async ({ records }) => { const tx = { $queryRaw: vi.fn().mockResolvedValue([]), budgetPool: { findMany: vi.fn().mockResolvedValueOnce([{ id: 'old' }]).mockResolvedValueOnce(records) } }; await expect(resolveApplicableBudgetPoolByContext(tx, { effectiveDate: new Date('2026-09-19') }, { forUpdate: true })) .rejects.toMatchObject({ statusCode: 409, details: { reasonCode: 'budget.pool_changed' } }); }); });