From 78147d5087a0b577f0924d4cd6a16dfa9d79d055 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Thu, 8 Oct 2026 10:17:43 +0000 Subject: [PATCH] test: check workspace model changes through the change record and AI selection The agent-switch sync test and three UI model tests read the per-workspace model key. They now check the explicit model change record and getWorkspaceAiSelection, which picks and backend metadata already feed, so the same checks hold before and after the model moves to the resolver. Stack-Layer: A2c1 --- .../WorkspaceModeAISync.test.tsx | 12 ++---------- tests/ui/agents/reawakenAiPins.test.ts | 15 ++++++++------- tests/ui/agents/thinkingPersistence.test.ts | 10 +++------- tests/ui/agents/thinkingSelector.test.ts | 5 ++--- 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/browser/components/WorkspaceModeAISync/WorkspaceModeAISync.test.tsx b/src/browser/components/WorkspaceModeAISync/WorkspaceModeAISync.test.tsx index b235fda4bfd..b7398fc7810 100644 --- a/src/browser/components/WorkspaceModeAISync/WorkspaceModeAISync.test.tsx +++ b/src/browser/components/WorkspaceModeAISync/WorkspaceModeAISync.test.tsx @@ -86,24 +86,16 @@ describe("WorkspaceModeAISync", () => { plan: { modelString: planModel }, }); - // Start with a different model so the mount sync performs an update. - updatePersistedState(getModelKey(workspaceId), "some-legacy-model"); - const { rerender } = renderSync({ workspaceId, agentId: "exec" }); - - // Mount sync should update the model but NOT record an explicit change entry. - await waitFor(() => { - expect(readPersistedState(getModelKey(workspaceId), "")).toBe(execModel); - }); + // Mount sync is not a switch, so it records no explicit change entry. expect(consumeWorkspaceModelChange(workspaceId, execModel)).toBeNull(); // Switching agents (within the same workspace) should be treated as explicit. rerender(); await waitFor(() => { - expect(readPersistedState(getModelKey(workspaceId), "")).toBe(planModel); + expect(consumeWorkspaceModelChange(workspaceId, planModel)).toBe("agent"); }); - expect(consumeWorkspaceModelChange(workspaceId, planModel)).toBe("agent"); }); test("an explicit agent switch that changes the thinking level leaves thinking Auto", async () => { diff --git a/tests/ui/agents/reawakenAiPins.test.ts b/tests/ui/agents/reawakenAiPins.test.ts index 52817ff6f43..0bd3ed581c3 100644 --- a/tests/ui/agents/reawakenAiPins.test.ts +++ b/tests/ui/agents/reawakenAiPins.test.ts @@ -11,7 +11,8 @@ import { waitFor, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { readPersistedState } from "@/browser/hooks/usePersistedState"; -import { getModelKey, getThinkingLevelKey } from "@/common/constants/storage"; +import { getWorkspaceAiSelection } from "@/browser/utils/workspaceAiSettingsSync"; +import { getThinkingLevelKey } from "@/common/constants/storage"; import { KNOWN_MODELS, MODEL_ABBREVIATIONS } from "@/common/constants/knownModels"; import type { Workspace as WorkspaceConfigEntry } from "@/node/config"; import { shouldRunIntegrationTests } from "../../testUtils"; @@ -89,7 +90,7 @@ describeIntegration("Reawakened sub-agent pins from the renderer", () => { workspace.aiSettingsByAgent = { exec: { model: SPAWN_MODEL, thinkingLevel: "high" } }; workspace.taskAiPins = {}; }); - const modelKey = getModelKey(app.workspaceId); + const selection = () => getWorkspaceAiSelection(app.workspaceId); const thinkingKey = getThinkingLevelKey(app.workspaceId); // 1. A plain send records no pins. @@ -97,11 +98,11 @@ describeIntegration("Reawakened sub-agent pins from the renderer", () => { expect(readChild(app).taskAiPins).toEqual({}); // 2. A backend bucket change reseeds the renderer (not a user pick): no pin. - expect(readPersistedState(modelKey, "")).not.toBe(BACKEND_MODEL); + expect(selection().model).not.toBe(BACKEND_MODEL); await editChild(app, (workspace) => { workspace.aiSettingsByAgent = { exec: { model: BACKEND_MODEL, thinkingLevel: "high" } }; }); - await waitFor(() => expect(readPersistedState(modelKey, "")).toBe(BACKEND_MODEL), { + await waitFor(() => expect(selection().model).toBe(BACKEND_MODEL), { timeout: 10_000, }); await sendAndSettle(app, "after backend reseed"); @@ -114,7 +115,7 @@ describeIntegration("Reawakened sub-agent pins from the renderer", () => { const picked = MODEL_ABBREVIATIONS.sonnet; if (!picked) throw new Error("Missing sonnet abbreviation"); await app.chat.send("/model sonnet"); - await waitFor(() => expect(readPersistedState(modelKey, "")).toBe(picked), { + await waitFor(() => expect(selection().model).toBe(picked), { timeout: 10_000, }); await editChild(app, (workspace) => { @@ -123,7 +124,7 @@ describeIntegration("Reawakened sub-agent pins from the renderer", () => { await waitFor(() => expect(readPersistedState(thinkingKey, "")).toBe("low"), { timeout: 10_000, }); - expect(readPersistedState(modelKey, "")).toBe(picked); + expect(selection().model).toBe(picked); await sendAndSettle(app, "send the pick"); expect(readChild(app).taskAiPins).toEqual({ model: picked }); @@ -132,7 +133,7 @@ describeIntegration("Reawakened sub-agent pins from the renderer", () => { await editChild(app, (workspace) => { workspace.aiSettingsByAgent = { exec: { model: BACKEND_MODEL, thinkingLevel: "high" } }; }); - await waitFor(() => expect(readPersistedState(modelKey, "")).toBe(BACKEND_MODEL), { + await waitFor(() => expect(selection().model).toBe(BACKEND_MODEL), { timeout: 10_000, }); await sendAndSettle(app, "plain after the pin"); diff --git a/tests/ui/agents/thinkingPersistence.test.ts b/tests/ui/agents/thinkingPersistence.test.ts index c71d2da4697..c6f4cecbfe4 100644 --- a/tests/ui/agents/thinkingPersistence.test.ts +++ b/tests/ui/agents/thinkingPersistence.test.ts @@ -8,8 +8,7 @@ import userEvent from "@testing-library/user-event"; import { CUSTOM_EVENTS } from "@/common/constants/events"; import { KNOWN_MODELS } from "@/common/constants/knownModels"; -import { getModelKey } from "@/common/constants/storage"; -import { readPersistedState } from "@/browser/hooks/usePersistedState"; +import { getWorkspaceAiSelection } from "@/browser/utils/workspaceAiSettingsSync"; import { formatModelDisplayName } from "@/common/utils/ai/modelDisplay"; import { shouldRunIntegrationTests } from "../../testUtils"; @@ -61,16 +60,13 @@ async function selectModel( fireEvent.click(option); await waitFor(() => { - const persisted = readPersistedState(getModelKey(workspaceId), ""); + const persisted = getWorkspaceAiSelection(workspaceId).model; if (persisted !== model) { throw new Error(`Expected model ${model} but got ${persisted}`); } }); - // Wait for the UI to reflect the new model. This guards against race conditions - // where backend metadata updates can temporarily revert localStorage (and thus - // the displayed model) when switching models rapidly. - // Use the exact display name that the UI will show. + // Wait for the UI to reflect the new model, using the exact display name the UI shows. const expectedDisplayName = modelDisplayName.toLowerCase(); await waitFor( () => { diff --git a/tests/ui/agents/thinkingSelector.test.ts b/tests/ui/agents/thinkingSelector.test.ts index 7e3b2452306..2027dbea396 100644 --- a/tests/ui/agents/thinkingSelector.test.ts +++ b/tests/ui/agents/thinkingSelector.test.ts @@ -9,8 +9,7 @@ import userEvent from "@testing-library/user-event"; import { CUSTOM_EVENTS } from "@/common/constants/events"; import { KNOWN_MODELS } from "@/common/constants/knownModels"; -import { getModelKey } from "@/common/constants/storage"; -import { readPersistedState } from "@/browser/hooks/usePersistedState"; +import { getWorkspaceAiSelection } from "@/browser/utils/workspaceAiSettingsSync"; import { formatModelDisplayName } from "@/common/utils/ai/modelDisplay"; import { shouldRunIntegrationTests } from "../../testUtils"; @@ -56,7 +55,7 @@ async function selectModel( fireEvent.click(option); await waitFor(() => { - const persisted = readPersistedState(getModelKey(workspaceId), ""); + const persisted = getWorkspaceAiSelection(workspaceId).model; if (persisted !== model) { throw new Error(`Expected model ${model} but got ${persisted}`); }