Files
paseo/packages/app/src/create-agent-preferences/preferences.test.ts
T
Mohamed Boudra 9c195feb9b Keep agent profile settings valid across providers (#3331)
* fix(app): keep profile modes scoped to providers

Applying a profile through sequential field setters allowed a provider change
to race mode persistence, poisoning modeless provider preferences with another
provider's mode. Apply profiles as one form transition and heal invalid
provider-scoped mode preferences when profiles are used.

* test(app): cover profile features across providers
2026-08-13 18:48:02 +02:00

280 lines
8.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { CreateAgentPreferencesService } from "./service";
import {
applyAgentProfilePreferences,
mergeCreateAgentSelectionPreferences,
mergeProviderPreferences,
parseFormPreferences,
} from "./preferences";
import { FakeCreateAgentPreferenceStorage } from "./test-utils/fake-preference-storage";
describe("create agent preferences", () => {
it("keeps the selected mode after saving model and thinking", async () => {
const storage = new FakeCreateAgentPreferenceStorage();
const preferences = new CreateAgentPreferencesService(storage);
const modelWrite = preferences.update((current) =>
mergeProviderPreferences({
preferences: current,
provider: "codex",
updates: { model: "gpt-5.5", thinkingByModel: { "gpt-5.5": "high" } },
}),
);
await storage.nextWrite();
const modeWrite = preferences.update((current) =>
mergeProviderPreferences({
preferences: current,
provider: "codex",
updates: { mode: "full-access" },
}),
);
expect(storage.pendingWriteCount()).toBe(1);
storage.finishOldestWrite();
await modelWrite;
await storage.nextWrite();
storage.finishOldestWrite();
await modeWrite;
expect(storage.savedPreferences()).toEqual({
provider: "codex",
providerPreferences: {
codex: {
model: "gpt-5.5",
thinkingByModel: { "gpt-5.5": "high" },
mode: "full-access",
},
},
});
});
it("does not commit a failed write or leak it into the next update", async () => {
const storage = new FakeCreateAgentPreferenceStorage();
const preferences = new CreateAgentPreferencesService(storage);
const failedWrite = preferences.update({ provider: "claude" });
await storage.nextWrite();
storage.failOldestWrite(new Error("disk full"));
await expect(failedWrite).rejects.toThrow("disk full");
expect(await preferences.load()).toEqual({});
const successfulWrite = preferences.update({ isolation: "worktree" });
await storage.nextWrite();
storage.finishOldestWrite();
await successfulWrite;
expect(storage.savedPreferences()).toEqual({ isolation: "worktree" });
});
it("flushes the full create-agent selection into provider preferences", async () => {
const storage = new FakeCreateAgentPreferenceStorage();
const preferences = new CreateAgentPreferencesService(storage);
const saveSelection = preferences.update((current) =>
mergeCreateAgentSelectionPreferences({
preferences: current,
provider: "codex",
modelId: "gpt-5.5",
modeId: "full-access",
thinkingOptionId: "high",
featureValues: { fast_mode: true },
}),
);
await storage.nextWrite();
storage.finishOldestWrite();
await saveSelection;
expect(storage.savedPreferences()).toEqual({
provider: "codex",
providerPreferences: {
codex: {
model: "gpt-5.5",
mode: "full-access",
thinkingByModel: { "gpt-5.5": "high" },
featureValues: { fast_mode: true },
},
},
});
});
it("does not erase a saved mode when a later partial update has no mode", () => {
expect(
mergeProviderPreferences({
preferences: {
provider: "codex",
providerPreferences: {
codex: {
model: "gpt-5.5",
mode: "full-access",
thinkingByModel: { "gpt-5.5": "high" },
},
},
},
provider: "codex",
updates: {
model: "gpt-5.6",
mode: undefined,
thinkingByModel: undefined,
featureValues: undefined,
},
}),
).toEqual({
provider: "codex",
providerPreferences: {
codex: {
model: "gpt-5.6",
mode: "full-access",
thinkingByModel: { "gpt-5.5": "high" },
},
},
});
});
it("erases a saved mode when a complete selection explicitly has no mode", () => {
expect(
mergeCreateAgentSelectionPreferences({
preferences: {
provider: "pi",
providerPreferences: { pi: { model: "anthropic/sonnet", mode: "full-access" } },
},
provider: "pi",
modelId: "anthropic/sonnet",
modeId: null,
}),
).toEqual({
provider: "pi",
providerPreferences: { pi: { model: "anthropic/sonnet" } },
});
});
it("repairs the previous provider while applying a profile", () => {
expect(
applyAgentProfilePreferences({
preferences: {
provider: "pi",
providerPreferences: {
pi: { model: "anthropic/sonnet", mode: "full-access" },
mock: { model: "ten-second-stream", mode: "load-test" },
},
},
previousProvider: "pi",
previousProviderModeIds: [],
provider: "mock",
modelId: "one-minute-stream",
modeId: "approval-test",
thinkingOptionId: "",
featureValues: {},
}),
).toEqual({
provider: "mock",
providerPreferences: {
pi: { model: "anthropic/sonnet" },
mock: { model: "one-minute-stream", mode: "approval-test", featureValues: {} },
},
});
});
it("loads invalid stored preferences as empty preferences", () => {
expect(parseFormPreferences({ providerPreferences: { codex: { mode: 42 } } })).toEqual({});
});
it("strips the explicitly supported legacy location fields", () => {
expect(
parseFormPreferences({
workingDir: "/old/workspace",
provider: "codex",
providerPreferences: {
codex: {
model: "gpt-5.4-mini",
mode: "full-access",
thinkingOptionId: "high",
},
},
serverId: "old-host",
}),
).toEqual({
provider: "codex",
providerPreferences: {
codex: {
model: "gpt-5.4-mini",
mode: "full-access",
thinkingByModel: { "gpt-5.4-mini": "high" },
},
},
});
});
it("rejects unknown persisted fields outside the explicit legacy shape", () => {
expect(parseFormPreferences({ provider: "codex", surprise: true })).toEqual({});
});
it("persists and reloads the workspace isolation choice", async () => {
const storage = new FakeCreateAgentPreferenceStorage();
const preferences = new CreateAgentPreferencesService(storage);
const save = preferences.update({ isolation: "worktree" });
await storage.nextWrite();
storage.finishOldestWrite();
await save;
expect(storage.savedPreferences()).toEqual({ isolation: "worktree" });
expect(await new CreateAgentPreferencesService(storage).load()).toEqual({
isolation: "worktree",
});
});
it("preserves legacy favourites across preference writes until host migration", async () => {
const favoriteModels = [{ provider: "claude", modelId: "opus" }];
const storage = new FakeCreateAgentPreferenceStorage({ stored: { favoriteModels } });
const preferences = new CreateAgentPreferencesService(storage);
const save = preferences.update({ isolation: "worktree" });
await storage.nextWrite();
storage.finishOldestWrite();
await save;
expect(storage.savedPreferences()).toEqual({ favoriteModels, isolation: "worktree" });
});
it("treats stored preferences without an isolation choice as undefined", () => {
expect(parseFormPreferences({ provider: "codex" }).isolation).toBeUndefined();
});
it("rejects an unknown isolation value as invalid stored preferences", () => {
expect(parseFormPreferences({ provider: "codex", isolation: "sandbox" })).toEqual({});
});
it("persists and reloads a terminal launch target", async () => {
const storage = new FakeCreateAgentPreferenceStorage();
const preferences = new CreateAgentPreferencesService(storage);
const save = preferences.update({ launchTarget: { kind: "terminal", profileId: "claude" } });
await storage.nextWrite();
storage.finishOldestWrite();
await save;
expect(storage.savedPreferences()).toEqual({
launchTarget: { kind: "terminal", profileId: "claude" },
});
expect(await new CreateAgentPreferencesService(storage).load()).toEqual({
launchTarget: { kind: "terminal", profileId: "claude" },
});
});
it("treats stored preferences without a launch target as undefined, defaulting to chat", () => {
expect(parseFormPreferences({ provider: "codex" }).launchTarget).toBeUndefined();
});
it("rejects a terminal launch target missing a profileId as invalid stored preferences", () => {
expect(parseFormPreferences({ launchTarget: { kind: "terminal" } })).toEqual({});
});
it("rejects an unknown launch target kind as invalid stored preferences", () => {
expect(parseFormPreferences({ launchTarget: { kind: "shell" } })).toEqual({});
});
});