feat(FN-3507): block dispatch when project lacks node mapping

Adds validation to block task dispatch when no project-node mapping exists (FN-3507), including a read helper in CentralCore and a new `node-dispatch-validation` module integrated into the scheduler and in-process runtime, with test coverage across routing and validation scenarios.

Fusion-Task-Id: FN-3507
This commit is contained in:
Fusion
2026-05-07 21:23:26 -07:00
committed by gsxdsm
parent 695cb0bca5
commit 11bf7084e1
9 changed files with 261 additions and 1 deletions

View File

@@ -804,6 +804,29 @@ describe("CentralCore", () => {
await expect(central.getProjectNodePathMapping(project.id, nodeA.id)).resolves.toBeUndefined();
});
it("should return exact mapped path via getProjectNodePath and undefined for unmapped pairs", async () => {
const projectPath = join(tempDir, "mapping-read-project");
mkdirSync(projectPath);
projectPaths.push(projectPath);
const project = await central.registerProject({
name: "Mapping Read Project",
path: projectPath,
});
const mappedNode = await central.registerNode({ name: "mapping-read-node", type: "local" });
const otherNode = await central.registerNode({ name: "mapping-read-node-other", type: "local" });
await central.upsertProjectNodePathMapping({
projectId: project.id,
nodeId: mappedNode.id,
path: "/mapped/node/path",
});
await expect(central.getProjectNodePath(project.id, mappedNode.id)).resolves.toBe("/mapped/node/path");
await expect(central.getProjectNodePath(project.id, otherNode.id)).resolves.toBeUndefined();
await expect(central.getProjectNodePath("proj_missing", mappedNode.id)).resolves.toBeUndefined();
});
it("should validate project and node existence for mapping APIs", async () => {
const node = await central.registerNode({ name: "mapping-validation-node", type: "local" });

View File

@@ -1769,6 +1769,16 @@ export class CentralCore extends EventEmitter<CentralCoreEvents> {
return row ? this.rowToProjectNodePathMapping(row) : undefined;
}
async getProjectNodePath(projectId: string, nodeId: string): Promise<string | undefined> {
this.ensureInitialized();
const row = this.db!
.prepare("SELECT path FROM projectNodePathMappings WHERE projectId = ? AND nodeId = ?")
.get(projectId, nodeId) as { path: string } | undefined;
return row?.path;
}
async listProjectNodePathMappings(filters?: {
projectId?: string;
nodeId?: string;