Release: test infrastructure, rules overhaul, and design refresh #30

Merged
TheFurya merged 43 commits from develop into main 2026-02-21 16:58:18 +01:00
3 changed files with 37 additions and 15 deletions
Showing only changes of commit a12478f24b - Show all commits

View File

@@ -1,5 +1,8 @@
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = dirname(fileURLToPath(import.meta.url))
interface Fixtures {
gameId: number

View File

@@ -1,9 +1,12 @@
import { execSync } from 'node:child_process'
import { writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = dirname(fileURLToPath(import.meta.url))
const API_BASE = 'http://localhost:8000/api/v1'
const COMPOSE_FILE = resolve(__dirname, '../../docker-compose.test.yml')
const COMPOSE = `docker compose -p nuzlocke-test -f ${COMPOSE_FILE}`
const FIXTURES_PATH = resolve(__dirname, '.fixtures.json')
function run(cmd: string): string {
@@ -11,6 +14,19 @@ function run(cmd: string): string {
return execSync(cmd, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'inherit'] })
}
async function waitForApi(url: string, maxAttempts = 30): Promise<void> {
for (let i = 0; i < maxAttempts; i++) {
try {
const res = await fetch(url)
if (res.ok) return
} catch {
// not ready yet
}
await new Promise((r) => setTimeout(r, 2000))
}
throw new Error(`API at ${url} not ready after ${maxAttempts} attempts`)
}
async function api<T>(
path: string,
options?: RequestInit,
@@ -28,19 +44,19 @@ async function api<T>(
export default async function globalSetup() {
// 1. Start test DB + API
run(`docker compose -f ${COMPOSE_FILE} up -d --wait`)
run(`${COMPOSE} up -d --build`)
// 2. Run migrations
run(
`docker compose -f ${COMPOSE_FILE} exec -T test-api alembic -c /app/alembic.ini upgrade head`,
)
// 2. Wait for API to be healthy
console.log('[setup] Waiting for API to be ready...')
await waitForApi('http://localhost:8000/')
// 3. Seed reference data (run from /app/src where the app package lives)
run(
`docker compose -f ${COMPOSE_FILE} exec -T -w /app/src test-api python -m app.seeds`,
)
// 3. Run migrations
run(`${COMPOSE} exec -T test-api alembic -c /app/alembic.ini upgrade head`)
// 4. Create test fixtures via API
// 4. Seed reference data (run from /app/src where the app package lives)
run(`${COMPOSE} exec -T -w /app/src test-api python -m app.seeds`)
// 5. Create test fixtures via API
const games = await api<Array<{ id: number; name: string }>>('/games')
const game = games[0]
if (!game) throw new Error('No games found after seeding')
@@ -96,7 +112,7 @@ export default async function globalSetup() {
}),
})
// 5. Write fixtures file
// 6. Write fixtures file
const fixtures = {
gameId: game.id,
runId: testRun.id,

View File

@@ -1,13 +1,16 @@
import { execSync } from 'node:child_process'
import { rmSync } from 'node:fs'
import { resolve } from 'node:path'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = dirname(fileURLToPath(import.meta.url))
const COMPOSE_FILE = resolve(__dirname, '../../docker-compose.test.yml')
const COMPOSE = `docker compose -p nuzlocke-test -f ${COMPOSE_FILE}`
const FIXTURES_PATH = resolve(__dirname, '.fixtures.json')
export default async function globalTeardown() {
console.log('[teardown] Stopping test containers...')
execSync(`docker compose -f ${COMPOSE_FILE} down -v`, {
execSync(`${COMPOSE} down -v --remove-orphans`, {
encoding: 'utf-8',
stdio: 'inherit',
})