Files
tessera/scripts/smoke-test.ts
Gjermund Høsøien Wiggen b96ba21e99 feat: add database seed script and utility scripts
- src/db/seed.ts: comprehensive seed data with idempotent upserts
  - 5 users (system, gjermund, operator, technician, analyst)
  - 5 queues (Support Desk, Operations, IT Infrastructure, Facilities, Field Ops)
  - 1 lifecycle (Demo service lifecycle with new→open→in_progress→resolved→closed)
  - 5 custom fields (impact, location, channel, urgency, outcome) with short keys
  - 10 realistic support tickets with varied statuses, custom fields, and history
  - 3 scrips (OnCreate email, OnResolve custom field, customer notification)
  - 2 templates (auto-response, resolve notification)
  - --reset flag to truncate all data before seeding
- scripts/smoke-test.ts: API smoke tests
- scripts/watch-frontend.sh: frontend dev helper

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 10:43:18 +02:00

113 lines
3.1 KiB
TypeScript

const backendUrl = process.env.BACKEND_URL ?? 'http://127.0.0.1:9876';
const frontendUrl = process.env.FRONTEND_URL ?? 'http://127.0.0.1:3100';
interface Ticket {
id: number;
subject: string;
}
interface Queue {
id: string;
name: string;
}
interface Transaction {
id: string;
ticket_id: number;
transaction_type: string;
}
async function requestJson<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`${url} returned ${response.status} ${response.statusText}`);
}
return response.json() as Promise<T>;
}
async function requestOk(url: string): Promise<void> {
const response = await fetch(url, { method: 'HEAD' });
if (!response.ok) {
throw new Error(`${url} returned ${response.status} ${response.statusText}`);
}
}
async function check(name: string, fn: () => Promise<void>): Promise<void> {
try {
await fn();
console.log(`ok ${name}`);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
console.error(`fail ${name}`);
console.error(` ${message}`);
process.exitCode = 1;
}
}
async function main() {
let ticketForDetail: Ticket | null = null;
await check('backend health', async () => {
const health = await requestJson<{ status: string }>(`${backendUrl}/health`);
if (health.status !== 'ok') {
throw new Error(`expected status ok, got ${JSON.stringify(health)}`);
}
});
await check('queues exist', async () => {
const queues = await requestJson<Queue[]>(`${backendUrl}/queues`);
if (queues.length < 1) {
throw new Error('expected at least one queue');
}
});
await check('tickets exist', async () => {
const tickets = await requestJson<Ticket[]>(`${backendUrl}/tickets`);
if (tickets.length < 1) {
throw new Error('expected at least one ticket');
}
ticketForDetail = tickets.find((ticket) => ticket.subject.includes('VPN access')) ?? tickets[0] ?? null;
});
await check('ticket detail has activity', async () => {
if (!ticketForDetail) {
throw new Error('no ticket available for detail check');
}
const transactions = await requestJson<Transaction[]>(
`${backendUrl}/tickets/${ticketForDetail.id}/transactions`,
);
if (transactions.length < 1) {
throw new Error(`expected ticket ${ticketForDetail.id} to have transactions`);
}
});
await check('frontend index responds', async () => {
await requestOk(frontendUrl);
});
await check('frontend ticket detail responds', async () => {
if (!ticketForDetail) {
throw new Error('no ticket available for frontend detail check');
}
await requestOk(`${frontendUrl}/tickets/${ticketForDetail.id}`);
});
await check('frontend api proxy responds', async () => {
const health = await requestJson<{ status: string }>(`${frontendUrl}/api/health`);
if (health.status !== 'ok') {
throw new Error(`expected status ok, got ${JSON.stringify(health)}`);
}
});
if (process.exitCode) {
process.exit(process.exitCode);
}
console.log('Smoke test passed');
}
main().catch((err) => {
console.error(err);
process.exit(1);
});