Files
josh f500db971b
build-and-push / build-and-push (push) Successful in 1m26s
Initial commit: Infrastructure host tracking app
Fastify + node:sqlite single-process app with vanilla JS UI for
looking up hosts by hardware ID, hostname, or asset ID. Includes
per-host network interface tracking, sites/rooms/server-types CRUD,
Docker packaging, and a Gitea Actions workflow that runs tests then
builds and pushes to gitea.thewrightserver.net/josh/infrastructure.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 17:05:50 -04:00

58 lines
2.7 KiB
JavaScript

import { test, after } from 'node:test';
import assert from 'node:assert/strict';
import { newApp } from './helpers.js';
test('rooms: CRUD with site filter', async (t) => {
const app = await newApp();
after(() => app.close());
const s1 = JSON.parse((await app.inject({ method: 'POST', url: '/api/sites', payload: { name: 'S1' } })).body);
const s2 = JSON.parse((await app.inject({ method: 'POST', url: '/api/sites', payload: { name: 'S2' } })).body);
await app.inject({ method: 'POST', url: '/api/rooms', payload: { site_id: s1.id, name: 'A' } });
await app.inject({ method: 'POST', url: '/api/rooms', payload: { site_id: s1.id, name: 'B' } });
await app.inject({ method: 'POST', url: '/api/rooms', payload: { site_id: s2.id, name: 'C' } });
const all = JSON.parse((await app.inject({ method: 'GET', url: '/api/rooms' })).body);
assert.equal(all.length, 3);
const onlyS1 = JSON.parse((await app.inject({ method: 'GET', url: `/api/rooms?site_id=${s1.id}` })).body);
assert.equal(onlyS1.length, 2);
assert.ok(onlyS1.every((r) => r.site_id === s1.id));
});
test('rooms: same name allowed in different sites, blocked in same site', async (t) => {
const app = await newApp();
after(() => app.close());
const s1 = JSON.parse((await app.inject({ method: 'POST', url: '/api/sites', payload: { name: 'S1' } })).body);
const s2 = JSON.parse((await app.inject({ method: 'POST', url: '/api/sites', payload: { name: 'S2' } })).body);
const a = await app.inject({ method: 'POST', url: '/api/rooms', payload: { site_id: s1.id, name: 'X' } });
assert.equal(a.statusCode, 201);
const b = await app.inject({ method: 'POST', url: '/api/rooms', payload: { site_id: s2.id, name: 'X' } });
assert.equal(b.statusCode, 201);
const dup = await app.inject({ method: 'POST', url: '/api/rooms', payload: { site_id: s1.id, name: 'X' } });
assert.equal(dup.statusCode, 409);
});
test('rooms: room with hosts cannot be deleted', async (t) => {
const app = await newApp();
after(() => app.close());
const site = JSON.parse((await app.inject({ method: 'POST', url: '/api/sites', payload: { name: 'S' } })).body);
const room = JSON.parse((await app.inject({ method: 'POST', url: '/api/rooms', payload: { site_id: site.id, name: 'R' } })).body);
const type = JSON.parse((await app.inject({ method: 'POST', url: '/api/server-types', payload: { name: 'T' } })).body);
await app.inject({
method: 'POST', url: '/api/hosts',
payload: {
hardware_id: 'HW', hostname: 'h', asset_id: 'A',
room_id: room.id, position: '', server_type_id: type.id,
},
});
const r = await app.inject({ method: 'DELETE', url: `/api/rooms/${room.id}` });
assert.equal(r.statusCode, 409);
});