fix: rebuild HWPOD native L1 shell
This commit is contained in:
+21
-1
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
import { validateHwpodOperationInput } from "./contracts.ts";
|
||||
import { buildHwpodTopologyReadModel } from "../cloud/hwpod-topology-read-model.ts";
|
||||
|
||||
export function createHwpodHttpApp(options: { runtimeApiUrl: string; runtimeApiAuthorization: string; temporal: any; specRegistry: any }) {
|
||||
return {
|
||||
@@ -28,7 +29,9 @@ export function createHwpodHttpApp(options: { runtimeApiUrl: string; runtimeApiA
|
||||
if (request.method === "DELETE") return json(200, await options.specRegistry.delete(hwpodId));
|
||||
return json(405, { ok: false, error: { code: "method_not_allowed", message: "GET, PUT, or DELETE required" }, valuesPrinted: false });
|
||||
}
|
||||
if (url.pathname === "/v1/hwpod/topology" && request.method === "GET") return proxyGet(options.runtimeApiUrl, `/v1/hwpod/topology${url.search}`, options.runtimeApiAuthorization);
|
||||
if (url.pathname === "/v1/hwpod/topology" && request.method === "GET") {
|
||||
return json(200, await buildHwpodTopologyFromRegistry(options.specRegistry, Object.fromEntries(url.searchParams)));
|
||||
}
|
||||
if (url.pathname === "/v1/hwpod-node-ops" && request.method === "GET") return proxyGet(options.runtimeApiUrl, `/v1/hwpod-node-ops${url.search}`, options.runtimeApiAuthorization);
|
||||
if (url.pathname === "/v1/hwpod/operations" && request.method === "POST") {
|
||||
const body = await request.json();
|
||||
@@ -59,6 +62,23 @@ export function createHwpodHttpApp(options: { runtimeApiUrl: string; runtimeApiA
|
||||
};
|
||||
}
|
||||
|
||||
export async function buildHwpodTopologyFromRegistry(specRegistry: any, query: Record<string, string> = {}) {
|
||||
const listed = await specRegistry.list();
|
||||
const specs = await Promise.all((listed.specs ?? []).map(async (summary: any) => {
|
||||
const entity = await specRegistry.get(summary.hwpodId);
|
||||
return {
|
||||
...summary,
|
||||
document: entity.document,
|
||||
authority: entity.authority,
|
||||
nodeId: entity.nodeId,
|
||||
};
|
||||
}));
|
||||
return buildHwpodTopologyReadModel(specs, {
|
||||
mode: "hwpod-native-api-no-node-registry",
|
||||
nodes: [],
|
||||
}, query);
|
||||
}
|
||||
|
||||
async function readiness(options: { runtimeApiUrl: string; runtimeApiAuthorization: string; temporal: any; specRegistry: any }) {
|
||||
try {
|
||||
const specs = await options.specRegistry.list();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { expect, test } from "bun:test";
|
||||
|
||||
import { createHwpodHttpApp } from "./http.ts";
|
||||
import { buildHwpodTopologyFromRegistry, createHwpodHttpApp } from "./http.ts";
|
||||
import { createHwpodSpecRegistry, type HwpodRuntimeSpecRecord, type HwpodRuntimeSpecStore } from "./spec-registry.ts";
|
||||
|
||||
test("runtime spec repository persists CRUD and freezes YAML-first built-ins", async () => {
|
||||
@@ -53,6 +53,31 @@ test("L1 HWPOD HTTP exposes runtime CRUD and typed frozen errors", async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test("L0 HWPOD topology is built from the local registry without Cloud API", async () => {
|
||||
const registry = createHwpodSpecRegistry({
|
||||
store: memoryStore(),
|
||||
builtIns: [{ configRef: "config/hwpod.yaml#spec", document: spec("builtin-pod", "/builtin") }],
|
||||
});
|
||||
await registry.create(spec("runtime-pod", "/runtime"));
|
||||
|
||||
const topology = await buildHwpodTopologyFromRegistry(registry, { resource: "device", q: "runtime-pod" });
|
||||
expect(topology).toMatchObject({
|
||||
ok: true,
|
||||
contractVersion: "hwpod-topology-v1",
|
||||
resource: "device",
|
||||
service: { connectedNodeCount: 0, websocketMode: "hwpod-native-api-no-node-registry" },
|
||||
summary: { deviceCount: 2, onlineNodeCount: 0 },
|
||||
items: [{ hwpodId: "runtime-pod", nodeId: "node-test", status: "offline", source: { authority: "runtime" } }],
|
||||
});
|
||||
expect(topology.items[0].blockers).toContainEqual(expect.objectContaining({ code: "hwpod_node_offline" }));
|
||||
|
||||
const temporal = { close: async () => {}, start: async () => ({}), describe: async () => ({}), result: async () => ({}) };
|
||||
const app = createHwpodHttpApp({ runtimeApiUrl: "http://runtime.invalid", runtimeApiAuthorization: "Bearer test", temporal, specRegistry: registry });
|
||||
const response = await request(app, "GET", "/v1/hwpod/topology?resource=node");
|
||||
expect(response).toMatchObject({ status: 200, body: { ok: true, resource: "node", items: [{ nodeId: "node-test", status: "offline" }] } });
|
||||
await app.close();
|
||||
});
|
||||
|
||||
async function request(app: any, method: string, pathname: string, body?: unknown) {
|
||||
const response = await app.fetch(new Request(`http://hwpod.test${pathname}`, { method, headers: { "content-type": "application/json" }, ...(body === undefined ? {} : { body: JSON.stringify(body) }) }));
|
||||
return { status: response.status, body: await response.json() };
|
||||
|
||||
Reference in New Issue
Block a user