zappier-edge/tests/db-invoice.test.ts

72 lines
2.6 KiB
TypeScript

import Database from 'better-sqlite3';
import { Invoice } from '../src/invoicing';
import { SqliteInvoiceRepo } from '../src/db/invoice-repo';
const invoice = (overrides: Partial<Invoice> = {}): Invoice => ({
id: 'INV-2026-07-0001',
customerId: 'cust_2',
period: '2026-07',
status: 'draft',
lines: [{ endpointId: 'storage', calls: 2, cents: 1500 }],
totalCents: 1500,
creditCents: 1000,
billableCents: 500,
billingType: 'stripe',
...overrides,
});
describe('SqliteInvoiceRepo', () => {
it('saves and retrieves an invoice with its lines', () => {
const repo = new SqliteInvoiceRepo(new Database(':memory:'));
repo.save(invoice());
const got = repo.get('INV-2026-07-0001');
expect(got?.billableCents).toBe(500);
expect(got?.lines).toEqual([{ endpointId: 'storage', calls: 2, cents: 1500 }]);
});
it('re-saving an invoice replaces its lines (draft regeneration)', () => {
const repo = new SqliteInvoiceRepo(new Database(':memory:'));
repo.save(invoice());
repo.save(invoice({ lines: [{ endpointId: 'transform', calls: 5, cents: 20 }], totalCents: 20 }));
const got = repo.get('INV-2026-07-0001');
expect(got?.lines).toEqual([{ endpointId: 'transform', calls: 5, cents: 20 }]);
expect(repo.list({})).toHaveLength(1);
});
it('filters by customer, period, and status', () => {
const repo = new SqliteInvoiceRepo(new Database(':memory:'));
repo.save(invoice());
repo.save(invoice({ id: 'INV-2026-07-0002', customerId: 'cust_3', status: 'issued' }));
repo.save(invoice({ id: 'INV-2026-08-0001', period: '2026-08' }));
expect(repo.list({ customerId: 'cust_3' })).toHaveLength(1);
expect(repo.list({ period: '2026-08' })).toHaveLength(1);
expect(repo.list({ status: 'issued' })).toHaveLength(1);
expect(repo.list({ period: '2026-07' })).toHaveLength(2);
expect(repo.list({})).toHaveLength(3);
});
it('increments sequences per period independently', () => {
const repo = new SqliteInvoiceRepo(new Database(':memory:'));
expect(repo.nextSequence('2026-07')).toBe(1);
repo.save(invoice());
expect(repo.nextSequence('2026-07')).toBe(2);
expect(repo.nextSequence('2026-08')).toBe(1);
});
it('persists lifecycle timestamps and PO numbers', () => {
const repo = new SqliteInvoiceRepo(new Database(':memory:'));
repo.save(
invoice({
billingType: 'purchase_order',
poNumber: 'PO-1',
status: 'issued',
issuedAtMs: 1000,
dueAtMs: 2000,
}),
);
const got = repo.get('INV-2026-07-0001');
expect(got?.poNumber).toBe('PO-1');
expect(got?.issuedAtMs).toBe(1000);
expect(got?.dueAtMs).toBe(2000);
});
});