import { describe, it, expect } from 'vitest' import { openDb } from '../src/db' import { createClient } from '../src/repos-clients' import { assignModule, createModule, setPrice } from '../src/repos-modules' import { createRecurringPlan, listRecurringPlans, updateRecurringPlan, deactivateRecurringPlan, } from '../src/repos-recurring' async function setup() { const db = openDb(':memory:') const c = await createClient(db, 'u1', { name: 'Acme', stateCode: '32' }) const m = await createModule(db, 'u1', { code: 'CLOUD', name: 'Cloud Hosting', allowedKinds: ['monthly', 'yearly'] }) await setPrice(db, 'u1', { moduleId: m.id, kind: 'monthly', pricePaise: 2_000_00, effectiveFrom: '2026-04-01' }) const cm = await assignModule(db, 'u1', { clientId: c.id, moduleId: m.id, kind: 'monthly' }) return { db, c, m, cm } } describe('recurring plans', () => { it('creates a plan, lists it, updates and deactivates with audit', async () => { const { db, c, cm } = await setup() const p = await createRecurringPlan(db, 'u1', { clientId: c.id, clientModuleId: cm.id, cadence: 'monthly', nextRun: '2026-08-01', policy: 'auto', }) expect(p.policy).toBe('auto') expect(p.amountPaise).toBeNull() // price resolves from the module at generation expect(await listRecurringPlans(db, c.id)).toHaveLength(1) const up = await updateRecurringPlan(db, 'u1', p.id, { amountPaise: 2_500_00 }) expect(up.amountPaise).toBe(2_500_00) const off = await deactivateRecurringPlan(db, 'u1', p.id) expect(off.active).toBe(false) const audits = await db.all(`SELECT action FROM audit_log WHERE entity='recurring_plan'`) expect(audits.length).toBe(3) // create + update + deactivate }) it('rejects a plan whose module has no price and no explicit amount', async () => { const db = openDb(':memory:') const c = await createClient(db, 'u1', { name: 'X', stateCode: '32' }) const m = await createModule(db, 'u1', { code: 'POS', name: 'POS', allowedKinds: ['yearly'] }) const cm = await assignModule(db, 'u1', { clientId: c.id, moduleId: m.id, kind: 'yearly' }) await expect(createRecurringPlan(db, 'u1', { clientId: c.id, clientModuleId: cm.id, cadence: 'yearly', nextRun: '2026-08-01', })).rejects.toThrow(/price/i) }) it('rejects a client_module that belongs to another client', async () => { const { db, cm } = await setup() const other = await createClient(db, 'u1', { name: 'Other', stateCode: '32' }) await expect(createRecurringPlan(db, 'u1', { clientId: other.id, clientModuleId: cm.id, cadence: 'monthly', nextRun: '2026-08-01', amountPaise: 100_00, })).rejects.toThrow(/client/i) }) })