feat(hq): schedule monthly aws cost pull and owner-gated manual pull route
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>feat/client-detail-redesign
parent
ea8c2b23ad
commit
5495b42e30
@ -0,0 +1,39 @@
|
||||
// apps/hq/test/aws-pull-route.test.ts
|
||||
import { describe, it, expect, afterAll, beforeAll } from 'vitest'
|
||||
import express from 'express'
|
||||
import { openDb } from '../src/db'
|
||||
import { seedIfEmpty } from '../src/seed'
|
||||
import { createStaff } from '../src/auth'
|
||||
import { apiRouter } from '../src/api'
|
||||
|
||||
describe('POST /api/aws/pull without credentials', () => {
|
||||
const prevKey = process.env['AWS_ACCESS_KEY_ID']
|
||||
const prevSecret = process.env['AWS_SECRET_ACCESS_KEY']
|
||||
beforeAll(() => { delete process.env['AWS_ACCESS_KEY_ID']; delete process.env['AWS_SECRET_ACCESS_KEY'] })
|
||||
afterAll(() => {
|
||||
if (prevKey !== undefined) process.env['AWS_ACCESS_KEY_ID'] = prevKey
|
||||
if (prevSecret !== undefined) process.env['AWS_SECRET_ACCESS_KEY'] = prevSecret
|
||||
})
|
||||
|
||||
const db = openDb(':memory:'); seedIfEmpty(db)
|
||||
createStaff(db, { email: 'owner@test.in', displayName: 'Owner', role: 'owner', password: 'owner-password' })
|
||||
createStaff(db, { email: 'staff@test.in', displayName: 'Staff', role: 'staff', password: 'staff-password' })
|
||||
const app = express(); app.use(express.json()); app.locals['db'] = db; app.use('/api', apiRouter(db))
|
||||
const server = app.listen(0)
|
||||
const base = `http://localhost:${(server.address() as { port: number }).port}/api`
|
||||
afterAll(() => server.close())
|
||||
const login = async (email: string, password: string) =>
|
||||
(await (await fetch(`${base}/auth/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email, password }) })).json() as any).token
|
||||
|
||||
it('returns 409 when AWS creds are absent', async () => {
|
||||
const token = await login('owner@test.in', 'owner-password')
|
||||
const res = await fetch(`${base}/aws/pull`, { method: 'POST', headers: { 'content-type': 'application/json', authorization: `Bearer ${token}` }, body: '{}' })
|
||||
expect(res.status).toBe(409)
|
||||
expect((await res.json() as any).error).toBe('aws-credentials-not-configured')
|
||||
})
|
||||
it('is owner-gated', async () => {
|
||||
const token = await login('staff@test.in', 'staff-password')
|
||||
const res = await fetch(`${base}/aws/pull`, { method: 'POST', headers: { 'content-type': 'application/json', authorization: `Bearer ${token}` }, body: '{}' })
|
||||
expect(res.status).toBe(403)
|
||||
})
|
||||
})
|
||||
Loading…
Reference in New Issue