- 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>
113 lines
3.1 KiB
TypeScript
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);
|
|
});
|