Add standalone API test runner with accounts and members suites

Custom test framework that starts the backend, creates a test DB, runs
migrations, and hits real HTTP endpoints. Supports --suite and --tag
filtering. 24 tests covering account CRUD, member inheritance, state
normalization, move, search, and auto-generated numbers. Run with
bun run api-test.
This commit is contained in:
Ryan Moon
2026-03-28 12:52:04 -05:00
parent b9e984cfa3
commit f47bfdbcdb
7 changed files with 733 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
const green = (s: string) => `\x1b[32m${s}\x1b[0m`
const red = (s: string) => `\x1b[31m${s}\x1b[0m`
const gray = (s: string) => `\x1b[90m${s}\x1b[0m`
const bold = (s: string) => `\x1b[1m${s}\x1b[0m`
const yellow = (s: string) => `\x1b[33m${s}\x1b[0m`
export interface TestResult {
name: string
suite: string
tags: string[]
passed: boolean
duration: number
error?: string
}
export function printHeader(baseUrl: string) {
console.log('')
console.log(bold(`API Tests — ${baseUrl}`))
console.log('')
}
export function printSuiteHeader(name: string) {
console.log(` ${bold(name)}`)
}
export function printTestResult(result: TestResult) {
const icon = result.passed ? green('✓') : red('✗')
const duration = gray(`(${result.duration}ms)`)
console.log(` ${icon} ${result.name} ${duration}`)
if (result.error) {
console.log(` ${red(result.error)}`)
}
}
export function printSummary(results: TestResult[], totalDuration: number) {
const passed = results.filter((r) => r.passed).length
const failed = results.filter((r) => !r.passed).length
const total = results.length
console.log('')
console.log('─'.repeat(50))
if (failed === 0) {
console.log(green(` ${passed} passed`) + gray(` (${(totalDuration / 1000).toFixed(1)}s)`))
} else {
console.log(
` ${green(`${passed} passed`)}, ${red(`${failed} failed`)}` +
gray(` of ${total} (${(totalDuration / 1000).toFixed(1)}s)`),
)
console.log('')
console.log(red(' Failed tests:'))
for (const r of results.filter((r) => !r.passed)) {
console.log(red(`${r.suite} > ${r.name}`))
if (r.error) console.log(` ${r.error}`)
}
}
console.log('')
}
export function printSkipped(reason: string) {
console.log(yellow(` ⊘ Skipped: ${reason}`))
}