Files
fusion/packages/dashboard/app/utils/nodeProjectAssignment.test.ts
gsxdsm 0489f035b5 fix: scope all git, activity, webhook, and summarize-title routes to correct project store
- Fix 30 git routes to use getScopedStore(req) instead of global store
- Fix activity GET/DELETE routes to use scoped store
- Fix POST /api/github/webhooks to use scoped store for badge updates
- Fix POST /api/ai/summarize-title to use scoped store for settings
- Fix GET /api/git/worktrees to use scoped store for task listing
- Add projectId parameter to all git and activity frontend API functions
- Update useActivityLog hook to pass projectId through

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-04-12 17:55:07 -07:00

184 lines
6.9 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
isProjectRoutedToNode,
getProjectsForNode,
getProjectCountForNode,
getUnassignedProjectCount,
} from "./nodeProjectAssignment";
import type { NodeInfo, ProjectInfo } from "../api";
function makeNode(overrides: Partial<NodeInfo> = {}): NodeInfo {
return {
id: "node-1",
name: "Test Node",
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<ProjectInfo> = {}): ProjectInfo {
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",
...overrides,
};
}
describe("nodeProjectAssignment", () => {
describe("isProjectRoutedToNode", () => {
describe("local node", () => {
const localNode = makeNode({ id: "local-1", type: "local" });
it("returns true for projects explicitly assigned to this local node", () => {
const project = makeProject({ id: "proj-1", nodeId: "local-1" });
expect(isProjectRoutedToNode(project, localNode)).toBe(true);
});
it("returns true for unassigned projects (nodeId undefined)", () => {
const project = makeProject({ id: "proj-1", nodeId: undefined });
expect(isProjectRoutedToNode(project, localNode)).toBe(true);
});
it("returns true for unassigned projects (nodeId null)", () => {
const project = makeProject({ id: "proj-1", nodeId: null as unknown as string });
expect(isProjectRoutedToNode(project, localNode)).toBe(true);
});
it("returns false for projects assigned to other nodes", () => {
const project = makeProject({ id: "proj-1", nodeId: "other-node" });
expect(isProjectRoutedToNode(project, localNode)).toBe(false);
});
it("returns false for projects assigned to remote nodes", () => {
const project = makeProject({ id: "proj-1", nodeId: "remote-1" });
expect(isProjectRoutedToNode(project, localNode)).toBe(false);
});
});
describe("remote node", () => {
const remoteNode = makeNode({ id: "remote-1", type: "remote" });
it("returns true for projects explicitly assigned to this remote node", () => {
const project = makeProject({ id: "proj-1", nodeId: "remote-1" });
expect(isProjectRoutedToNode(project, remoteNode)).toBe(true);
});
it("returns false for unassigned projects (nodeId undefined)", () => {
const project = makeProject({ id: "proj-1", nodeId: undefined });
expect(isProjectRoutedToNode(project, remoteNode)).toBe(false);
});
it("returns false for unassigned projects (nodeId null)", () => {
const project = makeProject({ id: "proj-1", nodeId: null as unknown as string });
expect(isProjectRoutedToNode(project, remoteNode)).toBe(false);
});
it("returns false for projects assigned to local nodes", () => {
const project = makeProject({ id: "proj-1", nodeId: "local-1" });
expect(isProjectRoutedToNode(project, remoteNode)).toBe(false);
});
it("returns false for projects assigned to other remote nodes", () => {
const project = makeProject({ id: "proj-1", nodeId: "other-remote" });
expect(isProjectRoutedToNode(project, remoteNode)).toBe(false);
});
});
});
describe("getProjectsForNode", () => {
it("returns all projects routed to a local node (including unassigned)", () => {
const localNode = makeNode({ id: "local-1", type: "local" });
const projects: ProjectInfo[] = [
makeProject({ id: "proj-1", nodeId: "local-1" }), // assigned to this local node
makeProject({ id: "proj-2", nodeId: undefined }), // unassigned
makeProject({ id: "proj-3", nodeId: "other-local" }), // assigned to different local node
makeProject({ id: "proj-4", nodeId: "remote-1" }), // assigned to remote
];
const result = getProjectsForNode(projects, localNode);
expect(result.map((p) => p.id)).toEqual(["proj-1", "proj-2"]);
});
it("returns only explicitly assigned projects for a remote node", () => {
const remoteNode = makeNode({ id: "remote-1", type: "remote" });
const projects: ProjectInfo[] = [
makeProject({ id: "proj-1", nodeId: "remote-1" }), // assigned to this remote node
makeProject({ id: "proj-2", nodeId: undefined }), // unassigned
makeProject({ id: "proj-3", nodeId: "local-1" }), // assigned to local
makeProject({ id: "proj-4", nodeId: "other-remote" }), // assigned to other remote
];
const result = getProjectsForNode(projects, remoteNode);
expect(result.map((p) => p.id)).toEqual(["proj-1"]);
});
});
describe("getProjectCountForNode", () => {
it("returns correct count for local node (includes unassigned)", () => {
const localNode = makeNode({ id: "local-1", type: "local" });
const projects: ProjectInfo[] = [
makeProject({ id: "proj-1", nodeId: "local-1" }),
makeProject({ id: "proj-2", nodeId: undefined }),
makeProject({ id: "proj-3", nodeId: undefined }),
];
expect(getProjectCountForNode(projects, localNode)).toBe(3);
});
it("returns correct count for remote node (explicit only)", () => {
const remoteNode = makeNode({ id: "remote-1", type: "remote" });
const projects: ProjectInfo[] = [
makeProject({ id: "proj-1", nodeId: "remote-1" }),
makeProject({ id: "proj-2", nodeId: "remote-1" }),
makeProject({ id: "proj-3", nodeId: undefined }),
];
expect(getProjectCountForNode(projects, remoteNode)).toBe(2);
});
it("returns 0 when no projects are routed to the node", () => {
const remoteNode = makeNode({ id: "remote-1", type: "remote" });
const projects: ProjectInfo[] = [
makeProject({ id: "proj-1", nodeId: "local-1" }),
makeProject({ id: "proj-2", nodeId: undefined }),
];
expect(getProjectCountForNode(projects, remoteNode)).toBe(0);
});
});
describe("getUnassignedProjectCount", () => {
it("counts projects without nodeId", () => {
const projects: ProjectInfo[] = [
makeProject({ id: "proj-1", nodeId: undefined }),
makeProject({ id: "proj-2", nodeId: null as unknown as string }),
makeProject({ id: "proj-3", nodeId: "local-1" }),
];
expect(getUnassignedProjectCount(projects)).toBe(2);
});
it("returns 0 when all projects are assigned", () => {
const projects: ProjectInfo[] = [
makeProject({ id: "proj-1", nodeId: "local-1" }),
makeProject({ id: "proj-2", nodeId: "remote-1" }),
];
expect(getUnassignedProjectCount(projects)).toBe(0);
});
it("returns 0 for empty array", () => {
expect(getUnassignedProjectCount([])).toBe(0);
});
});
});