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.
55 lines
3.1 KiB
TypeScript
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
|
|
})
|
|
})
|