import { describe, it, expect, vi, beforeEach } from "vitest"; import Fastify from "fastify"; import { registerBastionRoutes } from "../src/routes/bastions.js"; import { bastionRegistry } from "../src/services/bastion-registry.js"; import type { DbClient } from "../src/server.js"; import type { BastionState } from "@lab/shared"; function createMockDb(servers: unknown[] = []): DbClient { return { $queryRaw: vi.fn().mockResolvedValue([{ "?column?": 1 }]), server: { findMany: vi.fn().mockResolvedValue(servers), findUnique: vi.fn().mockResolvedValue(null), upsert: vi.fn().mockResolvedValue({}), }, joinToken: { findUnique: vi.fn().mockResolvedValue(null), findMany: vi.fn().mockResolvedValue([]), create: vi.fn().mockResolvedValue({ id: "t" }), update: vi.fn().mockResolvedValue({}), }, bastion: { upsert: vi.fn().mockResolvedValue({}), findMany: vi.fn().mockResolvedValue([]), findUnique: vi.fn().mockResolvedValue(null), update: vi.fn().mockResolvedValue({}), }, }; } function registerFakeBastion(bastionId: string, state: BastionState): void { bastionRegistry.register({ bastionId, hostname: "fake", network: "192.168.8.0/24", serverIp: "192.168.8.11", // socket is referenced only on commands, not during aggregation socket: { on: () => undefined, off: () => undefined, send: () => undefined, close: () => undefined } as never, connectedAt: new Date(), lastHeartbeat: new Date(), state, }); } describe("GET /api/machines aggregation", () => { beforeEach(() => { for (const b of bastionRegistry.getAll()) bastionRegistry.unregister(b.bastionId); }); it("promotes a live-discovered MAC to installed when the DB has a real hostname+role for it", async () => { // Simulates the worker0-k8s0 bug: bastion restarted, lost its installed map, // rediscovered the machine via DHCP/PXE. DB still has hostname=worker0-k8s0, // role=infra, ip=192.168.8.23. Without the fix, the CLI sees a "discovered" // row with no hostname/role/IP. With the fix, the row is promoted to // "installed" with full identity preserved. const mac = "78:55:36:08:28:fb"; registerFakeBastion("b1", { discovered: { [mac]: { mac, product: "SER", board: "SER", serial: "x", manufacturer: "AZW", cpu_model: "AMD Ryzen 7 255", cpu_cores: 16, memory_gb: 58, arch: "x86_64", disks: [], nics: [], first_seen: "", last_seen: "", }, }, install_queue: {}, installed: {}, debug: {}, }); const app = Fastify({ logger: false }); const db = createMockDb([ { mac, hostname: "worker0-k8s0", role: "infra", ip: "192.168.8.23", status: "discovered", labels: {} }, ]); registerBastionRoutes(app, db); const res = await app.inject({ method: "GET", url: "/api/machines" }); expect(res.statusCode).toBe(200); const body = JSON.parse(res.body); expect(body.discovered[mac]).toBeUndefined(); expect(body.installed[mac]).toMatchObject({ hostname: "worker0-k8s0", role: "infra", ip: "192.168.8.23", cpu_model: "AMD Ryzen 7 255", cpu_cores: 16, memory_gb: 58, }); await app.close(); }); it("leaves a fresh-discovery MAC in discovered when DB only has a discovery-shaped record", async () => { const mac = "aa:bb:cc:dd:ee:ff"; registerFakeBastion("b1", { discovered: { [mac]: { mac, product: "SER", board: "SER", serial: "x", manufacturer: "AZW", cpu_model: "AMD Ryzen 7", cpu_cores: 8, memory_gb: 32, arch: "x86_64", disks: [], nics: [], first_seen: "", last_seen: "", }, }, install_queue: {}, installed: {}, debug: {}, }); const app = Fastify({ logger: false }); // Matches what labd writes on first discovery: hostname=product, role="unknown" const db = createMockDb([ { mac, hostname: "SER", role: "unknown", ip: null, status: "discovered", labels: {} }, ]); registerBastionRoutes(app, db); const res = await app.inject({ method: "GET", url: "/api/machines" }); const body = JSON.parse(res.body); expect(body.discovered[mac]).toBeDefined(); expect(body.installed[mac]).toBeUndefined(); await app.close(); }); it("falls back to DB for MACs not in any live bucket", async () => { const mac = "11:22:33:44:55:66"; // No bastions connected const app = Fastify({ logger: false }); const db = createMockDb([ { mac, hostname: "worker1-k8s0", role: "infra", ip: "192.168.8.13", status: "online", labels: {} }, ]); registerBastionRoutes(app, db); const res = await app.inject({ method: "GET", url: "/api/machines" }); const body = JSON.parse(res.body); expect(body.installed[mac]).toMatchObject({ hostname: "worker1-k8s0", role: "infra", ip: "192.168.8.13", }); await app.close(); }); });