Files
NODEDC_DEVICE_CORE/apps/device-manager/server/device-manager-server.test.mjs
T

179 lines
6.8 KiB
JavaScript

import assert from "node:assert/strict";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { createLocalPreviewDeviceCore } from "./device-core-client.mjs";
import { createDeviceManagerAuth } from "./device-manager-auth.mjs";
import {
createConfiguredDeviceManagerServer,
createDeviceManagerServer,
} from "./device-manager-server.mjs";
test("production configuration starts with runner-owned file tokens", async (t) => {
const root = await mkdtemp(join(tmpdir(), "nodedc-device-manager-production-"));
const launcherTokenFile = join(root, "launcher-token");
const coreTokenFile = join(root, "core-token");
await writeFile(launcherTokenFile, `${"a".repeat(48)}\n`, { mode: 0o640 });
await writeFile(coreTokenFile, `${"b".repeat(48)}\n`, { mode: 0o640 });
t.after(() => rm(root, { recursive: true, force: true }));
const server = await createConfiguredDeviceManagerServer({
env: {
NODE_ENV: "production",
NODEDC_DEVICE_MANAGER_AUTH_REQUIRED: "true",
NODEDC_DEVICE_MANAGER_COOKIE_SECURE: "true",
NODEDC_DEVICE_MANAGER_LOCAL_PREVIEW: "false",
NODEDC_DEVICE_MANAGER_SERVICE_SLUG: "device-core",
NODEDC_LAUNCHER_BASE_URL: "https://hub.nodedc.ru",
NODEDC_LAUNCHER_INTERNAL_URL: "http://launcher:5173",
NODEDC_LAUNCHER_INTERNAL_TOKEN_FILE: launcherTokenFile,
NODEDC_DEVICE_CORE_INTERNAL_URL: "http://device-control-core:18120",
NODEDC_DEVICE_CORE_TOKEN_FILE: coreTokenFile,
},
});
await new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", resolve);
});
t.after(() => new Promise((resolve) => server.close(resolve)));
const address = server.address();
const response = await fetch(`http://127.0.0.1:${address.port}/healthz`);
assert.equal(response.status, 200);
assert.deepEqual(await response.json(), {
ok: true,
service: "nodedc-device-manager",
authRequired: true,
deviceCoreConfigured: true,
});
const rootResponse = await fetch(`http://127.0.0.1:${address.port}/`, {
redirect: "manual",
headers: { accept: "text/html" },
});
assert.equal(rootResponse.status, 302);
assert.equal(
rootResponse.headers.get("location"),
"https://hub.nodedc.ru/auth/login?returnTo=%2Fapi%2Fservices%2Fdevice-core%2Flaunch%3FreturnTo%3D%252F",
);
const healthAfterRedirect = await fetch(`http://127.0.0.1:${address.port}/healthz`);
assert.equal(healthAfterRedirect.status, 200);
assert.equal((await healthAfterRedirect.json()).ok, true);
});
test("Device Manager BFF exposes an empty, mutation-driven project workspace", async (t) => {
const auth = createDeviceManagerAuth({
env: { NODEDC_DEVICE_MANAGER_AUTH_REQUIRED: "false" },
});
const coreClient = createLocalPreviewDeviceCore();
const server = createDeviceManagerServer({ auth, coreClient });
await new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", resolve);
});
t.after(() => new Promise((resolve) => server.close(resolve)));
const address = server.address();
const baseUrl = `http://127.0.0.1:${address.port}`;
const session = await getJson(`${baseUrl}/api/device-manager/session`);
assert.equal(session.session.actor.hubRole, "owner");
assert.equal(session.session.actor.userRef, "user:local-device-admin");
assert.deepEqual((await getJson(`${baseUrl}/api/device-manager/projects`)).projects, []);
await postJson(`${baseUrl}/api/device-manager/owner-scopes:ensure`, {
scopeKind: "personal",
ownerRef: "user:local-device-admin",
displayName: "Local Device Admin",
}, {
"X-NODEDC-Hub-Role": "viewer",
"X-NODEDC-User-Ref": "user:spoofed-browser",
});
const created = await postJson(`${baseUrl}/api/device-manager/projects:ensure`, {
scopeKind: "personal",
ownerRef: "user:local-device-admin",
projectKey: "device-sandbox",
name: "Device sandbox",
description: "Created only through the canonical command path",
});
const projectRef = created.result.project.projectRef;
const projects = (await getJson(`${baseUrl}/api/device-manager/projects`)).projects;
assert.equal(projects.length, 1);
assert.equal(projects[0].projectKey, "device-sandbox");
assert.equal(projects[0].ownerScope.ownerRef, "user:local-device-admin");
await postJson(`${baseUrl}/api/device-manager/collections:ensure`, {
projectRef,
collectionKey: "pilot-devices",
name: "Pilot devices",
description: null,
});
await postJson(`${baseUrl}/api/device-manager/adapter-packages:ensure`, {
packageKey: "generic-sensor",
displayName: "Generic sensor",
publisherRef: "publisher:nodedc",
lifecycleState: "active",
});
await postJson(`${baseUrl}/api/device-manager/edges:ensure`, {
edgeKey: "preview-edge",
displayName: "Preview Edge",
deploymentRef: "deployment:preview-edge",
lifecycleState: "provisioning",
});
await postJson(`${baseUrl}/api/device-manager/project-grants:upsert`, {
projectRef,
principalKind: "group",
principalRef: "group:preview-viewers",
projectRole: "viewer",
capabilityAllow: [],
capabilityDeny: [],
lifecycleState: "active",
});
const workspace = await getJson(
`${baseUrl}/api/device-manager/projects/${encodeURIComponent(projectRef)}/workspace`,
);
assert.equal(workspace.workspace.project.projectRef, projectRef);
assert.equal(workspace.workspace.collections[0].collectionKey, "pilot-devices");
assert.equal(workspace.workspace.adapterPackages[0].packageKey, "generic-sensor");
assert.equal(workspace.workspace.edges[0].edgeKey, "preview-edge");
assert.equal(workspace.workspace.grants.length, 2);
assert.equal(workspace.workspace.policies.commandTransport, "disabled");
assert.deepEqual(workspace.workspace.devices, []);
const missingKey = await fetch(`${baseUrl}/api/device-manager/projects:ensure`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({}),
});
assert.equal(missingKey.status, 400);
assert.equal((await missingKey.json()).error, "device_idempotency_key_invalid");
});
async function getJson(url) {
const response = await fetch(url, { headers: { accept: "application/json" } });
const body = await response.json();
assert.equal(response.status, 200, JSON.stringify(body));
assert.equal(body.ok, true);
return body;
}
async function postJson(url, body, headers = {}) {
const response = await fetch(url, {
method: "POST",
headers: {
accept: "application/json",
"content-type": "application/json",
"idempotency-key": `device-manager-test-${crypto.randomUUID()}`,
...headers,
},
body: JSON.stringify(body),
});
const payload = await response.json();
assert.equal(response.status, 200, JSON.stringify(payload));
assert.equal(payload.ok, true);
return payload;
}