Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export function createInitialState(
}
): ProviderFormState {
const isEdit = mode === "edit";
const sourceProvider = isEdit ? provider : cloneProvider;
const raw = isEdit ? provider : cloneProvider;
const sourceProvider = raw ? structuredClone(raw) : undefined;

return {
basic: {
Expand Down Expand Up @@ -322,11 +323,13 @@ export function providerFormReducer(
return { ...state, ui: { ...state.ui, showFailureThresholdConfirm: action.payload } };

// Reset
case "RESET_FORM":
case "RESET_FORM": {
const fresh = structuredClone(defaultInitialState);
return {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] [TEST-MISSING-CRITICAL] RESET_FORM deep-copy change is untested

Why this is a problem: This PR changes reset behavior to avoid shared references (const fresh = structuredClone(defaultInitialState);), but there is no unit test that would fail if RESET_FORM regresses back to reference-sharing.

Violated guideline: "Test Coverage - All new features must have unit test coverage of at least 80%" (CLAUDE.md)

Suggested fix (extend tests/unit/dashboard/provider-form-clone-deep-copy.test.ts):

import { describe, expect, it } from "vitest";
import {
  createInitialState,
  providerFormReducer,
} from "@/app/[locale]/settings/providers/_components/forms/provider-form/provider-form-context";

describe("providerFormReducer RESET_FORM deep-copy safety", () => {
  it("returns fresh nested objects on each reset", () => {
    const base = createInitialState("create");

    const s1 = providerFormReducer(base, { type: "RESET_FORM" });
    const s2 = providerFormReducer(base, { type: "RESET_FORM" });

    expect(s1.routing.modelRedirects).not.toBe(s2.routing.modelRedirects);
    expect(s1.routing.allowedModels).not.toBe(s2.routing.allowedModels);
    expect(s1.routing.groupPriorities).not.toBe(s2.routing.groupPriorities);
  });

  it("preserves activeTab", () => {
    const withTab = providerFormReducer(createInitialState("create"), {
      type: "SET_ACTIVE_TAB",
      payload: "routing",
    });

    const reset = providerFormReducer(withTab, { type: "RESET_FORM" });
    expect(reset.ui.activeTab).toBe("routing");
  });
});

...defaultInitialState,
ui: { ...defaultInitialState.ui, activeTab: state.ui.activeTab },
...fresh,
ui: { ...fresh.ui, activeTab: state.ui.activeTab },
};
}

// Load provider data
case "LOAD_PROVIDER":
Expand Down
158 changes: 158 additions & 0 deletions tests/unit/dashboard/provider-form-clone-deep-copy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { describe, expect, it } from "vitest";
import { createInitialState } from "@/app/[locale]/settings/providers/_components/forms/provider-form/provider-form-context";
import type { ProviderDisplay } from "@/types/provider";

function makeProvider(overrides?: Partial<ProviderDisplay>): ProviderDisplay {
return {
id: 1,
name: "TestProvider",
url: "https://api.example.com",
maskedKey: "sk-****1234",
isEnabled: true,
weight: 1,
priority: 0,
groupPriorities: { groupA: 10, groupB: 20 },
costMultiplier: 1.0,
groupTag: "groupA,groupB",
providerType: "claude",
providerVendorId: null,
preserveClientIp: false,
modelRedirects: { "claude-3": "claude-3.5" },
allowedModels: ["claude-3", "claude-3.5"],
mcpPassthroughType: "none",
mcpPassthroughUrl: null,
limit5hUsd: null,
limitDailyUsd: null,
dailyResetMode: "fixed",
dailyResetTime: "00:00",
limitWeeklyUsd: null,
limitMonthlyUsd: null,
limitTotalUsd: null,
limitConcurrentSessions: 0,
maxRetryAttempts: null,
circuitBreakerFailureThreshold: 3,
circuitBreakerOpenDuration: 60000,
circuitBreakerHalfOpenSuccessThreshold: 2,
proxyUrl: null,
proxyFallbackToDirect: false,
firstByteTimeoutStreamingMs: 30000,
streamingIdleTimeoutMs: 60000,
requestTimeoutNonStreamingMs: 120000,
websiteUrl: null,
faviconUrl: null,
cacheTtlPreference: null,
context1mPreference: null,
codexReasoningEffortPreference: null,
codexReasoningSummaryPreference: null,
codexTextVerbosityPreference: null,
codexParallelToolCallsPreference: null,
anthropicMaxTokensPreference: null,
anthropicThinkingBudgetPreference: null,
anthropicAdaptiveThinking: {
effort: "high",
modelMatchMode: "specific",
models: ["claude-opus-4-6"],
},
geminiGoogleSearchPreference: null,
tpm: null,
rpm: null,
rpd: null,
cc: null,
createdAt: "2025-01-01T00:00:00.000Z",
updatedAt: "2025-01-01T00:00:00.000Z",
...overrides,
} as ProviderDisplay;
}

describe("createInitialState deep-copy safety", () => {
describe("clone mode", () => {
it("modelRedirects is a distinct object with equal values", () => {
const source = makeProvider();
const state = createInitialState("create", undefined, source);
expect(state.routing.modelRedirects).toEqual(source.modelRedirects);
expect(state.routing.modelRedirects).not.toBe(source.modelRedirects);
});

it("allowedModels is a distinct array with equal values", () => {
const source = makeProvider();
const state = createInitialState("create", undefined, source);
expect(state.routing.allowedModels).toEqual(source.allowedModels);
expect(state.routing.allowedModels).not.toBe(source.allowedModels);
});

it("groupPriorities is a distinct object with equal values", () => {
const source = makeProvider();
const state = createInitialState("create", undefined, source);
expect(state.routing.groupPriorities).toEqual(source.groupPriorities);
expect(state.routing.groupPriorities).not.toBe(source.groupPriorities);
});

it("anthropicAdaptiveThinking is a distinct object with distinct models array", () => {
const source = makeProvider();
const state = createInitialState("create", undefined, source);
expect(state.routing.anthropicAdaptiveThinking).toEqual(source.anthropicAdaptiveThinking);
expect(state.routing.anthropicAdaptiveThinking).not.toBe(source.anthropicAdaptiveThinking);
expect(state.routing.anthropicAdaptiveThinking!.models).not.toBe(
source.anthropicAdaptiveThinking!.models
);
});

it("null anthropicAdaptiveThinking stays null", () => {
const source = makeProvider({ anthropicAdaptiveThinking: null });
const state = createInitialState("create", undefined, source);
expect(state.routing.anthropicAdaptiveThinking).toBeNull();
});

it("null modelRedirects falls back to empty object", () => {
const source = makeProvider({ modelRedirects: null });
const state = createInitialState("create", undefined, source);
expect(state.routing.modelRedirects).toEqual({});
});

it("null allowedModels falls back to empty array", () => {
const source = makeProvider({ allowedModels: null });
const state = createInitialState("create", undefined, source);
expect(state.routing.allowedModels).toEqual([]);
});

it("null groupPriorities falls back to empty object", () => {
const source = makeProvider({ groupPriorities: null });
const state = createInitialState("create", undefined, source);
expect(state.routing.groupPriorities).toEqual({});
});

it("name gets _Copy suffix", () => {
const source = makeProvider({ name: "MyProvider" });
const state = createInitialState("create", undefined, source);
expect(state.basic.name).toBe("MyProvider_Copy");
});

it("key is always empty", () => {
const source = makeProvider();
const state = createInitialState("create", undefined, source);
expect(state.basic.key).toBe("");
});
});

describe("edit mode", () => {
it("nested objects are isolated from source provider", () => {
const source = makeProvider();
const state = createInitialState("edit", source);
expect(state.routing.modelRedirects).toEqual(source.modelRedirects);
expect(state.routing.modelRedirects).not.toBe(source.modelRedirects);
expect(state.routing.allowedModels).not.toBe(source.allowedModels);
expect(state.routing.groupPriorities).not.toBe(source.groupPriorities);
expect(state.routing.anthropicAdaptiveThinking).not.toBe(source.anthropicAdaptiveThinking);
});
});

describe("create mode without clone source", () => {
it("nested objects use fresh defaults", () => {
const state = createInitialState("create");
expect(state.routing.modelRedirects).toEqual({});
expect(state.routing.allowedModels).toEqual([]);
expect(state.routing.groupPriorities).toEqual({});
expect(state.routing.anthropicAdaptiveThinking).toBeNull();
});
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium] [TEST-MISSING-CRITICAL] No test coverage for the RESET_FORM reducer's structuredClone fix

Why this is a problem: The PR description explicitly identifies the RESET_FORM reducer as having the same class of shallow-copy bug, and the fix adds structuredClone(defaultInitialState) at provider-form-context.tsx:327. However, the 13 new test cases only cover createInitialState -- the RESET_FORM reducer path has zero test coverage. If this structuredClone call were accidentally removed in a future change, no test would catch the regression.

Per CLAUDE.md: "All new features must have unit test coverage of at least 80%"

Suggested fix: Add test cases for the RESET_FORM reducer in this file:

import { providerFormReducer, createInitialState } from "@/app/[locale]/settings/providers/_components/forms/provider-form/provider-form-context";

describe("providerFormReducer RESET_FORM", () => {
  it("returns a state with no shared references across resets", () => {
    const initial = createInitialState("create");
    const reset1 = providerFormReducer(initial, { type: "RESET_FORM" });
    const reset2 = providerFormReducer(initial, { type: "RESET_FORM" });

    expect(reset1.routing.modelRedirects).not.toBe(reset2.routing.modelRedirects);
    expect(reset1.routing.allowedModels).not.toBe(reset2.routing.allowedModels);
    expect(reset1.routing.groupPriorities).not.toBe(reset2.routing.groupPriorities);
  });

  it("preserves the active tab from current state", () => {
    const initial = createInitialState("create");
    const withTab = { ...initial, ui: { ...initial.ui, activeTab: "routing" as const } };
    const reset = providerFormReducer(withTab, { type: "RESET_FORM" });
    expect(reset.ui.activeTab).toBe("routing");
  });
});

Loading