/* @vitest-environment jsdom */
import { render } from "lit" ;
import { describe, expect, it } from "vitest" ;
import { renderNodes, type NodesProps } from "./nodes.ts" ;
function baseProps(overrides: Partial<NodesProps> = {}): NodesProps {
return {
loading: false ,
nodes: [],
devicesLoading: false ,
devicesError: null ,
devicesList: {
pending: [],
paired: [],
},
configForm: null ,
configLoading: false ,
configSaving: false ,
configDirty: false ,
configFormMode: "form" ,
execApprovalsLoading: false ,
execApprovalsSaving: false ,
execApprovalsDirty: false ,
execApprovalsSnapshot: null ,
execApprovalsForm: null ,
execApprovalsSelectedAgent: null ,
execApprovalsTarget: "gateway" ,
execApprovalsTargetNodeId: null ,
onRefresh: () => undefined,
onDevicesRefresh: () => undefined,
onDeviceApprove: () => undefined,
onDeviceReject: () => undefined,
onDeviceRotate: () => undefined,
onDeviceRevoke: () => undefined,
onLoadConfig: () => undefined,
onLoadExecApprovals: () => undefined,
onBindDefault: () => undefined,
onBindAgent: () => undefined,
onSaveBindings: () => undefined,
onExecApprovalsTargetChange: () => undefined,
onExecApprovalsSelectAgent: () => undefined,
onExecApprovalsPatch: () => undefined,
onExecApprovalsRemove: () => undefined,
onSaveExecApprovals: () => undefined,
...overrides,
};
}
function renderNodesText(overrides: Partial<NodesProps>): string {
const container = document.createElement("div" );
render(renderNodes(baseProps(overrides)), container);
return container.textContent ?? "" ;
}
describe("nodes devices pending rendering" , () => {
it("shows requested and approved access for a scope upgrade" , () => {
const text = renderNodesText({
devicesList: {
pending: [
{
requestId: "req-1" ,
deviceId: "device-1" ,
displayName: "Device One" ,
role: "operator" ,
scopes: ["operator.admin" , "operator.read" ],
ts: Date.now(),
},
],
paired: [
{
deviceId: "device-1" ,
displayName: "Device One" ,
roles: ["operator" ],
scopes: ["operator.read" ],
},
],
},
});
expect(text).toContain("scope upgrade requires approval" );
expect(text).toContain("requested: roles: operator" );
expect(text).toContain("approved now: roles: operator" );
expect(text).toContain("operator.admin, operator.read" );
});
it("normalizes pending device ids before matching paired access" , () => {
const text = renderNodesText({
devicesList: {
pending: [
{
requestId: "req-1" ,
deviceId: " device-1 " ,
displayName: "Device One" ,
role: "operator" ,
scopes: ["operator.admin" , "operator.read" ],
ts: Date.now(),
},
],
paired: [
{
deviceId: "device-1" ,
displayName: "Device One" ,
roles: ["operator" ],
scopes: ["operator.read" ],
},
],
},
});
expect(text).toContain("scope upgrade requires approval" );
expect(text).toContain("approved now: roles: operator" );
});
it("does not show upgrade context for key-mismatched pending requests" , () => {
const text = renderNodesText({
devicesList: {
pending: [
{
requestId: "req-1" ,
deviceId: "device-1" ,
publicKey: "new-key" ,
displayName: "Device One" ,
role: "operator" ,
scopes: ["operator.admin" ],
ts: Date.now(),
},
],
paired: [
{
deviceId: "device-1" ,
publicKey: "old-key" ,
displayName: "Device One" ,
roles: ["operator" ],
scopes: ["operator.read" ],
},
],
},
});
expect(text).toContain("new device pairing request" );
expect(text).not.toContain("scope upgrade requires approval" );
expect(text).not.toContain("approved now:" );
});
it("falls back to roles when role is absent" , () => {
const text = renderNodesText({
devicesList: {
pending: [
{
requestId: "req-2" ,
deviceId: "device-2" ,
roles: ["node" , "operator" ],
scopes: ["operator.read" ],
ts: Date.now(),
},
],
paired: [],
},
});
expect(text).toContain("requested: roles: node, operator" );
expect(text).toContain("scopes: operator.read" );
});
});
Messung V0.5 in Prozent C=99 H=98 G=98
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-07)
¤
*© Formatika GbR, Deutschland