fix(dashboard): guard setMetricsById against same-value calls to end loop

Even with primitive useEffect deps, TanStack Query or React concurrent
rendering can cause onMetrics to fire with identical values mid-render-cycle.
Add a ref-based equality check in Dashboard.onMetrics: if all five metric
values are unchanged, skip setMetricsById entirely — no state update, no
Dashboard re-render, loop terminates.

Also fixes vi.fn<TFunction>() typing in server-workspace-rows.test.tsx to
satisfy tsc (Vitest 4 single-type-arg signature).
This commit is contained in:
Offending Commit
2026-06-03 17:47:59 -05:00
parent 9cc8637dc7
commit 3b88a41afd
2 changed files with 18 additions and 5 deletions

View File

@@ -2,6 +2,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { act, render, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { ServerWorkspaceRows } from "@/components/dashboard/ServerWorkspaceRows";
import type { FleetRowMetrics } from "@/components/fleet/fleetAggregates";
import { DemoProvider } from "@/context/DemoContext";
import type { Instance } from "@/lib/config";
@@ -29,7 +30,7 @@ function makeQc() {
return new QueryClient({ defaultOptions: { queries: { retry: false, staleTime: Infinity } } });
}
function renderRows(instance: Instance, onMetrics: ReturnType<typeof vi.fn>) {
function renderRows(instance: Instance, onMetrics: (id: string, metrics: FleetRowMetrics) => void) {
const qc = makeQc();
return render(
<QueryClientProvider client={qc}>
@@ -52,7 +53,7 @@ describe("ServerWorkspaceRows — onMetrics stability", () => {
afterEach(() => localStorage.clear());
it("calls onMetrics with health:ok after data loads", async () => {
const onMetrics = vi.fn();
const onMetrics = vi.fn<(id: string, m: FleetRowMetrics) => void>();
renderRows(neo, onMetrics);
await waitFor(() =>
expect(onMetrics).toHaveBeenCalledWith(
@@ -63,7 +64,7 @@ describe("ServerWorkspaceRows — onMetrics stability", () => {
});
it("does not call onMetrics again when values have not changed", async () => {
const onMetrics = vi.fn();
const onMetrics = vi.fn<(id: string, m: FleetRowMetrics) => void>();
renderRows(neo, onMetrics);
// Wait until we have at least one call with stable state
@@ -83,7 +84,7 @@ describe("ServerWorkspaceRows — onMetrics stability", () => {
});
it("calls onMetrics when health transitions from loading to ok", async () => {
const onMetrics = vi.fn();
const onMetrics = vi.fn<(id: string, m: FleetRowMetrics) => void>();
renderRows(neo, onMetrics);
// Must eventually report ok (not just loading)