import { test, after } from 'node:test'; import assert from 'node:assert/strict'; import { newApp, seedFixtures, seedHost } from './helpers.js'; const validIface = (host_id, name = 'eth0') => ({ host_id, name, mac_address: 'aa:bb:cc:dd:ee:ff', ip_address: '10.0.0.5', subnet: '10.0.0.0/24', link_speed: '1000/full', }); test('interfaces: create, list, get, update, delete', async () => { const app = await newApp(); after(() => app.close()); const fx = await seedFixtures(app); const host = await seedHost(app, fx, 'A'); const create = await app.inject({ method: 'POST', url: '/api/interfaces', payload: validIface(host.id, 'eth0'), }); assert.equal(create.statusCode, 201); const iface = JSON.parse(create.body); assert.equal(iface.name, 'eth0'); assert.equal(iface.host_id, host.id); assert.equal(iface.ip_address, '10.0.0.5'); const list = await app.inject({ method: 'GET', url: `/api/interfaces?host_id=${host.id}`, }); assert.equal(list.statusCode, 200); assert.equal(JSON.parse(list.body).length, 1); const get = await app.inject({ method: 'GET', url: `/api/interfaces/${iface.id}` }); assert.equal(get.statusCode, 200); assert.equal(JSON.parse(get.body).name, 'eth0'); const upd = await app.inject({ method: 'PUT', url: `/api/interfaces/${iface.id}`, payload: { ...validIface(host.id, 'eth0'), ip_address: '10.0.0.99' }, }); assert.equal(upd.statusCode, 200); assert.equal(JSON.parse(upd.body).ip_address, '10.0.0.99'); const del = await app.inject({ method: 'DELETE', url: `/api/interfaces/${iface.id}` }); assert.equal(del.statusCode, 204); const after404 = await app.inject({ method: 'GET', url: `/api/interfaces/${iface.id}` }); assert.equal(after404.statusCode, 404); }); test('interfaces: optional fields may be empty strings', async () => { const app = await newApp(); after(() => app.close()); const fx = await seedFixtures(app); const host = await seedHost(app, fx, 'B'); const r = await app.inject({ method: 'POST', url: '/api/interfaces', payload: { host_id: host.id, name: 'eth0' }, }); assert.equal(r.statusCode, 201); const iface = JSON.parse(r.body); assert.equal(iface.mac_address, ''); assert.equal(iface.ip_address, ''); assert.equal(iface.subnet, ''); assert.equal(iface.link_speed, ''); }); test('interfaces: duplicate (host_id, name) returns 409', async () => { const app = await newApp(); after(() => app.close()); const fx = await seedFixtures(app); const host = await seedHost(app, fx, 'C'); const a = await app.inject({ method: 'POST', url: '/api/interfaces', payload: { host_id: host.id, name: 'eth0' }, }); assert.equal(a.statusCode, 201); const dup = await app.inject({ method: 'POST', url: '/api/interfaces', payload: { host_id: host.id, name: 'eth0' }, }); assert.equal(dup.statusCode, 409); assert.match(JSON.parse(dup.body).error, /already exists/); }); test('interfaces: same name allowed on different hosts', async () => { const app = await newApp(); after(() => app.close()); const fx = await seedFixtures(app); const h1 = await seedHost(app, fx, 'D1'); const h2 = await seedHost(app, fx, 'D2'); const a = await app.inject({ method: 'POST', url: '/api/interfaces', payload: { host_id: h1.id, name: 'eth0' }, }); const b = await app.inject({ method: 'POST', url: '/api/interfaces', payload: { host_id: h2.id, name: 'eth0' }, }); assert.equal(a.statusCode, 201); assert.equal(b.statusCode, 201); }); test('interfaces: missing host FK returns 409', async () => { const app = await newApp(); after(() => app.close()); const r = await app.inject({ method: 'POST', url: '/api/interfaces', payload: { host_id: 9999, name: 'eth0' }, }); assert.equal(r.statusCode, 409); }); test('interfaces: invalid formats return 400 with details', async () => { const app = await newApp(); after(() => app.close()); const fx = await seedFixtures(app); const host = await seedHost(app, fx, 'E'); const cases = [ { mac_address: 'zz:zz:zz:zz:zz:zz' }, { ip_address: '999.0.0.1' }, { subnet: '10.0.0.0/99' }, { link_speed: '1000/weird' }, ]; for (const extra of cases) { const r = await app.inject({ method: 'POST', url: '/api/interfaces', payload: { host_id: host.id, name: 'eth0', ...extra }, }); assert.equal(r.statusCode, 400, `expected 400 for ${JSON.stringify(extra)}`); const body = JSON.parse(r.body); assert.equal(body.error, 'validation failed'); assert.ok(Array.isArray(body.details) && body.details.length > 0); } }); test('interfaces: deleting the host cascades', async () => { const app = await newApp(); after(() => app.close()); const fx = await seedFixtures(app); const host = await seedHost(app, fx, 'F'); await app.inject({ method: 'POST', url: '/api/interfaces', payload: { host_id: host.id, name: 'eth0' }, }); await app.inject({ method: 'POST', url: '/api/interfaces', payload: { host_id: host.id, name: 'eth1' }, }); const del = await app.inject({ method: 'DELETE', url: `/api/hosts/${host.id}` }); assert.equal(del.statusCode, 204); const list = await app.inject({ method: 'GET', url: `/api/interfaces?host_id=${host.id}`, }); assert.equal(list.statusCode, 200); assert.equal(JSON.parse(list.body).length, 0); }); test('interfaces: list requires host_id', async () => { const app = await newApp(); after(() => app.close()); const r = await app.inject({ method: 'GET', url: '/api/interfaces' }); assert.equal(r.statusCode, 400); });