Files
kandev/apps/web/app/settings/agents/[agentId]/agent-save-helpers.test.ts
T
2026-09-25 10:31:58 +01:00

685 lines
24 KiB
TypeScript

import { beforeEach, describe, it, expect, vi } from "vitest";
import {
createAgentAction,
createAgentProfileAction,
updateAgentProfileAction,
updateAgentProfileMcpConfigAction,
} from "@/app/actions/agents";
import { agentProfileId as toAgentProfileId, type AgentProfile } from "@/lib/types/http";
import {
isProfileDirty,
mergeSavedAgentDraft,
saveExistingAgent,
saveNewAgent,
toAgentProfilePatch,
type SaveAgentCallbacks,
type DraftAgent,
type DraftProfile,
} from "./agent-save-helpers";
import type { ProfileFormData } from "@/components/settings/profile-form-fields";
vi.mock("@/app/actions/agents", () => ({
createAgentAction: vi.fn(),
createAgentProfileAction: vi.fn(),
deleteAgentProfileAction: vi.fn(),
updateAgentAction: vi.fn(),
updateAgentProfileAction: vi.fn(),
updateAgentProfileMcpConfigAction: vi.fn(),
}));
const baseProfile: AgentProfile = {
id: toAgentProfileId("p1"),
agentId: "a1",
name: "Profile",
agentDisplayName: "Mock",
model: "mock-fast",
mode: "default",
allowIndexing: false,
autoApprove: false,
cliFlags: [],
cliPassthrough: false,
createdAt: "2026-01-01T00:00:00Z",
updatedAt: "2026-01-01T00:00:00Z",
};
const draftFrom = (saved: AgentProfile, overrides: Partial<DraftProfile> = {}): DraftProfile => ({
...saved,
...overrides,
});
const ALLOW_ALL_TOOLS_FLAG = "--allow-all-tools";
const COMMAND_PREFIX = "greywall --";
const PERSISTED_PROFILE_ID = toAgentProfileId("persisted-profile");
const DRAFT_PROFILE_ID = toAgentProfileId("draft-profile");
const NEW_PROFILE_ID = toAgentProfileId("draft-new-profile");
const NEW_PROFILE_NAME = "New profile";
const PLAYWRIGHT_MCP_SERVERS = '{"mcpServers":{"playwright":{"command":"npx"}}}';
beforeEach(() => {
vi.clearAllMocks();
});
function createTestCallbacks(initialDraft: DraftAgent) {
let currentDraft = initialDraft;
const upsertAgent = vi.fn();
const replaceRoute = vi.fn();
const callbacks: SaveAgentCallbacks = {
onToastError: vi.fn(),
currentAgentModelConfig: {
default_model: "mock-fast",
available_models: [],
supports_dynamic_models: false,
},
permissionSettings: {},
resolveDisplayName: () => "Mock",
upsertAgent,
setDraftAgent: (value) => {
currentDraft = typeof value === "function" ? value(currentDraft) : value;
},
ensureProfiles: (agent) => agent,
cloneAgent: (agent) => ({
...agent,
profiles: agent.profiles.map((profile) => ({ ...profile })),
}),
replaceRoute,
};
return { callbacks, upsertAgent, replaceRoute, getDraft: () => currentDraft };
}
describe("toAgentProfilePatch", () => {
it("maps snake_case form keys to camelCase AgentProfile fields", () => {
const patch: Partial<ProfileFormData> = {
name: "CLI",
model: "claude-sonnet",
mode: "default",
allow_indexing: true,
auto_approve: true,
cli_passthrough: true,
cursor_mcp_auth_enabled: false,
cli_flags: [{ flag: ALLOW_ALL_TOOLS_FLAG, enabled: true, description: "" }],
};
expect(toAgentProfilePatch(patch)).toEqual({
name: "CLI",
model: "claude-sonnet",
mode: "default",
allowIndexing: true,
autoApprove: true,
cliPassthrough: true,
cursorMcpAuthEnabled: false,
cliFlags: [{ flag: ALLOW_ALL_TOOLS_FLAG, enabled: true, description: "" }],
});
});
it("maps fallback_model and auto_fallback to camelCase fields", () => {
expect(toAgentProfilePatch({ fallback_model: "gpt-5", auto_fallback: true })).toEqual({
fallbackModel: "gpt-5",
autoFallback: true,
});
});
it("maps require_exact_model to the camelCase field", () => {
expect(toAgentProfilePatch({ require_exact_model: true })).toEqual({
requireExactModel: true,
});
});
it("omits undefined keys so partial patches do not clobber unrelated fields", () => {
expect(toAgentProfilePatch({ cli_passthrough: false })).toEqual({ cliPassthrough: false });
expect(toAgentProfilePatch({})).toEqual({});
});
it("maps command_prefix to commandPrefix", () => {
expect(toAgentProfilePatch({ command_prefix: COMMAND_PREFIX })).toEqual({
commandPrefix: COMMAND_PREFIX,
});
});
it("maps a cleared command_prefix (empty string) rather than dropping it", () => {
expect(toAgentProfilePatch({ command_prefix: "" })).toEqual({ commandPrefix: "" });
});
});
describe("isProfileDirty", () => {
it("returns false when draft equals saved", () => {
expect(isProfileDirty(draftFrom(baseProfile), baseProfile)).toBe(false);
});
it("returns true when only the Cursor MCP auth preference changes", () => {
expect(
isProfileDirty(draftFrom(baseProfile, { cursorMcpAuthEnabled: false }), baseProfile),
).toBe(true);
});
it("returns true when only mode changes", () => {
const draft = draftFrom(baseProfile, { mode: "plan-mock" });
expect(isProfileDirty(draft, baseProfile)).toBe(true);
});
it("treats undefined mode as equal to empty string", () => {
const saved: AgentProfile = { ...baseProfile, mode: undefined };
const draft = draftFrom(saved, { mode: "" });
expect(isProfileDirty(draft, saved)).toBe(false);
});
it("returns true when mode changes from empty to a value", () => {
const saved: AgentProfile = { ...baseProfile, mode: "" };
const draft = draftFrom(saved, { mode: "plan-mock" });
expect(isProfileDirty(draft, saved)).toBe(true);
});
it("returns true when mode changes from a value to empty (cleared)", () => {
const saved: AgentProfile = { ...baseProfile, mode: "plan-mock" };
const draft = draftFrom(saved, { mode: "" });
expect(isProfileDirty(draft, saved)).toBe(true);
});
it("returns true when fallbackModel or autoFallback changes", () => {
expect(isProfileDirty(draftFrom(baseProfile, { fallbackModel: "gpt-5" }), baseProfile)).toBe(
true,
);
expect(isProfileDirty(draftFrom(baseProfile, { autoFallback: true }), baseProfile)).toBe(true);
const saved: AgentProfile = { ...baseProfile, fallbackModel: "gpt-5", autoFallback: true };
expect(isProfileDirty(draftFrom(saved), saved)).toBe(false);
});
it("returns true when there is no saved profile", () => {
expect(isProfileDirty(draftFrom(baseProfile))).toBe(true);
});
it("returns true when cliFlags list changes", () => {
const draft = draftFrom(baseProfile, {
cliFlags: [{ flag: ALLOW_ALL_TOOLS_FLAG, enabled: true, description: "" }],
});
expect(isProfileDirty(draft, baseProfile)).toBe(true);
});
it("returns true when a cliFlag enabled state changes", () => {
const saved: AgentProfile = {
...baseProfile,
cliFlags: [{ flag: ALLOW_ALL_TOOLS_FLAG, enabled: false, description: "" }],
};
const draft = draftFrom(saved, {
cliFlags: [{ flag: ALLOW_ALL_TOOLS_FLAG, enabled: true, description: "" }],
});
expect(isProfileDirty(draft, saved)).toBe(true);
});
it("returns false when cliFlags are equal", () => {
const flags = [{ flag: ALLOW_ALL_TOOLS_FLAG, enabled: true, description: "desc" }];
const saved: AgentProfile = { ...baseProfile, cliFlags: flags };
const draft = draftFrom(saved, { cliFlags: [...flags] });
expect(isProfileDirty(draft, saved)).toBe(false);
});
it("returns true when autoApprove changes via camelCase draft field", () => {
const draft = draftFrom(baseProfile, { autoApprove: true });
expect(isProfileDirty(draft, baseProfile)).toBe(true);
});
it("returns false when stale snake_case auto_approve disagrees with camelCase", () => {
const draft = draftFrom(baseProfile, { auto_approve: true, autoApprove: false });
expect(isProfileDirty(draft, baseProfile)).toBe(false);
});
it("returns true when a command prefix is set on a profile that had none", () => {
const draft = draftFrom(baseProfile, { commandPrefix: COMMAND_PREFIX });
expect(isProfileDirty(draft, baseProfile)).toBe(true);
});
it("returns true when a previously saved command prefix is cleared", () => {
const saved: AgentProfile = { ...baseProfile, commandPrefix: COMMAND_PREFIX };
const draft = draftFrom(saved, { commandPrefix: "" });
expect(isProfileDirty(draft, saved)).toBe(true);
});
it("treats an undefined command prefix as equal to an empty string", () => {
const saved: AgentProfile = { ...baseProfile, commandPrefix: undefined };
const draft = draftFrom(saved, { commandPrefix: "" });
expect(isProfileDirty(draft, saved)).toBe(false);
});
it("returns false when the command prefix is unchanged", () => {
const saved: AgentProfile = { ...baseProfile, commandPrefix: COMMAND_PREFIX };
const draft = draftFrom(saved, { commandPrefix: COMMAND_PREFIX });
expect(isProfileDirty(draft, saved)).toBe(false);
});
});
describe("mergeSavedAgentDraft", () => {
it("remaps created profile IDs while preserving edits made during save", () => {
const submittedProfile = draftFrom(baseProfile, {
id: DRAFT_PROFILE_ID,
name: "Submitted name",
});
const submitted = agentWithProfiles([submittedProfile]);
const current = {
...submitted,
workspace_id: "newer-workspace",
profiles: [{ ...submittedProfile, name: "Newer name" }],
};
const saved = agentWithProfiles([{ ...submittedProfile, id: PERSISTED_PROFILE_ID }]);
const merged = mergeSavedAgentDraft(
current,
submitted,
saved,
new Map([[submittedProfile.id, PERSISTED_PROFILE_ID]]),
);
expect(merged.workspace_id).toBe("newer-workspace");
expect(merged.profiles[0].id).toBe(PERSISTED_PROFILE_ID);
expect(merged.profiles[0].name).toBe("Newer name");
});
it("keeps existing profiles while remapping a newly created profile", () => {
const existing = draftFrom(baseProfile, { id: toAgentProfileId("existing") });
const submittedProfile = draftFrom(baseProfile, {
id: DRAFT_PROFILE_ID,
name: "Submitted",
});
const persisted = { ...submittedProfile, id: PERSISTED_PROFILE_ID };
const submitted = agentWithProfiles([submittedProfile]);
const current = agentWithProfiles([{ ...submittedProfile, name: "Newer" }]);
const saved = agentWithProfiles([existing, persisted]);
const merged = mergeSavedAgentDraft(
current,
submitted,
saved,
new Map([[submittedProfile.id, persisted.id]]),
);
expect(merged.profiles.map((profile) => profile.id)).toEqual([existing.id, persisted.id]);
expect(merged.profiles[1].name).toBe("Newer");
});
});
describe("saveNewAgent", () => {
it("reconciles a created agent so a failed MCP write retries without another create", async () => {
const draftProfile = draftFrom(baseProfile, {
id: DRAFT_PROFILE_ID,
mcp_config: {
enabled: true,
servers: PLAYWRIGHT_MCP_SERVERS,
dirty: true,
error: null,
},
});
const draftAgent = agentWithProfiles([draftProfile]);
const created = agentWithProfiles([
{ ...draftProfile, id: PERSISTED_PROFILE_ID, mcp_config: undefined },
]);
const { callbacks, upsertAgent, replaceRoute, getDraft } = createTestCallbacks(draftAgent);
const failure = new Error("MCP unavailable");
vi.mocked(createAgentAction).mockResolvedValue(created);
vi.mocked(updateAgentProfileMcpConfigAction)
.mockRejectedValueOnce(failure)
.mockResolvedValueOnce({
profile_id: PERSISTED_PROFILE_ID,
enabled: true,
servers: {},
meta: {},
});
await expect(saveNewAgent(draftAgent, callbacks)).rejects.toBe(failure);
const reconciled = upsertAgent.mock.calls[0][0];
expect(reconciled.profiles[0]).toMatchObject({
id: PERSISTED_PROFILE_ID,
mcp_config: { dirty: true },
});
expect(getDraft().profiles[0]).toMatchObject({
id: PERSISTED_PROFILE_ID,
mcp_config: { dirty: true },
});
expect(replaceRoute).toHaveBeenCalledWith("/settings/agents/mock-agent");
const savedDraft = await saveExistingAgent(getDraft(), reconciled, false, callbacks);
expect(createAgentAction).toHaveBeenCalledOnce();
expect(createAgentProfileAction).not.toHaveBeenCalled();
expect(updateAgentProfileMcpConfigAction).toHaveBeenCalledTimes(2);
expect(savedDraft.profiles[0].mcp_config).toBeUndefined();
});
});
describe("saveExistingAgent", () => {
it("reconciles a created profile so a failed MCP write retries without duplication", async () => {
const newProfile = draftFrom(baseProfile, {
id: NEW_PROFILE_ID,
name: NEW_PROFILE_NAME,
mcp_config: {
enabled: true,
servers: PLAYWRIGHT_MCP_SERVERS,
dirty: true,
error: null,
},
});
const updatedExisting = { ...baseProfile, name: "Updated existing profile" };
const savedAgent = agentWithProfiles([baseProfile]);
const draftAgent = agentWithProfiles([updatedExisting, newProfile]);
const createdProfile = { ...newProfile, id: PERSISTED_PROFILE_ID, mcp_config: undefined };
const { callbacks, upsertAgent, getDraft } = createTestCallbacks(draftAgent);
const failure = new Error("MCP unavailable");
vi.mocked(updateAgentProfileAction).mockResolvedValue(updatedExisting);
vi.mocked(createAgentProfileAction).mockResolvedValue(createdProfile);
vi.mocked(updateAgentProfileMcpConfigAction)
.mockRejectedValueOnce(failure)
.mockResolvedValueOnce({
profile_id: PERSISTED_PROFILE_ID,
enabled: true,
servers: {},
});
await expect(saveExistingAgent(draftAgent, savedAgent, false, callbacks)).rejects.toBe(failure);
const reconciled = upsertAgent.mock.calls[0][0];
expect(reconciled.profiles[0].name).toBe("Updated existing profile");
expect(reconciled.profiles[1]).toMatchObject({
id: PERSISTED_PROFILE_ID,
mcp_config: { dirty: true },
});
expect(getDraft().profiles[0].name).toBe("Updated existing profile");
expect(getDraft().profiles[1]).toMatchObject({
id: PERSISTED_PROFILE_ID,
mcp_config: { dirty: true },
});
const savedDraft = await saveExistingAgent(getDraft(), reconciled, false, callbacks);
expect(createAgentProfileAction).toHaveBeenCalledOnce();
expect(updateAgentProfileAction).toHaveBeenCalledOnce();
expect(updateAgentProfileMcpConfigAction).toHaveBeenCalledTimes(2);
expect(savedDraft.profiles[1].mcp_config).toBeUndefined();
});
});
describe("command prefix save payloads", () => {
it("includes the command prefix when saving a dirty existing profile", async () => {
const savedAgent = agentWithProfiles([baseProfile]);
const draftProfile = draftFrom(baseProfile, { commandPrefix: COMMAND_PREFIX });
const draftAgent = agentWithProfiles([draftProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(updateAgentProfileAction).mockResolvedValue({
...baseProfile,
commandPrefix: COMMAND_PREFIX,
});
await saveExistingAgent(draftAgent, savedAgent, false, callbacks);
expect(updateAgentProfileAction).toHaveBeenCalledWith(
baseProfile.id,
expect.objectContaining({ command_prefix: COMMAND_PREFIX }),
);
});
it("saves an empty command prefix when a previously-saved prefix is cleared", async () => {
const savedProfileWithPrefix: AgentProfile = { ...baseProfile, commandPrefix: COMMAND_PREFIX };
const savedAgent = agentWithProfiles([savedProfileWithPrefix]);
const draftProfile = draftFrom(savedProfileWithPrefix, { commandPrefix: "" });
const draftAgent = agentWithProfiles([draftProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(updateAgentProfileAction).mockResolvedValue({ ...baseProfile, commandPrefix: "" });
await saveExistingAgent(draftAgent, savedAgent, false, callbacks);
expect(updateAgentProfileAction).toHaveBeenCalledWith(
baseProfile.id,
expect.objectContaining({ command_prefix: "" }),
);
});
it("includes the command prefix when creating a new agent's profile", async () => {
const draftProfile = draftFrom(baseProfile, {
id: DRAFT_PROFILE_ID,
commandPrefix: COMMAND_PREFIX,
});
const draftAgent = agentWithProfiles([draftProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(createAgentAction).mockResolvedValue(
agentWithProfiles([{ ...draftProfile, id: PERSISTED_PROFILE_ID }]),
);
await saveNewAgent(draftAgent, callbacks);
expect(createAgentAction).toHaveBeenCalledWith(
expect.objectContaining({
profiles: [expect.objectContaining({ command_prefix: COMMAND_PREFIX })],
}),
);
});
it("includes the command prefix when adding a new profile to an existing agent", async () => {
const savedAgent = agentWithProfiles([baseProfile]);
const newProfile = draftFrom(baseProfile, {
id: NEW_PROFILE_ID,
name: NEW_PROFILE_NAME,
commandPrefix: COMMAND_PREFIX,
});
const draftAgent = agentWithProfiles([baseProfile, newProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(createAgentProfileAction).mockResolvedValue({
...newProfile,
id: PERSISTED_PROFILE_ID,
});
await saveExistingAgent(draftAgent, savedAgent, false, callbacks);
expect(createAgentProfileAction).toHaveBeenCalledWith(
savedAgent.id,
expect.objectContaining({ command_prefix: COMMAND_PREFIX }),
);
});
});
describe("Cursor MCP auth preference save payloads", () => {
it("omits an unchanged preference when saving another existing profile field", async () => {
const savedProfile = { ...baseProfile, cursorMcpAuthEnabled: true };
const savedAgent = agentWithProfiles([savedProfile]);
const draftProfile = draftFrom(savedProfile, { name: "Renamed profile" });
const draftAgent = agentWithProfiles([draftProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(updateAgentProfileAction).mockResolvedValue(draftProfile);
await saveExistingAgent(draftAgent, savedAgent, false, callbacks);
expect(updateAgentProfileAction).toHaveBeenCalledWith(
baseProfile.id,
expect.objectContaining({ cursor_mcp_auth_enabled: undefined }),
);
});
it("preserves false when updating an existing profile", async () => {
const savedProfile = { ...baseProfile, cursorMcpAuthEnabled: true };
const savedAgent = agentWithProfiles([savedProfile]);
const draftProfile = draftFrom(savedProfile, { cursorMcpAuthEnabled: false });
const draftAgent = agentWithProfiles([draftProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(updateAgentProfileAction).mockResolvedValue(draftProfile);
await saveExistingAgent(draftAgent, savedAgent, false, callbacks);
expect(updateAgentProfileAction).toHaveBeenCalledWith(
baseProfile.id,
expect.objectContaining({ cursor_mcp_auth_enabled: false }),
);
});
it("defaults new agent profile payloads to enabled", async () => {
const draftProfile = draftFrom(baseProfile, { id: DRAFT_PROFILE_ID });
const draftAgent = agentWithProfiles([draftProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(createAgentAction).mockResolvedValue(
agentWithProfiles([{ ...draftProfile, id: PERSISTED_PROFILE_ID }]),
);
await saveNewAgent(draftAgent, callbacks);
expect(createAgentAction).toHaveBeenCalledWith(
expect.objectContaining({
profiles: [expect.objectContaining({ cursor_mcp_auth_enabled: true })],
}),
);
});
it("preserves false when creating an additional profile", async () => {
const savedAgent = agentWithProfiles([baseProfile]);
const newProfile = draftFrom(baseProfile, {
id: NEW_PROFILE_ID,
name: NEW_PROFILE_NAME,
cursorMcpAuthEnabled: false,
});
const draftAgent = agentWithProfiles([baseProfile, newProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(createAgentProfileAction).mockResolvedValue({
...newProfile,
id: PERSISTED_PROFILE_ID,
});
await saveExistingAgent(draftAgent, savedAgent, false, callbacks);
expect(createAgentProfileAction).toHaveBeenCalledWith(
savedAgent.id,
expect.objectContaining({ cursor_mcp_auth_enabled: false }),
);
});
});
describe("fallback model save payloads", () => {
it("includes fallback_model and auto_fallback when saving a dirty existing profile", async () => {
const savedAgent = agentWithProfiles([baseProfile]);
const draftProfile = draftFrom(baseProfile, {
fallbackModel: "gpt-5",
autoFallback: true,
});
const draftAgent = agentWithProfiles([draftProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(updateAgentProfileAction).mockResolvedValue({
...baseProfile,
fallbackModel: "gpt-5",
autoFallback: true,
});
await saveExistingAgent(draftAgent, savedAgent, false, callbacks);
expect(updateAgentProfileAction).toHaveBeenCalledWith(
baseProfile.id,
expect.objectContaining({ fallback_model: "gpt-5", auto_fallback: true }),
);
});
it("includes the fallback fields when creating a new agent's profile", async () => {
const draftProfile = draftFrom(baseProfile, {
id: DRAFT_PROFILE_ID,
fallbackModel: "gpt-5",
autoFallback: true,
});
const draftAgent = agentWithProfiles([draftProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(createAgentAction).mockResolvedValue(
agentWithProfiles([{ ...draftProfile, id: PERSISTED_PROFILE_ID }]),
);
await saveNewAgent(draftAgent, callbacks);
expect(createAgentAction).toHaveBeenCalledWith(
expect.objectContaining({
profiles: [expect.objectContaining({ fallback_model: "gpt-5", auto_fallback: true })],
}),
);
});
it("includes the fallback fields when adding a new profile to an existing agent", async () => {
const savedAgent = agentWithProfiles([baseProfile]);
const newProfile = draftFrom(baseProfile, {
id: NEW_PROFILE_ID,
name: NEW_PROFILE_NAME,
fallbackModel: "gpt-5",
autoFallback: true,
});
const draftAgent = agentWithProfiles([baseProfile, newProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(createAgentProfileAction).mockResolvedValue({
...newProfile,
id: PERSISTED_PROFILE_ID,
});
await saveExistingAgent(draftAgent, savedAgent, false, callbacks);
expect(createAgentProfileAction).toHaveBeenCalledWith(
savedAgent.id,
expect.objectContaining({ fallback_model: "gpt-5", auto_fallback: true }),
);
});
});
describe("provider config save payloads", () => {
const OPENAI_COMPATIBLE = "openai_compatible";
const BASE_URL = "http://localhost:20128/v1";
it("returns true when a provider field changes", () => {
const draft = draftFrom(baseProfile, {
providerKind: OPENAI_COMPATIBLE,
providerBaseUrl: BASE_URL,
});
expect(isProfileDirty(draft, baseProfile)).toBe(true);
});
it("includes provider fields when saving a dirty existing profile", async () => {
const savedAgent = agentWithProfiles([baseProfile]);
const draftProfile = draftFrom(baseProfile, {
providerKind: OPENAI_COMPATIBLE,
providerBaseUrl: BASE_URL,
providerApiKeySecretId: "sec_1",
});
const draftAgent = agentWithProfiles([draftProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(updateAgentProfileAction).mockResolvedValue({ ...baseProfile });
await saveExistingAgent(draftAgent, savedAgent, false, callbacks);
expect(updateAgentProfileAction).toHaveBeenCalledWith(
baseProfile.id,
expect.objectContaining({
provider_kind: OPENAI_COMPATIBLE,
provider_base_url: BASE_URL,
provider_api_key_secret_id: "sec_1",
}),
);
});
it("includes provider fields when adding a new profile to an existing agent", async () => {
const savedAgent = agentWithProfiles([baseProfile]);
const newProfile = draftFrom(baseProfile, {
id: NEW_PROFILE_ID,
name: NEW_PROFILE_NAME,
providerKind: OPENAI_COMPATIBLE,
providerBaseUrl: BASE_URL,
});
const draftAgent = agentWithProfiles([baseProfile, newProfile]);
const { callbacks } = createTestCallbacks(draftAgent);
vi.mocked(createAgentProfileAction).mockResolvedValue({
...newProfile,
id: PERSISTED_PROFILE_ID,
});
await saveExistingAgent(draftAgent, savedAgent, false, callbacks);
expect(createAgentProfileAction).toHaveBeenCalledWith(
savedAgent.id,
expect.objectContaining({ provider_kind: OPENAI_COMPATIBLE, provider_base_url: BASE_URL }),
);
});
});
function agentWithProfiles(profiles: DraftProfile[]): DraftAgent {
return {
id: "agent-1",
name: "mock-agent",
supports_mcp: true,
profiles,
created_at: "",
updated_at: "",
};
}