Files
OmniRoute/tests/unit/ui/modality-bridge-video-tab.test.tsx
Markus Hartung 04dba0460e fix(responses-continuation): recover a real id/output for passthrough and translate-mode replies (#11434)
Retargetado para release/v3.8.51 (release/v3.8.50 está congelada — freeze issue #11439). Validado em lote combinado (batch-0824h2, junto de #11435/#11436/#11437) contra o tip de release/v3.8.51: typecheck:core limpo, gates estáticos OK, 127/127 testes focados passando.

Investigação sólida com repro real via container isolado, três causas independentes identificadas e corrigidas com testes de regressão dedicados para cada uma. Obrigado pela contribuição!
2026-08-24 19:57:12 -03:00

287 lines
11 KiB
TypeScript

// @vitest-environment jsdom
import React, { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import ModalityBridgeVideoTab from "@/app/(dashboard)/dashboard/settings/components/modalityBridge/ModalityBridgeVideoTab";
vi.mock("next-intl", () => ({
useTranslations: (namespace?: string) => (key: string) =>
namespace === "settings" && key === "degradationFull"
? "MISSING:settings.degradationFull"
: key,
}));
const roots: Array<{ root: Root; element: HTMLDivElement }> = [];
async function waitFor(predicate: () => boolean, label: string): Promise<void> {
const startedAt = Date.now();
while (!predicate()) {
if (Date.now() - startedAt > 2_000) throw new Error(`Timed out waiting for ${label}`);
await act(async () => new Promise((resolve) => setTimeout(resolve, 10)));
}
}
describe("ModalityBridgeVideoTab", () => {
let fetchMock: ReturnType<typeof vi.fn>;
let failPatch = false;
let failSettingsLoad = false;
beforeEach(() => {
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
failPatch = false;
failSettingsLoad = false;
fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
const url = String(input);
if (url.includes("/api/modality-bridge/video/runtime")) {
return Response.json({
available: true,
ffmpegVersion: "6.1.1",
ffprobeVersion: "6.1.1",
});
}
if (url.includes("/api/modality-bridge/stats")) {
return Response.json({
video: {
attempts: 4,
successes: 3,
bridged: 3,
cacheHits: 1,
failures: 1,
totalLatencyMs: 400,
averageLatencyMs: 100,
lastUsedAt: null,
},
});
}
if (url.includes("/api/models")) {
return Response.json({
models: [
{ provider: "openai", model: "gpt-4o-mini", supportsVision: true },
{ provider: "example", model: "text-only", supportsVision: false },
],
});
}
if (url.includes("/api/settings")) {
if (init?.method === "PATCH") {
return failPatch
? Response.json({ error: "sanitized" }, { status: 500 })
: Response.json({});
}
if (failSettingsLoad) return Response.json({ error: "sanitized" }, { status: 500 });
return Response.json({
modalityBridgeVideoEnabled: false,
modalityBridgeVideoModel: "openai/gpt-4o-mini",
modalityBridgeVideoFrameCount: 8,
modalityBridgeVideoMaxVideos: 1,
modalityBridgeVideoTimeout: 120_000,
});
}
return Response.json({});
});
vi.stubGlobal("fetch", fetchMock);
});
afterEach(() => {
for (const { root, element } of roots.splice(0)) {
act(() => root.unmount());
element.remove();
}
vi.unstubAllGlobals();
});
async function render(props: { runtimeHostname?: string } = {}): Promise<HTMLDivElement> {
const element = document.createElement("div");
document.body.appendChild(element);
const root = createRoot(element);
await act(async () => root.render(<ModalityBridgeVideoTab {...props} />));
roots.push({ root, element });
await waitFor(
() => element.querySelector('[data-testid="modality-bridge-video-frame-count"]') !== null,
"Video Bridge settings"
);
return element;
}
async function renderWithoutWaiting(): Promise<HTMLDivElement> {
const element = document.createElement("div");
document.body.appendChild(element);
const root = createRoot(element);
await act(async () => root.render(<ModalityBridgeVideoTab />));
roots.push({ root, element });
return element;
}
it("shows ready runtime versions, video stats, and only vision-capable models", async () => {
const element = await render();
await waitFor(() => element.textContent?.includes("6.1.1") ?? false, "runtime status");
await waitFor(
() => Array.from(element.querySelectorAll("option")).some((option) => option.value),
"model options"
);
const options = Array.from(element.querySelectorAll("option")).map((option) => option.value);
expect(options).toContain("openai/gpt-4o-mini");
expect(options).not.toContain("example/text-only");
expect(element.textContent).toContain("4 requestlogger.attempts");
expect(element.textContent).toContain("3 modalityBridgeStatsBridged");
expect(element.textContent).toContain("1 modalityBridgeStatsFailures");
expect(element.textContent).toContain("trafficInspector.timingTotalLatency: 400 ms");
expect(element.textContent).toContain("avgLatency: 100 ms");
expect(element.textContent).not.toContain("modalityBridgeVideoComingSoon");
});
it("labels runtime status as strict loopback without probing it from a LAN dashboard", async () => {
const element = await render({ runtimeHostname: "192.168.0.15" });
expect(element.textContent).toContain("authz.badge.strict");
expect(element.textContent).toContain("endpoint.badgeLoopbackTooltip");
expect(element.textContent).not.toContain("modalityBridgeVideoRuntimeUnavailable");
expect(element.textContent).not.toContain("modalityBridgeVideoRuntimeInstall");
expect(
fetchMock.mock.calls.some(([input]) =>
String(input).includes("/api/modality-bridge/video/runtime")
)
).toBe(false);
});
it("persists the enable toggle and clamps frame count to 16", async () => {
const element = await render();
const toggle = element.querySelector('[role="switch"]') as HTMLButtonElement;
await act(async () => toggle.click());
const frameCount = element.querySelector(
'[data-testid="modality-bridge-video-frame-count"]'
) as HTMLInputElement;
act(() => {
const setter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"value"
)?.set;
setter?.call(frameCount, "99");
frameCount.dispatchEvent(new Event("input", { bubbles: true }));
});
await act(async () => {
frameCount.dispatchEvent(new FocusEvent("focusout", { bubbles: true }));
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(
() =>
fetchMock.mock.calls.some(([, init]) => {
if (init?.method !== "PATCH") return false;
const body = JSON.parse(String(init.body)) as Record<string, unknown>;
return body.modalityBridgeVideoFrameCount === 16;
}),
"clamped frame count PATCH"
);
const patches = fetchMock.mock.calls
.filter(([, init]) => init?.method === "PATCH")
.map(([, init]) => JSON.parse(String(init?.body)) as Record<string, unknown>);
expect(patches).toContainEqual({ modalityBridgeVideoEnabled: true });
});
it("defaults to full analysis and persists an explicit focused-mode opt-in", async () => {
const element = await render();
const analysisMode = element.querySelector(
'[data-testid="modality-bridge-video-analysis-mode"]'
) as HTMLSelectElement | null;
expect(analysisMode).not.toBeNull();
expect(analysisMode?.value).toBe("full");
expect(Array.from(analysisMode?.options ?? []).map((option) => option.value)).toEqual([
"full",
"focused",
]);
expect(Array.from(analysisMode?.options ?? []).map((option) => option.textContent)).toEqual([
"health.degradationFull",
"modalityBridgeTaskAware",
]);
const description = element.querySelector("#modality-bridge-video-analysis-mode-description");
expect(description?.textContent).toBe("modalityBridgeVideoDesc");
await act(async () => {
if (!analysisMode) return;
const setter = Object.getOwnPropertyDescriptor(
window.HTMLSelectElement.prototype,
"value"
)?.set;
setter?.call(analysisMode, "focused");
analysisMode.dispatchEvent(new Event("change", { bubbles: true }));
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(
() =>
fetchMock.mock.calls.some(([, init]) => {
if (init?.method !== "PATCH") return false;
const body = JSON.parse(String(init.body)) as Record<string, unknown>;
return body.modalityBridgeVideoAnalysisMode === "focused";
}),
"focused analysis-mode PATCH"
);
expect(description?.textContent).toBe("modalityBridgeTaskAwareDesc");
const modePatches = fetchMock.mock.calls
.filter(([, init]) => init?.method === "PATCH")
.map(([, init]) => JSON.parse(String(init?.body)) as Record<string, unknown>)
.filter((body) => body.modalityBridgeVideoAnalysisMode !== undefined);
expect(modePatches).toEqual([{ modalityBridgeVideoAnalysisMode: "focused" }]);
});
it("caps the configurable timeout at the broker's 120 second hard deadline", async () => {
const element = await render();
const timeout = element.querySelector(
'[data-testid="modality-bridge-video-timeout"]'
) as HTMLInputElement;
expect(timeout.max).toBe("120000");
act(() => {
const setter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"value"
)?.set;
setter?.call(timeout, "180000");
timeout.dispatchEvent(new Event("input", { bubbles: true }));
});
await act(async () => {
timeout.dispatchEvent(new FocusEvent("focusout", { bubbles: true }));
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(
() =>
fetchMock.mock.calls.some(([, init]) => {
if (init?.method !== "PATCH") return false;
const body = JSON.parse(String(init.body)) as Record<string, unknown>;
return body.modalityBridgeVideoTimeout === 120_000;
}),
"clamped timeout PATCH"
);
});
it("shows a load error instead of silently applying defaults", async () => {
failSettingsLoad = true;
const element = await renderWithoutWaiting();
await waitFor(() => element.querySelector('[role="alert"]') !== null, "load error");
expect(element.textContent).toContain("publicSystem.error.title");
expect(element.querySelector('[data-testid="modality-bridge-video-frame-count"]')).toBeNull();
});
it("shows a save error and rolls an optimistic numeric edit back after failed PATCH", async () => {
const element = await render();
failPatch = true;
const frameCount = element.querySelector(
'[data-testid="modality-bridge-video-frame-count"]'
) as HTMLInputElement;
act(() => {
const setter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"value"
)?.set;
setter?.call(frameCount, "12");
frameCount.dispatchEvent(new Event("input", { bubbles: true }));
});
await act(async () => {
frameCount.dispatchEvent(new FocusEvent("focusout", { bubbles: true }));
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => element.querySelector('[role="alert"]') !== null, "save error");
expect(frameCount.value).toBe("8");
});
});