You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sims-hq/apps/hq/test/aws-repo.test.ts

55 lines
3.1 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { openDb } from '../src/db'
import { createClient } from '../src/repos-clients'
import { upsertAwsUsage, getAwsUsage, listAwsUsage, costRanking, awsCostForClient, previousMonth } from '../src/repos-aws'
async function setup() {
const db = openDb(':memory:')
const a = await createClient(db, 'u1', { name: 'Acme', code: 'ACME', stateCode: '32' })
const b = await createClient(db, 'u1', { name: 'Bolt', code: 'BOLT', stateCode: '29' })
return { db, a, b }
}
describe('aws usage repo', () => {
it('upserts and merges a (client, month) row without clobbering the other source', async () => {
const { db, a } = await setup()
await upsertAwsUsage(db, 'u1', { clientId: a.id, month: '2026-06', costPaise: 5_000_00, source: 'auto' })
await upsertAwsUsage(db, 'u1', { clientId: a.id, month: '2026-06', storageGb: 42.5, source: 'manual' }) // merges
const row = (await getAwsUsage(db, a.id, '2026-06'))!
expect(row.costPaise).toBe(5_000_00) // auto cost preserved
expect(row.storageGb).toBe(42.5) // manual storage applied
expect(await db.get(`SELECT COUNT(*) AS n FROM aws_usage WHERE client_id=?`, a.id)).toMatchObject({ n: 1 })
const audits = await db.all(`SELECT action FROM audit_log WHERE entity='aws_usage'`)
expect(audits.length).toBe(2) // create + update
})
it('lists newest month first, capped', async () => {
const { db, a } = await setup()
for (const m of ['2026-04', '2026-05', '2026-06']) await upsertAwsUsage(db, 'u1', { clientId: a.id, month: m, costPaise: 100 })
expect((await listAwsUsage(db, a.id, 2)).map((r) => r.month)).toEqual(['2026-06', '2026-05'])
})
it('rejects a bad month', async () => {
const { db, a } = await setup()
await expect(upsertAwsUsage(db, 'u1', { clientId: a.id, month: '2026/06', costPaise: 1 })).rejects.toThrow(/month/i)
})
it('ranks clients by cost with share in basis points', async () => {
const { db, a, b } = await setup()
await upsertAwsUsage(db, 'u1', { clientId: a.id, month: '2026-06', costPaise: 7_500_00 })
await upsertAwsUsage(db, 'u1', { clientId: b.id, month: '2026-06', costPaise: 2_500_00 })
const r = await costRanking(db, '2026-06')
expect(r.total).toBe(10_000_00)
expect(r.rows.map((x) => x.clientId)).toEqual([a.id, b.id]) // Acme first (higher cost)
expect(r.rows[0]!.sharePctBp).toBe(7500) // 75.00%
expect(r.rows[1]!.sharePctBp).toBe(2500)
})
it('sums a client cost over a month range and derives the previous month', async () => {
const { db, a } = await setup()
await upsertAwsUsage(db, 'u1', { clientId: a.id, month: '2026-05', costPaise: 100 })
await upsertAwsUsage(db, 'u1', { clientId: a.id, month: '2026-06', costPaise: 200 })
await upsertAwsUsage(db, 'u1', { clientId: a.id, month: '2026-07', costPaise: 400 })
expect(await awsCostForClient(db, a.id, '2026-05-01', '2026-06-30')).toBe(300)
expect(await awsCostForClient(db, a.id)).toBe(700)
expect(previousMonth('2026-07-10')).toBe('2026-06')
expect(previousMonth('2026-01-05')).toBe('2025-12') // year rollover
})
})