fix(dashboard): split active vs running counts in agents overview label
The Overview dropdown previously rendered "X active · Y running" where both X (stats.activeCount) and Y (activeAgents.length) counted agents whose state was either "active" or "running" — so an agent that was merely enabled but idle would still inflate the "running" tally. The label now counts each state distinctly so "running" only reflects agents that are mid-heartbeat. Adds AgentsOverviewBar.test.tsx to lock in the new contract. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
10
.changeset/agents-overview-running-count.md
Normal file
10
.changeset/agents-overview-running-count.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix the misleading "X active · Y running" label in the Agents overview
|
||||||
|
dropdown. Both numbers previously counted agents whose state was either
|
||||||
|
`active` or `running`, so the "running" tally over-reported by including
|
||||||
|
idle-but-enabled agents. The label now counts each state distinctly:
|
||||||
|
"active" reflects only `state === "active"` and "running" reflects only
|
||||||
|
`state === "running"`.
|
||||||
@@ -36,7 +36,7 @@ export function AgentsOverviewBar({
|
|||||||
<span className="agents-overview-bar__title">Overview</span>
|
<span className="agents-overview-bar__title">Overview</span>
|
||||||
</span>
|
</span>
|
||||||
<span className="agents-overview-bar__meta text-secondary">
|
<span className="agents-overview-bar__meta text-secondary">
|
||||||
{stats?.activeCount ?? 0} active · {activeAgents.length} running
|
{activeAgents.filter((a) => a.state === "active").length} active · {activeAgents.filter((a) => a.state === "running").length} running
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
{isOpen ? (
|
{isOpen ? (
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import { describe, it, expect, vi } from "vitest";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { AgentsOverviewBar } from "../AgentsOverviewBar";
|
||||||
|
import type { Agent } from "../../api";
|
||||||
|
|
||||||
|
vi.mock("lucide-react", async () => {
|
||||||
|
const actual = await vi.importActual("lucide-react");
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
ChevronDown: () => <span data-testid="chevron-down" />,
|
||||||
|
ChevronRight: () => <span data-testid="chevron-right" />,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mock("../AgentMetricsBar", () => ({
|
||||||
|
AgentMetricsBar: () => <div data-testid="agent-metrics-bar" />,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("../ActiveAgentsPanel", () => ({
|
||||||
|
ActiveAgentsPanel: () => <div data-testid="active-agents-panel" />,
|
||||||
|
}));
|
||||||
|
|
||||||
|
function makeAgent(id: string, state: Agent["state"]): Agent {
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: id,
|
||||||
|
role: "executor",
|
||||||
|
state,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
metadata: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("AgentsOverviewBar", () => {
|
||||||
|
it("counts active and running states separately in the meta label", () => {
|
||||||
|
// Three in 'active' (idle/enabled) and one in 'running' (mid-heartbeat).
|
||||||
|
// The label must reflect the distinction — previously both buckets were
|
||||||
|
// displayed under "running", which was misleading when only some were
|
||||||
|
// actually executing.
|
||||||
|
const agents = [
|
||||||
|
makeAgent("a-1", "active"),
|
||||||
|
makeAgent("a-2", "active"),
|
||||||
|
makeAgent("a-3", "active"),
|
||||||
|
makeAgent("a-4", "running"),
|
||||||
|
];
|
||||||
|
|
||||||
|
render(
|
||||||
|
<AgentsOverviewBar
|
||||||
|
stats={null}
|
||||||
|
activeAgents={agents}
|
||||||
|
isOpen={false}
|
||||||
|
onToggle={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText("3 active · 1 running")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows zero counts when no agents are active or running", () => {
|
||||||
|
render(
|
||||||
|
<AgentsOverviewBar
|
||||||
|
stats={null}
|
||||||
|
activeAgents={[]}
|
||||||
|
isOpen={false}
|
||||||
|
onToggle={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText("0 active · 0 running")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders metrics bar and active agents panel when open", () => {
|
||||||
|
render(
|
||||||
|
<AgentsOverviewBar
|
||||||
|
stats={null}
|
||||||
|
activeAgents={[]}
|
||||||
|
isOpen
|
||||||
|
onToggle={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByTestId("agent-metrics-bar")).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId("active-agents-panel")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hides content when collapsed", () => {
|
||||||
|
render(
|
||||||
|
<AgentsOverviewBar
|
||||||
|
stats={null}
|
||||||
|
activeAgents={[]}
|
||||||
|
isOpen={false}
|
||||||
|
onToggle={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByTestId("agent-metrics-bar")).toBeNull();
|
||||||
|
expect(screen.queryByTestId("active-agents-panel")).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user