import Database from 'better-sqlite3'; import { SqliteBillingReportRepo } from '../src/db/billing-repo'; describe('SqliteBillingReportRepo', () => { it('returns 0 for an unknown customer/period', () => { const repo = new SqliteBillingReportRepo(new Database(':memory:')); expect(repo.getReportedCents('cust_1', '2026-07')).toBe(0); }); it('round-trips an upserted cumulative value', () => { const repo = new SqliteBillingReportRepo(new Database(':memory:')); repo.upsertReportedCents('cust_1', '2026-07', 500); expect(repo.getReportedCents('cust_1', '2026-07')).toBe(500); }); it('keeps periods and customers isolated', () => { const repo = new SqliteBillingReportRepo(new Database(':memory:')); repo.upsertReportedCents('cust_1', '2026-07', 500); expect(repo.getReportedCents('cust_1', '2026-08')).toBe(0); expect(repo.getReportedCents('cust_2', '2026-07')).toBe(0); }); it('replaces the cumulative value on repeated upsert (latest wins, not summed)', () => { const repo = new SqliteBillingReportRepo(new Database(':memory:')); repo.upsertReportedCents('cust_1', '2026-07', 500); repo.upsertReportedCents('cust_1', '2026-07', 800); expect(repo.getReportedCents('cust_1', '2026-07')).toBe(800); }); describe('job locks', () => { it('acquires a free lock and refuses a second acquire until released', () => { const repo = new SqliteBillingReportRepo(new Database(':memory:')); expect(repo.tryAcquireLock('report-usage', 60_000)).toBe(true); expect(repo.tryAcquireLock('report-usage', 60_000)).toBe(false); repo.releaseLock('report-usage'); expect(repo.tryAcquireLock('report-usage', 60_000)).toBe(true); }); it('takes over a lock whose age exceeds the TTL', () => { let nowMs = 1_000_000; const repo = new SqliteBillingReportRepo(new Database(':memory:'), () => nowMs); expect(repo.tryAcquireLock('report-usage', 60_000)).toBe(true); nowMs += 60_001; // lock is now stale expect(repo.tryAcquireLock('report-usage', 60_000)).toBe(true); }); it('keeps lock names independent', () => { const repo = new SqliteBillingReportRepo(new Database(':memory:')); expect(repo.tryAcquireLock('report-usage', 60_000)).toBe(true); expect(repo.tryAcquireLock('other-job', 60_000)).toBe(true); }); it('releaseLock is a no-op for a lock that was never held', () => { const repo = new SqliteBillingReportRepo(new Database(':memory:')); expect(() => repo.releaseLock('never-held')).not.toThrow(); }); }); });