mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
[Test] Add unit tests for router_settings components
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f778afc14b
commit
0e8b5616d6
6 changed files with 650 additions and 0 deletions
|
|
@ -0,0 +1,55 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import LatencyBasedConfiguration from "./LatencyBasedConfiguration";
|
||||
|
||||
describe("LatencyBasedConfiguration", () => {
|
||||
it("should render the section heading", () => {
|
||||
render(<LatencyBasedConfiguration routingStrategyArgs={{}} />);
|
||||
expect(screen.getByText("Latency-Based Configuration")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render default params when no args are provided", () => {
|
||||
render(<LatencyBasedConfiguration routingStrategyArgs={null as any} />);
|
||||
// Default: ttl=3600, lowest_latency_buffer=0
|
||||
expect(screen.getByDisplayValue("3600")).toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue("0")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render the provided routing strategy args as inputs", () => {
|
||||
const args = { ttl: 7200, lowest_latency_buffer: 0.1 };
|
||||
render(<LatencyBasedConfiguration routingStrategyArgs={args} />);
|
||||
expect(screen.getByDisplayValue("7200")).toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue("0.1")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render an input with the correct name attribute for each param", () => {
|
||||
const args = { ttl: 3600, lowest_latency_buffer: 0 };
|
||||
render(<LatencyBasedConfiguration routingStrategyArgs={args} />);
|
||||
expect(screen.getByRole("textbox", { name: /ttl/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole("textbox", { name: /lowest latency buffer/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display the TTL parameter explanation", () => {
|
||||
render(<LatencyBasedConfiguration routingStrategyArgs={null as any} />);
|
||||
expect(
|
||||
screen.getByText(/sliding window to look back over/i)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display the lowest_latency_buffer parameter explanation", () => {
|
||||
render(<LatencyBasedConfiguration routingStrategyArgs={null as any} />);
|
||||
expect(
|
||||
screen.getByText(/shuffle between deployments within this %/i)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render object values stringified into the input", () => {
|
||||
const args = { ttl: { nested: true } };
|
||||
render(<LatencyBasedConfiguration routingStrategyArgs={args} />);
|
||||
// HTML input type=text strips newlines, so check that the key/value appears
|
||||
const input = document.querySelector('input[name="ttl"]') as HTMLInputElement;
|
||||
expect(input).not.toBeNull();
|
||||
expect(input.value).toContain('"nested"');
|
||||
expect(input.value).toContain('true');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import ReliabilityRetriesSection from "./ReliabilityRetriesSection";
|
||||
|
||||
const baseSettings = {
|
||||
num_retries: 3,
|
||||
timeout: 30,
|
||||
allowed_fails: 2,
|
||||
fallbacks: ["gpt-3.5"],
|
||||
context_window_fallbacks: [],
|
||||
routing_strategy_args: { ttl: 3600 },
|
||||
routing_strategy: "simple-shuffle",
|
||||
enable_tag_filtering: false,
|
||||
};
|
||||
|
||||
describe("ReliabilityRetriesSection", () => {
|
||||
it("should render the section heading", () => {
|
||||
render(<ReliabilityRetriesSection routerSettings={{}} routerFieldsMetadata={{}} />);
|
||||
expect(screen.getByText("Reliability & Retries")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render input fields for non-excluded settings", () => {
|
||||
render(<ReliabilityRetriesSection routerSettings={baseSettings} routerFieldsMetadata={{}} />);
|
||||
expect(screen.getByDisplayValue("3")).toBeInTheDocument(); // num_retries
|
||||
expect(screen.getByDisplayValue("30")).toBeInTheDocument(); // timeout
|
||||
expect(screen.getByDisplayValue("2")).toBeInTheDocument(); // allowed_fails
|
||||
});
|
||||
|
||||
it("should not render inputs for excluded keys", () => {
|
||||
render(<ReliabilityRetriesSection routerSettings={baseSettings} routerFieldsMetadata={{}} />);
|
||||
// Each excluded key must not produce a visible input value
|
||||
const inputs = screen.queryAllByRole("textbox");
|
||||
const inputNames = inputs.map((el) => el.getAttribute("name"));
|
||||
expect(inputNames).not.toContain("fallbacks");
|
||||
expect(inputNames).not.toContain("context_window_fallbacks");
|
||||
expect(inputNames).not.toContain("routing_strategy_args");
|
||||
expect(inputNames).not.toContain("routing_strategy");
|
||||
expect(inputNames).not.toContain("enable_tag_filtering");
|
||||
});
|
||||
|
||||
it("should use ui_field_name from metadata as the label", () => {
|
||||
const metadata = {
|
||||
num_retries: { ui_field_name: "Number of Retries", field_description: "How many times to retry" },
|
||||
};
|
||||
render(
|
||||
<ReliabilityRetriesSection routerSettings={{ num_retries: 3 }} routerFieldsMetadata={metadata} />
|
||||
);
|
||||
expect(screen.getByText("Number of Retries")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should fall back to the raw param name when no metadata label is available", () => {
|
||||
render(
|
||||
<ReliabilityRetriesSection routerSettings={{ num_retries: 3 }} routerFieldsMetadata={{}} />
|
||||
);
|
||||
expect(screen.getByText("num_retries")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render null values as an empty input", () => {
|
||||
render(
|
||||
<ReliabilityRetriesSection routerSettings={{ timeout: null }} routerFieldsMetadata={{}} />
|
||||
);
|
||||
const input = screen.getByRole("textbox", { name: /timeout/i }) as HTMLInputElement;
|
||||
expect(input.value).toBe("");
|
||||
});
|
||||
|
||||
it("should render object values stringified into the input", () => {
|
||||
const settings = { retry_policy: { "rate-limited": 2 } };
|
||||
render(<ReliabilityRetriesSection routerSettings={settings} routerFieldsMetadata={{}} />);
|
||||
// HTML input type=text strips newlines, so check that the key/value appears
|
||||
const input = document.querySelector('input[name="retry_policy"]') as HTMLInputElement;
|
||||
expect(input).not.toBeNull();
|
||||
expect(input.value).toContain('"rate-limited"');
|
||||
expect(input.value).toContain('2');
|
||||
});
|
||||
|
||||
it("should render no inputs when routerSettings is empty", () => {
|
||||
render(<ReliabilityRetriesSection routerSettings={{}} routerFieldsMetadata={{}} />);
|
||||
expect(screen.queryAllByRole("textbox")).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
import { describe, it, expect, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import RouterSettingsForm from "./RouterSettingsForm";
|
||||
import type { RouterSettingsFormValue } from "./RouterSettingsForm";
|
||||
|
||||
// Use the same antd mock as RoutingStrategySelector to keep things consistent
|
||||
vi.mock("antd", () => ({
|
||||
Select: Object.assign(
|
||||
({ value, onChange, children }: any) => (
|
||||
<select
|
||||
data-testid="strategy-select"
|
||||
value={value ?? ""}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
>
|
||||
{children}
|
||||
</select>
|
||||
),
|
||||
{
|
||||
Option: ({ value, children }: any) => (
|
||||
<option value={value}>{children}</option>
|
||||
),
|
||||
}
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@tremor/react", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@tremor/react")>();
|
||||
return {
|
||||
...actual,
|
||||
Switch: ({ checked, onChange }: any) => (
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
checked={checked}
|
||||
onChange={(e) => onChange(e.target.checked)}
|
||||
/>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
const defaultValue: RouterSettingsFormValue = {
|
||||
routerSettings: {},
|
||||
selectedStrategy: null,
|
||||
enableTagFiltering: false,
|
||||
};
|
||||
|
||||
const baseProps = {
|
||||
value: defaultValue,
|
||||
onChange: vi.fn(),
|
||||
routerFieldsMetadata: {},
|
||||
availableRoutingStrategies: [],
|
||||
routingStrategyDescriptions: {},
|
||||
};
|
||||
|
||||
describe("RouterSettingsForm", () => {
|
||||
it("should render", () => {
|
||||
render(<RouterSettingsForm {...baseProps} />);
|
||||
expect(screen.getByText("Routing Settings")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should not show the strategy selector when no strategies are provided", () => {
|
||||
render(<RouterSettingsForm {...baseProps} />);
|
||||
expect(screen.queryByTestId("strategy-select")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show the strategy selector when strategies are available", () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
availableRoutingStrategies: ["simple-shuffle", "latency-based-routing"],
|
||||
};
|
||||
render(<RouterSettingsForm {...props} />);
|
||||
expect(screen.getByTestId("strategy-select")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should not render LatencyBasedConfiguration for non-latency strategies", () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
value: { ...defaultValue, selectedStrategy: "simple-shuffle" },
|
||||
availableRoutingStrategies: ["simple-shuffle"],
|
||||
};
|
||||
render(<RouterSettingsForm {...props} />);
|
||||
expect(screen.queryByText("Latency-Based Configuration")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render LatencyBasedConfiguration when strategy is latency-based-routing", () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
value: {
|
||||
...defaultValue,
|
||||
selectedStrategy: "latency-based-routing",
|
||||
routerSettings: { routing_strategy_args: { ttl: 3600, lowest_latency_buffer: 0 } },
|
||||
},
|
||||
availableRoutingStrategies: ["latency-based-routing"],
|
||||
};
|
||||
render(<RouterSettingsForm {...props} />);
|
||||
expect(screen.getByText("Latency-Based Configuration")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should call onChange with the updated strategy when the selector changes", () => {
|
||||
const onChange = vi.fn();
|
||||
const props = {
|
||||
...baseProps,
|
||||
onChange,
|
||||
availableRoutingStrategies: ["simple-shuffle", "latency-based-routing"],
|
||||
};
|
||||
render(<RouterSettingsForm {...props} />);
|
||||
|
||||
const select = screen.getByTestId("strategy-select") as HTMLSelectElement;
|
||||
select.value = "latency-based-routing";
|
||||
select.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ selectedStrategy: "latency-based-routing" })
|
||||
);
|
||||
});
|
||||
|
||||
it("should call onChange with the updated enableTagFiltering when the toggle changes", async () => {
|
||||
const onChange = vi.fn();
|
||||
const user = userEvent.setup();
|
||||
render(<RouterSettingsForm {...baseProps} onChange={onChange} />);
|
||||
|
||||
await user.click(screen.getByRole("switch"));
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ enableTagFiltering: true })
|
||||
);
|
||||
});
|
||||
|
||||
it("should show the Reliability & Retries section", () => {
|
||||
render(<RouterSettingsForm {...baseProps} />);
|
||||
expect(screen.getByText("Reliability & Retries")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
import { describe, it, expect, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import RoutingStrategySelector from "./RoutingStrategySelector";
|
||||
|
||||
// Ant Design's Select is complex to drive in JSDOM; swap it for a plain
|
||||
// <select> so we can assert options and fire change events normally.
|
||||
vi.mock("antd", () => ({
|
||||
Select: Object.assign(
|
||||
({ value, onChange, children }: any) => (
|
||||
<div data-testid="ant-select">
|
||||
<select
|
||||
data-testid="strategy-select"
|
||||
value={value ?? ""}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
>
|
||||
{children}
|
||||
</select>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
Option: ({ value, children }: any) => (
|
||||
<option value={value}>{children}</option>
|
||||
),
|
||||
}
|
||||
),
|
||||
}));
|
||||
|
||||
const baseProps = {
|
||||
selectedStrategy: null,
|
||||
availableStrategies: ["simple-shuffle", "latency-based-routing", "least-busy"],
|
||||
routingStrategyDescriptions: {
|
||||
"simple-shuffle": "Randomly pick a deployment",
|
||||
"latency-based-routing": "Pick the lowest-latency deployment",
|
||||
},
|
||||
routerFieldsMetadata: {},
|
||||
onStrategyChange: vi.fn(),
|
||||
};
|
||||
|
||||
describe("RoutingStrategySelector", () => {
|
||||
it("should render", () => {
|
||||
render(<RoutingStrategySelector {...baseProps} />);
|
||||
expect(screen.getByTestId("ant-select")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display default label when no metadata is provided", () => {
|
||||
render(<RoutingStrategySelector {...baseProps} />);
|
||||
expect(screen.getByText("Routing Strategy")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display ui_field_name from metadata when provided", () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
routerFieldsMetadata: {
|
||||
routing_strategy: {
|
||||
ui_field_name: "Strategy",
|
||||
field_description: "How to pick a deployment",
|
||||
},
|
||||
},
|
||||
};
|
||||
render(<RoutingStrategySelector {...props} />);
|
||||
expect(screen.getByText("Strategy")).toBeInTheDocument();
|
||||
expect(screen.getByText("How to pick a deployment")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render all available strategies as options", () => {
|
||||
render(<RoutingStrategySelector {...baseProps} />);
|
||||
expect(screen.getByText("simple-shuffle")).toBeInTheDocument();
|
||||
expect(screen.getByText("latency-based-routing")).toBeInTheDocument();
|
||||
expect(screen.getByText("least-busy")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display strategy descriptions alongside option labels", () => {
|
||||
render(<RoutingStrategySelector {...baseProps} />);
|
||||
expect(screen.getByText("Randomly pick a deployment")).toBeInTheDocument();
|
||||
expect(screen.getByText("Pick the lowest-latency deployment")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should not render a description for a strategy that has none", () => {
|
||||
render(<RoutingStrategySelector {...baseProps} />);
|
||||
// "least-busy" has no entry in routingStrategyDescriptions
|
||||
const select = screen.getByTestId("strategy-select");
|
||||
const leastBusyOption = Array.from(select.querySelectorAll("option")).find(
|
||||
(o) => o.value === "least-busy"
|
||||
);
|
||||
expect(leastBusyOption).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should call onStrategyChange with the selected strategy value", () => {
|
||||
const onStrategyChange = vi.fn();
|
||||
render(<RoutingStrategySelector {...baseProps} onStrategyChange={onStrategyChange} />);
|
||||
|
||||
const select = screen.getByTestId("strategy-select") as HTMLSelectElement;
|
||||
select.value = "latency-based-routing";
|
||||
select.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
|
||||
expect(onStrategyChange).toHaveBeenCalledWith("latency-based-routing");
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
import { describe, it, expect, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import TagFilteringToggle from "./TagFilteringToggle";
|
||||
|
||||
// setupTests.ts mocks @tremor/react but leaves Switch as the real implementation.
|
||||
// Re-mock Switch as a plain checkbox so toggle interactions are trivially testable.
|
||||
vi.mock("@tremor/react", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@tremor/react")>();
|
||||
return {
|
||||
...actual,
|
||||
Switch: ({ checked, onChange, className }: any) => (
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
checked={checked}
|
||||
onChange={(e) => onChange(e.target.checked)}
|
||||
className={className}
|
||||
/>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
const baseMetadata = {
|
||||
enable_tag_filtering: {
|
||||
ui_field_name: "Tag Filtering",
|
||||
field_description: "Route requests based on tags",
|
||||
link: null,
|
||||
},
|
||||
};
|
||||
|
||||
describe("TagFilteringToggle", () => {
|
||||
it("should render", () => {
|
||||
render(
|
||||
<TagFilteringToggle enabled={false} routerFieldsMetadata={{}} onToggle={vi.fn()} />
|
||||
);
|
||||
expect(screen.getByRole("switch")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display default label when no metadata is provided", () => {
|
||||
render(
|
||||
<TagFilteringToggle enabled={false} routerFieldsMetadata={{}} onToggle={vi.fn()} />
|
||||
);
|
||||
expect(screen.getByText("Enable Tag Filtering")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display the label from metadata when provided", () => {
|
||||
render(
|
||||
<TagFilteringToggle
|
||||
enabled={false}
|
||||
routerFieldsMetadata={baseMetadata}
|
||||
onToggle={vi.fn()}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("Tag Filtering")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display the description from metadata", () => {
|
||||
render(
|
||||
<TagFilteringToggle
|
||||
enabled={false}
|
||||
routerFieldsMetadata={baseMetadata}
|
||||
onToggle={vi.fn()}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("Route requests based on tags")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render a Learn more link when metadata provides one", () => {
|
||||
const metadata = {
|
||||
enable_tag_filtering: {
|
||||
...baseMetadata.enable_tag_filtering,
|
||||
link: "https://docs.example.com/tag-filtering",
|
||||
},
|
||||
};
|
||||
render(
|
||||
<TagFilteringToggle enabled={false} routerFieldsMetadata={metadata} onToggle={vi.fn()} />
|
||||
);
|
||||
const link = screen.getByRole("link", { name: /learn more/i });
|
||||
expect(link).toBeInTheDocument();
|
||||
expect(link).toHaveAttribute("href", "https://docs.example.com/tag-filtering");
|
||||
});
|
||||
|
||||
it("should not render a Learn more link when metadata has no link", () => {
|
||||
render(
|
||||
<TagFilteringToggle
|
||||
enabled={false}
|
||||
routerFieldsMetadata={baseMetadata}
|
||||
onToggle={vi.fn()}
|
||||
/>
|
||||
);
|
||||
expect(screen.queryByRole("link", { name: /learn more/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should reflect the enabled=true state on the switch", () => {
|
||||
render(
|
||||
<TagFilteringToggle enabled={true} routerFieldsMetadata={{}} onToggle={vi.fn()} />
|
||||
);
|
||||
expect(screen.getByRole("switch")).toBeChecked();
|
||||
});
|
||||
|
||||
it("should call onToggle with the new value when the switch is toggled", async () => {
|
||||
const onToggle = vi.fn();
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<TagFilteringToggle enabled={false} routerFieldsMetadata={{}} onToggle={onToggle} />
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole("switch"));
|
||||
|
||||
expect(onToggle).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { renderWithProviders, screen, waitFor } from "../../../tests/test-utils";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import RouterSettings from "./index";
|
||||
|
||||
vi.mock("antd", () => ({
|
||||
Select: Object.assign(
|
||||
({ value, onChange, children }: any) => (
|
||||
<select
|
||||
data-testid="strategy-select"
|
||||
value={value ?? ""}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
>
|
||||
{children}
|
||||
</select>
|
||||
),
|
||||
{
|
||||
Option: ({ value, children }: any) => (
|
||||
<option value={value}>{children}</option>
|
||||
),
|
||||
}
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@tremor/react", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@tremor/react")>();
|
||||
return {
|
||||
...actual,
|
||||
Switch: ({ checked, onChange }: any) => (
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
checked={checked}
|
||||
onChange={(e) => onChange(e.target.checked)}
|
||||
/>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@/components/networking", () => ({
|
||||
getCallbacksCall: vi.fn(),
|
||||
getRouterSettingsCall: vi.fn(),
|
||||
setCallbacksCall: vi.fn(),
|
||||
}));
|
||||
|
||||
import {
|
||||
getCallbacksCall,
|
||||
getRouterSettingsCall,
|
||||
setCallbacksCall,
|
||||
} from "@/components/networking";
|
||||
|
||||
const mockCallbacksResponse = {
|
||||
router_settings: {
|
||||
routing_strategy: "simple-shuffle",
|
||||
num_retries: 3,
|
||||
timeout: 30,
|
||||
},
|
||||
};
|
||||
|
||||
const mockRouterSettingsResponse = {
|
||||
fields: [
|
||||
{
|
||||
field_name: "routing_strategy",
|
||||
ui_field_name: "Routing Strategy",
|
||||
field_description: "How requests are distributed",
|
||||
options: ["simple-shuffle", "latency-based-routing"],
|
||||
link: null,
|
||||
},
|
||||
{
|
||||
field_name: "enable_tag_filtering",
|
||||
ui_field_name: "Tag Filtering",
|
||||
field_description: "Route by tag",
|
||||
field_value: false,
|
||||
link: null,
|
||||
},
|
||||
],
|
||||
routing_strategy_descriptions: {
|
||||
"simple-shuffle": "Randomly pick a deployment",
|
||||
"latency-based-routing": "Pick the lowest-latency deployment",
|
||||
},
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
accessToken: "test-token",
|
||||
userRole: "Admin",
|
||||
userID: "user-1",
|
||||
modelData: null,
|
||||
};
|
||||
|
||||
describe("RouterSettings", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(getCallbacksCall).mockResolvedValue(mockCallbacksResponse);
|
||||
vi.mocked(getRouterSettingsCall).mockResolvedValue(mockRouterSettingsResponse);
|
||||
vi.mocked(setCallbacksCall).mockResolvedValue({});
|
||||
});
|
||||
|
||||
it("should render nothing when accessToken is null", () => {
|
||||
const { container } = renderWithProviders(
|
||||
<RouterSettings {...defaultProps} accessToken={null} />
|
||||
);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it("should render the Save Changes and Reset buttons when authenticated", () => {
|
||||
renderWithProviders(<RouterSettings {...defaultProps} />);
|
||||
expect(screen.getByRole("button", { name: /save changes/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: /reset/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should fetch callbacks and router settings on mount", async () => {
|
||||
renderWithProviders(<RouterSettings {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getCallbacksCall).toHaveBeenCalledWith("test-token", "user-1", "Admin");
|
||||
});
|
||||
expect(getRouterSettingsCall).toHaveBeenCalledWith("test-token");
|
||||
});
|
||||
|
||||
it("should not fetch data when any required prop is missing", () => {
|
||||
renderWithProviders(
|
||||
<RouterSettings {...defaultProps} userRole={null} />
|
||||
);
|
||||
expect(getCallbacksCall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should render routing strategies loaded from the API", async () => {
|
||||
renderWithProviders(<RouterSettings {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("strategy-select")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const select = screen.getByTestId("strategy-select") as HTMLSelectElement;
|
||||
const optionValues = Array.from(select.options).map((o) => o.value);
|
||||
expect(optionValues).toContain("simple-shuffle");
|
||||
expect(optionValues).toContain("latency-based-routing");
|
||||
});
|
||||
|
||||
it("should call setCallbacksCall with updated settings on Save Changes", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(<RouterSettings {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: /save changes/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await user.click(screen.getByRole("button", { name: /save changes/i }));
|
||||
|
||||
expect(setCallbacksCall).toHaveBeenCalledWith(
|
||||
"test-token",
|
||||
expect.objectContaining({ router_settings: expect.any(Object) })
|
||||
);
|
||||
});
|
||||
|
||||
it("should show a success notification after saving", async () => {
|
||||
const NotificationsManager = await import("@/components/molecules/notifications_manager");
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(<RouterSettings {...defaultProps} />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.getByRole("button", { name: /save changes/i })).toBeInTheDocument()
|
||||
);
|
||||
await user.click(screen.getByRole("button", { name: /save changes/i }));
|
||||
|
||||
expect(NotificationsManager.default.success).toHaveBeenCalledWith(
|
||||
"router settings updated successfully"
|
||||
);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue