Files
lunarfront-app/packages/backend/api-tests/suites/accounts.ts
Ryan Moon f47bfdbcdb 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.
2026-03-28 12:52:04 -05:00

104 lines
4.1 KiB
TypeScript

import { suite } from '../lib/context.js'
suite('Accounts', { tags: ['accounts', 'crud'] }, (t) => {
t.test('creates an account', { tags: ['create'] }, async () => {
const res = await t.api.post('/v1/accounts', { name: 'Test Account' })
t.assert.status(res, 201)
t.assert.equal(res.data.name, 'Test Account')
t.assert.ok(res.data.id)
t.assert.ok(res.data.accountNumber, 'should auto-generate account number')
t.assert.equal(res.data.accountNumber.length, 6)
})
t.test('creates account with address and normalizes state', { tags: ['create'] }, async () => {
const res = await t.api.post('/v1/accounts', {
name: 'Texas Family',
address: { street: '123 Main', city: 'Austin', state: 'texas', zip: '78701' },
})
t.assert.status(res, 201)
t.assert.equal(res.data.address.state, 'TX')
})
t.test('lists accounts with pagination', { tags: ['read'] }, async () => {
await t.api.post('/v1/accounts', { name: 'List Test A' })
await t.api.post('/v1/accounts', { name: 'List Test B' })
const res = await t.api.get('/v1/accounts', { limit: 100 })
t.assert.status(res, 200)
t.assert.ok(res.data.data.length >= 2)
t.assert.ok(res.data.pagination.total >= 2)
})
t.test('searches accounts by name', { tags: ['search'] }, async () => {
await t.api.post('/v1/accounts', { name: 'Searchable Unicorn' })
const res = await t.api.get('/v1/accounts', { q: 'Unicorn' })
t.assert.status(res, 200)
t.assert.ok(res.data.data.some((a: { name: string }) => a.name === 'Searchable Unicorn'))
})
t.test('searches accounts by member name', { tags: ['search'] }, async () => {
const acct = await t.api.post('/v1/accounts', { name: 'Member Search Family' })
await t.api.post(`/v1/accounts/${acct.data.id}/members`, {
firstName: 'FindableKid',
lastName: 'Smith',
})
const res = await t.api.get('/v1/accounts', { q: 'FindableKid' })
t.assert.status(res, 200)
t.assert.ok(res.data.data.some((a: { id: string }) => a.id === acct.data.id))
})
t.test('gets account by id', { tags: ['read'] }, async () => {
const created = await t.api.post('/v1/accounts', { name: 'Get By ID' })
const res = await t.api.get(`/v1/accounts/${created.data.id}`)
t.assert.status(res, 200)
t.assert.equal(res.data.name, 'Get By ID')
})
t.test('returns 404 for missing account', { tags: ['read'] }, async () => {
const res = await t.api.get('/v1/accounts/a0000000-0000-0000-0000-999999999999')
t.assert.status(res, 404)
})
t.test('updates an account', { tags: ['update'] }, async () => {
const created = await t.api.post('/v1/accounts', { name: 'Before Update' })
const res = await t.api.patch(`/v1/accounts/${created.data.id}`, { name: 'After Update' })
t.assert.status(res, 200)
t.assert.equal(res.data.name, 'After Update')
})
t.test('soft-deletes an account', { tags: ['delete'] }, async () => {
const created = await t.api.post('/v1/accounts', { name: 'To Delete' })
const res = await t.api.del(`/v1/accounts/${created.data.id}`)
t.assert.status(res, 200)
t.assert.equal(res.data.isActive, false)
})
t.test('sets primary member on first member create', { tags: ['create'] }, async () => {
const acct = await t.api.post('/v1/accounts', { name: 'Primary Test' })
const member = await t.api.post(`/v1/accounts/${acct.data.id}/members`, {
firstName: 'First',
lastName: 'Member',
})
const refreshed = await t.api.get(`/v1/accounts/${acct.data.id}`)
t.assert.equal(refreshed.data.primaryMemberId, member.data.id)
})
t.test('does not overwrite primary on second member', { tags: ['create'] }, async () => {
const acct = await t.api.post('/v1/accounts', { name: 'Primary Keep' })
const first = await t.api.post(`/v1/accounts/${acct.data.id}/members`, {
firstName: 'First',
lastName: 'One',
})
await t.api.post(`/v1/accounts/${acct.data.id}/members`, {
firstName: 'Second',
lastName: 'One',
})
const refreshed = await t.api.get(`/v1/accounts/${acct.data.id}`)
t.assert.equal(refreshed.data.primaryMemberId, first.data.id)
})
})