fix(dashboard): make model auto-fetch order independent (#11805)

Obrigado! Validado em lote combinado (8 PRs, release/v3.8.51):

- TDD claro: implementação antiga falhava 1/5 no caso "enabled-first mixed" (postava `/sync-models?mode=sync` incorretamente); implementação corrigida passa 5/5.
- Avalia todas as conexões ativas antes de tratar o auto-fetch como habilitado, tornando o resultado independente da ordem de conexões API/DB.
- `tests/unit/ui/use-provider-models-auto-fetch.test.tsx` — verde via vitest.
- Gates estáticos do lote OK (incluindo confirmação de que o único erro de lint pré-existente no arquivo tocado apenas mudou de linha 130→137 por causa das linhas adicionadas por esta PR — sem regressão real).
This commit is contained in:
Ravi Tharuma
2026-08-28 03:15:18 +02:00
committed by GitHub
parent d937b5229e
commit cfeed516e8
2 changed files with 84 additions and 6 deletions

View File

@@ -77,11 +77,18 @@ export function useProviderModels(providerId: string): UseProviderModelsResult {
}>;
};
if (cancelled) return;
const providerConn = connData.connections?.find(
const providerConnections = connData.connections?.filter(
(c) => (c.provider === providerId || c.id === providerId) && c.isActive !== false
);
const providerConn = providerConnections?.[0];
if (providerConn?.providerSpecificData?.autoFetchModels === true && !cancelled) {
if (
providerConn &&
providerConnections.every(
(connection) => connection.providerSpecificData?.autoFetchModels === true
) &&
!cancelled
) {
const syncRes = await fetch(
`/api/providers/${encodeURIComponent(providerConn.id)}/sync-models?mode=sync`,
{ method: "POST" }

View File

@@ -6,9 +6,8 @@ vi.mock("next-intl", () => ({
useTranslations: () => (key: string) => key,
}));
const { useProviderModels } = await import(
"@/app/(dashboard)/dashboard/providers/hooks/useProviderModels"
);
const { useProviderModels } =
await import("@/app/(dashboard)/dashboard/providers/hooks/useProviderModels");
function createResponse(body: unknown, ok = true): Response {
return {
@@ -17,6 +16,15 @@ function createResponse(body: unknown, ok = true): Response {
} as Response;
}
function connection(id: string, autoFetchModels: boolean, isActive = true) {
return {
id,
provider: "custom-provider",
isActive,
providerSpecificData: { autoFetchModels },
};
}
async function renderProviderModels(providerId = "custom-provider") {
const container = document.createElement("div");
document.body.appendChild(container);
@@ -101,8 +109,71 @@ describe("useProviderModels upstream auto-fetch", () => {
const mounted = await renderProviderModels();
await flushQueuedSync();
expect(fetchMock).toHaveBeenCalledWith("/api/providers/connection-1/sync-models?mode=sync", {
method: "POST",
});
mounted.unmount();
});
it.each([
[
"enabled connection first",
[connection("connection-on", true), connection("connection-off", false)],
],
[
"disabled connection first",
[connection("connection-off", false), connection("connection-on", true)],
],
])("does not synchronize a mixed provider when the %s", async (_name, connections) => {
const fetchMock = vi.fn(async (input: string) => {
if (input.startsWith("/api/v1/providers/")) {
return createResponse({ data: [] });
}
if (input === "/api/providers") {
return createResponse({ connections });
}
throw new Error(`Unexpected request: ${input}`);
});
vi.stubGlobal("fetch", fetchMock);
const mounted = await renderProviderModels();
try {
await flushQueuedSync();
expect(fetchMock).not.toHaveBeenCalledWith(
expect.stringContaining("/sync-models?mode=sync"),
expect.anything()
);
} finally {
mounted.unmount();
}
});
it("ignores inactive opt-outs when every active connection is enabled", async () => {
const fetchMock = vi.fn(async (input: string) => {
if (input.startsWith("/api/v1/providers/")) {
return createResponse({ data: [] });
}
if (input === "/api/providers") {
return createResponse({
connections: [
connection("connection-inactive", false, false),
connection("connection-active", true),
],
});
}
if (input === "/api/providers/connection-active/sync-models?mode=sync") {
return createResponse({});
}
throw new Error(`Unexpected request: ${input}`);
});
vi.stubGlobal("fetch", fetchMock);
const mounted = await renderProviderModels();
await flushQueuedSync();
expect(fetchMock).toHaveBeenCalledWith(
"/api/providers/connection-1/sync-models?mode=sync",
"/api/providers/connection-active/sync-models?mode=sync",
{ method: "POST" }
);
mounted.unmount();