feat(FN-2953): merge fusion/fn-2953

Commits merged:
- merge fusion/fn-2953

Fusion-Task-Id: FN-2953
This commit is contained in:
Fusion
2026-04-29 12:10:01 -07:00
committed by gsxdsm
parent 822bde7829
commit 8dd4999f11
2 changed files with 76 additions and 3 deletions

View File

@@ -2211,6 +2211,21 @@ describe("ListView - Bulk Selection", () => {
expect(await screen.findByRole("option", { name: "○ Node Two (Offline)" })).toBeInTheDocument();
});
it("shows NodeHealthDot for selected bulk node override", async () => {
const user = userEvent.setup();
const tasks = [createMockTask({ id: "FN-001" })];
vi.mocked(fetchNodes).mockResolvedValue([{ id: "node-abc", name: "Node ABC", status: "online" } as never]);
render(<ListView tasks={tasks} onMoveTask={vi.fn()} onOpenDetail={vi.fn()} addToast={mockAddToast} projectId={TEST_PROJECT_ID} availableModels={availableModels} />);
await user.click(screen.getByLabelText("Select FN-001"));
const nodeSelect = await screen.findByLabelText("Node Override");
await user.selectOptions(nodeSelect, "node-abc");
expect(document.querySelector(".status-dot--online")).toBeInTheDocument();
expect(screen.getByText("Online")).toBeInTheDocument();
});
it("applies explicit node override through batchUpdateTaskModels", async () => {
const user = userEvent.setup();
const tasks = [createMockTask({ id: "FN-001" })];

View File

@@ -125,6 +125,9 @@ describe("RoutingTab", () => {
);
expect(await screen.findByText("Per-task override")).toBeInTheDocument();
expect(screen.getByText(/Effective node/i)).toBeInTheDocument();
expect(screen.getAllByText("Alpha (local)")[0]).toBeInTheDocument();
expect(document.querySelector(".status-dot--online")).toBeInTheDocument();
});
it("renders routing source as project default", async () => {
@@ -166,15 +169,70 @@ describe("RoutingTab", () => {
expect(screen.getByRole("option", { name: "Delta (remote) — error" })).toBeInTheDocument();
});
it("disables node selector for in-progress tasks and shows blocking message", async () => {
render(<RoutingTab task={makeTask({ column: "in-progress", nodeId: "node-a" })} settings={makeSettings()} addToast={addToast} />);
it("renders routing summary with project default", async () => {
render(
<RoutingTab
task={makeTask()}
settings={makeSettings({ defaultNodeId: "node-b" })}
addToast={addToast}
/>,
);
expect(await screen.findByText("Project default")).toBeInTheDocument();
expect(screen.getByText(/Effective node/i)).toBeInTheDocument();
expect(screen.getByText("Unhealthy")).toBeInTheDocument();
});
it("renders NodeHealthDot in routing summary for effective node", async () => {
render(
<RoutingTab
task={makeTask({ nodeId: "node-a" })}
settings={makeSettings()}
addToast={addToast}
/>,
);
await screen.findByText("Alpha (local)");
expect(document.querySelector(".status-dot--online")).toBeInTheDocument();
});
it("renders NodeHealthDot and Unhealthy badge for offline effective node", async () => {
render(
<RoutingTab
task={makeTask()}
settings={makeSettings({ defaultNodeId: "node-b" })}
addToast={addToast}
/>,
);
await screen.findByText("Unhealthy");
expect(document.querySelector(".status-dot--offline")).toBeInTheDocument();
});
it("renders no-routing summary when no override or project default exists", async () => {
render(<RoutingTab task={makeTask()} settings={makeSettings()} addToast={addToast} />);
expect(await screen.findByText("Local (no routing configured)")).toBeInTheDocument();
expect(screen.getByText("No routing")).toBeInTheDocument();
});
it("disables node selector for in-progress tasks", async () => {
render(<RoutingTab task={makeTask({ column: "in-progress" })} settings={makeSettings()} addToast={addToast} />);
const selector = await screen.findByLabelText("Select execution node");
expect(selector).toBeDisabled();
expect(screen.getByText("Node override cannot be changed while the task is active.")).toBeInTheDocument();
});
it("enables node selector for non-in-progress tasks", async () => {
it("disables node selector for active task statuses", async () => {
render(<RoutingTab task={makeTask({ column: "todo", status: "executing" })} settings={makeSettings()} addToast={addToast} />);
const selector = await screen.findByLabelText("Select execution node");
expect(selector).toBeDisabled();
expect(screen.getByText("Node override cannot be changed while the task is active.")).toBeInTheDocument();
});
it("enables node selector for non-active tasks", async () => {
render(<RoutingTab task={makeTask({ column: "todo", status: "pending" })} settings={makeSettings()} addToast={addToast} />);
const selector = await screen.findByLabelText("Select execution node");