Files
fusion/packages/dashboard/app/utils/__tests__/nodeProjectAssignment.test.ts
Fusion eed16abacb feat(FN-3509): document node mapping availability UI in dashboard guide
Documents the node mapping availability UI in the dashboard guide.

Fusion-Task-Id: FN-3509
2026-05-08 18:44:16 -07:00

100 lines
3.8 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
getAvailableNodeMappingsForNode,
getNodeMappingsForProject,
getProjectCountForNode,
getProjectsForNode,
getUnassignedProjectCount,
isProjectAvailableOnNode,
resolveNodeDisplayName,
} from "../nodeProjectAssignment";
import type { NodeInfo, ProjectInfoWithSource } from "../../api";
function makeNode(overrides: Partial<NodeInfo> = {}): NodeInfo {
return {
id: "node-1",
name: "Node One",
type: "local",
status: "online",
maxConcurrent: 2,
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
...overrides,
};
}
function makeProject(overrides: Partial<ProjectInfoWithSource> = {}): ProjectInfoWithSource {
return {
id: "proj-1",
name: "Project One",
path: "/workspace/project-one",
status: "active",
isolationMode: "in-process",
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
nodeMappings: [],
...overrides,
};
}
describe("nodeProjectAssignment", () => {
it("normalizes mapping entries and defaults available=true", () => {
const project = makeProject({
nodeMappings: [
{ nodeId: "node-1", path: "/mnt/one", available: true },
{ nodeId: "node-2", path: "/mnt/two", available: false },
{ nodeId: "", path: "/bad", available: true },
],
});
expect(getNodeMappingsForProject(project)).toEqual([
{ nodeId: "node-1", path: "/mnt/one", available: true, nodeName: undefined },
{ nodeId: "node-2", path: "/mnt/two", available: false, nodeName: undefined },
]);
});
it("filters available mappings for a given node", () => {
const project = makeProject({
nodeMappings: [
{ nodeId: "node-1", path: "/mnt/live", available: true },
{ nodeId: "node-1", path: "/mnt/down", available: false },
],
});
const node = makeNode({ id: "node-1" });
expect(getAvailableNodeMappingsForNode(project, node)).toEqual([
{ nodeId: "node-1", path: "/mnt/live", available: true, nodeName: undefined },
]);
expect(isProjectAvailableOnNode(project, node)).toBe(true);
});
it("builds project lists and counts from available mappings only", () => {
const node = makeNode({ id: "node-1" });
const projects = [
makeProject({ id: "proj-a", nodeMappings: [{ nodeId: "node-1", path: "/a", available: true }] }),
makeProject({ id: "proj-b", nodeMappings: [{ nodeId: "node-1", path: "/b", available: false }] }),
makeProject({ id: "proj-c", nodeMappings: [{ nodeId: "node-2", path: "/c", available: true }] }),
];
expect(getProjectsForNode(projects, node).map((project) => project.id)).toEqual(["proj-a"]);
expect(getProjectCountForNode(projects, node)).toBe(1);
});
it("resolves display names in canonical order", () => {
const nodes = [makeNode({ id: "node-1", name: "Primary Node" })];
const project = makeProject({ _sourceNodeName: "Source Node" });
expect(resolveNodeDisplayName("node-1", { nodeId: "node-1", path: "/a", available: true, nodeName: "Mapping Name" }, nodes, project)).toBe("Primary Node");
expect(resolveNodeDisplayName("node-2", { nodeId: "node-2", path: "/b", available: true, nodeName: "Mapping Name" }, nodes, project)).toBe("Mapping Name");
expect(resolveNodeDisplayName("node-3", undefined, nodes, project)).toBe("Source Node");
expect(resolveNodeDisplayName("node-4", undefined, nodes, makeProject({ _sourceNodeName: undefined }))).toBe("node-4");
});
it("counts unassigned projects as projects without mappings", () => {
expect(getUnassignedProjectCount([
makeProject({ id: "proj-a", nodeMappings: [] }),
makeProject({ id: "proj-b", nodeMappings: [{ nodeId: "node-1", path: "/b", available: true }] }),
])).toBe(1);
});
});