mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-12 17:04:30 +00:00
[Test] Add Vitest unit tests for policies components
Add 58 unit tests across 5 files in ui/litellm-dashboard/src/components/policies/: - build_attachment_data.test.ts: pure logic tests for global vs specific scope - impact_preview_alert.test.tsx: rendering for global scope warning and specific scope counts/tags - PolicySelector.test.tsx: pure function tests for getPolicyOptionEntries/policyVersionRef + component fetch/disable behavior - policy_info.test.tsx: loading, not-found, policy details rendering, admin gating, onClose/onEdit callbacks - attachment_table.test.tsx: loading/empty states, row rendering, scope badges, delete admin gating Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import { screen, waitFor } from "@testing-library/react";
|
||||
import { renderWithProviders } from "../../../tests/test-utils";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import * as networking from "../networking";
|
||||
import PolicySelector, { getPolicyOptionEntries, policyVersionRef, POLICY_VERSION_ID_PREFIX } from "./PolicySelector";
|
||||
import { Policy } from "./types";
|
||||
|
||||
vi.mock("../networking");
|
||||
|
||||
const makePolicy = (overrides: Partial<Policy>): Policy => ({
|
||||
policy_id: "uuid-1",
|
||||
policy_name: "test-policy",
|
||||
inherit: null,
|
||||
description: null,
|
||||
guardrails_add: [],
|
||||
guardrails_remove: [],
|
||||
condition: null,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
describe("policyVersionRef", () => {
|
||||
it("should prefix the policy id with the version prefix", () => {
|
||||
expect(policyVersionRef("abc-123")).toBe(`${POLICY_VERSION_ID_PREFIX}abc-123`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getPolicyOptionEntries", () => {
|
||||
it("should filter out draft policies", () => {
|
||||
const policies = [
|
||||
makePolicy({ policy_name: "draft-one", version_status: "draft" }),
|
||||
makePolicy({ policy_name: "published-one", version_status: "published", policy_id: "pub-id" }),
|
||||
];
|
||||
const options = getPolicyOptionEntries(policies);
|
||||
expect(options).toHaveLength(1);
|
||||
expect(options[0].label).toContain("published-one");
|
||||
});
|
||||
|
||||
it("should use the policy_name as value for production policies", () => {
|
||||
const policy = makePolicy({ policy_name: "prod-policy", version_status: "production" });
|
||||
const options = getPolicyOptionEntries([policy]);
|
||||
expect(options[0].value).toBe("prod-policy");
|
||||
});
|
||||
|
||||
it("should use a version ref as value for published (non-production) policies", () => {
|
||||
const policy = makePolicy({ policy_id: "abc-123", policy_name: "pub-policy", version_status: "published" });
|
||||
const options = getPolicyOptionEntries([policy]);
|
||||
expect(options[0].value).toBe(policyVersionRef("abc-123"));
|
||||
});
|
||||
|
||||
it("should include the version number and status in the label", () => {
|
||||
const policy = makePolicy({ policy_name: "my-policy", version_status: "published", version_number: 3 });
|
||||
const options = getPolicyOptionEntries([policy]);
|
||||
expect(options[0].label).toContain("v3");
|
||||
expect(options[0].label).toContain("published");
|
||||
});
|
||||
|
||||
it("should append the description to the label when present", () => {
|
||||
const policy = makePolicy({
|
||||
policy_name: "my-policy",
|
||||
version_status: "published",
|
||||
description: "blocks PII",
|
||||
});
|
||||
const options = getPolicyOptionEntries([policy]);
|
||||
expect(options[0].label).toContain("blocks PII");
|
||||
});
|
||||
|
||||
it("should treat policies with no version_status as draft and filter them out", () => {
|
||||
const policy = makePolicy({ policy_name: "implicit-draft" });
|
||||
const options = getPolicyOptionEntries([policy]);
|
||||
expect(options).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("PolicySelector", () => {
|
||||
const mockOnChange = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should render", () => {
|
||||
vi.mocked(networking.getPoliciesList).mockResolvedValue({ policies: [] });
|
||||
renderWithProviders(
|
||||
<PolicySelector accessToken="tok" onChange={mockOnChange} />
|
||||
);
|
||||
expect(screen.getByRole("combobox")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should fetch policies on mount with the given access token", async () => {
|
||||
vi.mocked(networking.getPoliciesList).mockResolvedValue({ policies: [] });
|
||||
renderWithProviders(<PolicySelector accessToken="my-token" onChange={mockOnChange} />);
|
||||
await waitFor(() => {
|
||||
expect(networking.getPoliciesList).toHaveBeenCalledWith("my-token");
|
||||
});
|
||||
});
|
||||
|
||||
it("should call onPoliciesLoaded with the fetched policies after mount", async () => {
|
||||
const policies = [makePolicy({ version_status: "production" })];
|
||||
vi.mocked(networking.getPoliciesList).mockResolvedValue({ policies });
|
||||
const onPoliciesLoaded = vi.fn();
|
||||
renderWithProviders(
|
||||
<PolicySelector accessToken="tok" onChange={mockOnChange} onPoliciesLoaded={onPoliciesLoaded} />
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(onPoliciesLoaded).toHaveBeenCalledWith(policies);
|
||||
});
|
||||
});
|
||||
|
||||
it("should show a disabled placeholder when disabled prop is true", () => {
|
||||
vi.mocked(networking.getPoliciesList).mockResolvedValue({ policies: [] });
|
||||
renderWithProviders(
|
||||
<PolicySelector accessToken="tok" onChange={mockOnChange} disabled />
|
||||
);
|
||||
expect(screen.getByRole("combobox")).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should not fetch policies when accessToken is empty", () => {
|
||||
renderWithProviders(<PolicySelector accessToken="" onChange={mockOnChange} />);
|
||||
expect(networking.getPoliciesList).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,142 @@
|
||||
import React from "react";
|
||||
import { screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { renderWithProviders } from "../../../tests/test-utils";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import AttachmentTable from "./attachment_table";
|
||||
import { PolicyAttachment } from "./types";
|
||||
|
||||
vi.mock("./impact_popover", () => ({
|
||||
default: () => <button aria-label="View blast radius" />,
|
||||
}));
|
||||
|
||||
vi.mock("@heroicons/react/outline", () => ({
|
||||
TrashIcon: function TrashIcon() { return null; },
|
||||
SwitchVerticalIcon: function SwitchVerticalIcon() { return null; },
|
||||
ChevronUpIcon: function ChevronUpIcon() { return null; },
|
||||
ChevronDownIcon: function ChevronDownIcon() { return null; },
|
||||
}));
|
||||
|
||||
vi.mock("@tremor/react", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@tremor/react")>();
|
||||
return {
|
||||
...actual,
|
||||
Button: React.forwardRef<HTMLButtonElement, any>(({ children, ...props }, ref) =>
|
||||
React.createElement("button", { ...props, ref }, children)
|
||||
),
|
||||
Tooltip: ({ children }: { children?: React.ReactNode }) =>
|
||||
React.createElement(React.Fragment, null, children),
|
||||
Switch: ({ checked, onChange, className }: { checked?: boolean; onChange?: (v: boolean) => void; className?: string }) =>
|
||||
React.createElement("input", {
|
||||
type: "checkbox",
|
||||
role: "switch",
|
||||
checked,
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => onChange?.(e.target.checked),
|
||||
className,
|
||||
}),
|
||||
Icon: ({ icon: IconComp, onClick, className }: any) =>
|
||||
React.createElement("button", { type: "button", onClick, className }, IconComp?.displayName ?? IconComp?.name ?? "icon"),
|
||||
};
|
||||
});
|
||||
|
||||
const makeAttachment = (overrides: Partial<PolicyAttachment> = {}): PolicyAttachment => ({
|
||||
attachment_id: "att-abcdef1",
|
||||
policy_name: "my-policy",
|
||||
scope: null,
|
||||
teams: [],
|
||||
keys: [],
|
||||
models: [],
|
||||
tags: [],
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
attachments: [],
|
||||
isLoading: false,
|
||||
onDeleteClick: vi.fn(),
|
||||
isAdmin: true,
|
||||
accessToken: "test-token",
|
||||
};
|
||||
|
||||
describe("AttachmentTable", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should render", () => {
|
||||
renderWithProviders(<AttachmentTable {...defaultProps} />);
|
||||
expect(screen.getByText("Policy")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show a loading message when isLoading is true", () => {
|
||||
renderWithProviders(<AttachmentTable {...defaultProps} isLoading />);
|
||||
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show 'No attachments found' when there are no attachments", () => {
|
||||
renderWithProviders(<AttachmentTable {...defaultProps} />);
|
||||
expect(screen.getByText(/no attachments found/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render a row for each attachment", () => {
|
||||
const attachments = [
|
||||
makeAttachment({ attachment_id: "att-aaa0001", policy_name: "policy-alpha" }),
|
||||
makeAttachment({ attachment_id: "att-bbb0002", policy_name: "policy-beta" }),
|
||||
];
|
||||
renderWithProviders(<AttachmentTable {...defaultProps} attachments={attachments} />);
|
||||
expect(screen.getByText("policy-alpha")).toBeInTheDocument();
|
||||
expect(screen.getByText("policy-beta")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show 'Global (*)' badge when scope is '*'", () => {
|
||||
const attachments = [makeAttachment({ scope: "*" })];
|
||||
renderWithProviders(<AttachmentTable {...defaultProps} attachments={attachments} />);
|
||||
expect(screen.getByText("Global (*)")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show team tags when the attachment has teams", () => {
|
||||
const attachments = [makeAttachment({ teams: ["team-alpha", "team-beta"] })];
|
||||
renderWithProviders(<AttachmentTable {...defaultProps} attachments={attachments} />);
|
||||
expect(screen.getByText("team-alpha")).toBeInTheDocument();
|
||||
expect(screen.getByText("team-beta")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show an overflow indicator when there are more than 2 teams", () => {
|
||||
const attachments = [makeAttachment({ teams: ["t1", "t2", "t3", "t4"] })];
|
||||
renderWithProviders(<AttachmentTable {...defaultProps} attachments={attachments} />);
|
||||
expect(screen.getByText("+2")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should call onDeleteClick with the attachment_id when the delete icon is clicked", async () => {
|
||||
const attachment = makeAttachment({ attachment_id: "att-del-me1" });
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(<AttachmentTable {...defaultProps} attachments={[attachment]} />);
|
||||
await user.click(screen.getByRole("button", { name: /TrashIcon/i }));
|
||||
expect(defaultProps.onDeleteClick).toHaveBeenCalledWith("att-del-me1");
|
||||
});
|
||||
|
||||
it("should not show the delete icon for non-admins", () => {
|
||||
const attachment = makeAttachment();
|
||||
renderWithProviders(<AttachmentTable {...defaultProps} attachments={[attachment]} isAdmin={false} />);
|
||||
expect(screen.queryByRole("button", { name: /TrashIcon/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show a truncated attachment ID in the table", () => {
|
||||
const attachment = makeAttachment({ attachment_id: "att-abcdef1234567" });
|
||||
renderWithProviders(<AttachmentTable {...defaultProps} attachments={[attachment]} />);
|
||||
expect(screen.getByText("att-abc...")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render model tags when the attachment has models", () => {
|
||||
const attachments = [makeAttachment({ models: ["gpt-4", "claude-3"] })];
|
||||
renderWithProviders(<AttachmentTable {...defaultProps} attachments={attachments} />);
|
||||
expect(screen.getByText("gpt-4")).toBeInTheDocument();
|
||||
expect(screen.getByText("claude-3")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render tag chips when the attachment has tags", () => {
|
||||
const attachments = [makeAttachment({ tags: ["prod"] })];
|
||||
renderWithProviders(<AttachmentTable {...defaultProps} attachments={attachments} />);
|
||||
expect(screen.getByText("prod")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildAttachmentData } from "./build_attachment_data";
|
||||
|
||||
describe("buildAttachmentData", () => {
|
||||
describe("when scope is global", () => {
|
||||
it("should set scope to '*'", () => {
|
||||
const result = buildAttachmentData({ policy_name: "my-policy" }, "global");
|
||||
expect(result.scope).toBe("*");
|
||||
});
|
||||
|
||||
it("should not include teams, keys, models, or tags even when provided", () => {
|
||||
const result = buildAttachmentData(
|
||||
{ policy_name: "my-policy", teams: ["team-a"], keys: ["key-1"], models: ["gpt-4"], tags: ["prod"] },
|
||||
"global"
|
||||
);
|
||||
expect(result.teams).toBeUndefined();
|
||||
expect(result.keys).toBeUndefined();
|
||||
expect(result.models).toBeUndefined();
|
||||
expect(result.tags).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should include the policy_name", () => {
|
||||
const result = buildAttachmentData({ policy_name: "rate-limit-policy" }, "global");
|
||||
expect(result.policy_name).toBe("rate-limit-policy");
|
||||
});
|
||||
});
|
||||
|
||||
describe("when scope is specific", () => {
|
||||
it("should not set scope field", () => {
|
||||
const result = buildAttachmentData({ policy_name: "my-policy" }, "specific");
|
||||
expect(result.scope).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should include teams when provided", () => {
|
||||
const result = buildAttachmentData({ policy_name: "p", teams: ["team-a", "team-b"] }, "specific");
|
||||
expect(result.teams).toEqual(["team-a", "team-b"]);
|
||||
});
|
||||
|
||||
it("should include keys when provided", () => {
|
||||
const result = buildAttachmentData({ policy_name: "p", keys: ["sk-abc"] }, "specific");
|
||||
expect(result.keys).toEqual(["sk-abc"]);
|
||||
});
|
||||
|
||||
it("should include models when provided", () => {
|
||||
const result = buildAttachmentData({ policy_name: "p", models: ["gpt-4", "claude-3"] }, "specific");
|
||||
expect(result.models).toEqual(["gpt-4", "claude-3"]);
|
||||
});
|
||||
|
||||
it("should include tags when provided", () => {
|
||||
const result = buildAttachmentData({ policy_name: "p", tags: ["prod", "us-east"] }, "specific");
|
||||
expect(result.tags).toEqual(["prod", "us-east"]);
|
||||
});
|
||||
|
||||
it("should omit teams when the array is empty", () => {
|
||||
const result = buildAttachmentData({ policy_name: "p", teams: [] }, "specific");
|
||||
expect(result.teams).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should omit keys when the array is empty", () => {
|
||||
const result = buildAttachmentData({ policy_name: "p", keys: [] }, "specific");
|
||||
expect(result.keys).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should omit models when the array is empty", () => {
|
||||
const result = buildAttachmentData({ policy_name: "p", models: [] }, "specific");
|
||||
expect(result.models).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should omit tags when the array is empty", () => {
|
||||
const result = buildAttachmentData({ policy_name: "p", tags: [] }, "specific");
|
||||
expect(result.tags).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should omit all scope fields when none are provided", () => {
|
||||
const result = buildAttachmentData({ policy_name: "p" }, "specific");
|
||||
expect(result.teams).toBeUndefined();
|
||||
expect(result.keys).toBeUndefined();
|
||||
expect(result.models).toBeUndefined();
|
||||
expect(result.tags).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
import { screen } from "@testing-library/react";
|
||||
import { renderWithProviders } from "../../../tests/test-utils";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import ImpactPreviewAlert from "./impact_preview_alert";
|
||||
|
||||
const globalImpact = {
|
||||
affected_keys_count: -1,
|
||||
affected_teams_count: -1,
|
||||
sample_keys: [],
|
||||
sample_teams: [],
|
||||
};
|
||||
|
||||
const specificImpact = {
|
||||
affected_keys_count: 3,
|
||||
affected_teams_count: 1,
|
||||
sample_keys: ["sk-abc", "sk-def", "sk-ghi"],
|
||||
sample_teams: ["team-alpha"],
|
||||
};
|
||||
|
||||
describe("ImpactPreviewAlert", () => {
|
||||
it("should render", () => {
|
||||
renderWithProviders(<ImpactPreviewAlert impactResult={specificImpact} />);
|
||||
expect(screen.getByText("Impact Preview")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("when affected_keys_count is -1 (global scope)", () => {
|
||||
it("should show a warning about all keys and teams being affected", () => {
|
||||
renderWithProviders(<ImpactPreviewAlert impactResult={globalImpact} />);
|
||||
expect(screen.getByText(/all keys and teams/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when scope is specific", () => {
|
||||
it("should show the number of affected keys", () => {
|
||||
renderWithProviders(<ImpactPreviewAlert impactResult={specificImpact} />);
|
||||
expect(screen.getByText(/3 keys/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show the number of affected teams", () => {
|
||||
renderWithProviders(<ImpactPreviewAlert impactResult={specificImpact} />);
|
||||
expect(screen.getByText(/1 team\b/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render sample key tags", () => {
|
||||
renderWithProviders(<ImpactPreviewAlert impactResult={specificImpact} />);
|
||||
expect(screen.getByText("sk-abc")).toBeInTheDocument();
|
||||
expect(screen.getByText("sk-def")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render sample team tags", () => {
|
||||
renderWithProviders(<ImpactPreviewAlert impactResult={specificImpact} />);
|
||||
expect(screen.getByText("team-alpha")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show a '...more' indicator when there are more than 5 keys", () => {
|
||||
const manyKeys = {
|
||||
affected_keys_count: 10,
|
||||
affected_teams_count: 0,
|
||||
sample_keys: ["k1", "k2", "k3", "k4", "k5"],
|
||||
sample_teams: [],
|
||||
};
|
||||
renderWithProviders(<ImpactPreviewAlert impactResult={manyKeys} />);
|
||||
expect(screen.getByText(/and 5 more/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should use singular 'key' when exactly one key is affected", () => {
|
||||
const oneKey = { affected_keys_count: 1, affected_teams_count: 0, sample_keys: ["sk-1"], sample_teams: [] };
|
||||
renderWithProviders(<ImpactPreviewAlert impactResult={oneKey} />);
|
||||
expect(screen.getByText(/1 key\b/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should not show a key section when there are no sample keys", () => {
|
||||
const noKeys = { affected_keys_count: 0, affected_teams_count: 2, sample_keys: [], sample_teams: ["t1", "t2"] };
|
||||
renderWithProviders(<ImpactPreviewAlert impactResult={noKeys} />);
|
||||
expect(screen.queryByText(/^Keys:/i)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,134 @@
|
||||
import { screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { renderWithProviders } from "../../../tests/test-utils";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import * as networking from "../networking";
|
||||
import PolicyInfoView from "./policy_info";
|
||||
import { Policy } from "./types";
|
||||
|
||||
vi.mock("../networking");
|
||||
vi.mock("./pipeline_flow_builder", () => ({
|
||||
PipelineInfoDisplay: () => <div data-testid="pipeline-info" />,
|
||||
}));
|
||||
|
||||
const basePolicy: Policy = {
|
||||
policy_id: "policy-uuid-1",
|
||||
policy_name: "My Test Policy",
|
||||
inherit: null,
|
||||
description: "A test description",
|
||||
guardrails_add: ["guardrail-a"],
|
||||
guardrails_remove: [],
|
||||
condition: null,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
policyId: "policy-uuid-1",
|
||||
onClose: vi.fn(),
|
||||
onEdit: vi.fn(),
|
||||
accessToken: "test-token",
|
||||
isAdmin: true,
|
||||
getPolicy: vi.fn(),
|
||||
};
|
||||
|
||||
describe("PolicyInfoView", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should not show policy content while the fetch is in flight", () => {
|
||||
defaultProps.getPolicy.mockReturnValue(new Promise(() => {}));
|
||||
vi.mocked(networking.getResolvedGuardrails).mockReturnValue(new Promise(() => {}));
|
||||
renderWithProviders(<PolicyInfoView {...defaultProps} />);
|
||||
expect(screen.queryByText("My Test Policy")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show a 'Policy not found' message when getPolicy resolves null", async () => {
|
||||
defaultProps.getPolicy.mockResolvedValue(null);
|
||||
vi.mocked(networking.getResolvedGuardrails).mockResolvedValue({ resolved_guardrails: [] });
|
||||
renderWithProviders(<PolicyInfoView {...defaultProps} />);
|
||||
expect(await screen.findByText(/policy not found/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render the policy name after loading", async () => {
|
||||
defaultProps.getPolicy.mockResolvedValue(basePolicy);
|
||||
vi.mocked(networking.getResolvedGuardrails).mockResolvedValue({ resolved_guardrails: [] });
|
||||
renderWithProviders(<PolicyInfoView {...defaultProps} />);
|
||||
expect(await screen.findByText("My Test Policy")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render the policy ID", async () => {
|
||||
defaultProps.getPolicy.mockResolvedValue(basePolicy);
|
||||
vi.mocked(networking.getResolvedGuardrails).mockResolvedValue({ resolved_guardrails: [] });
|
||||
renderWithProviders(<PolicyInfoView {...defaultProps} />);
|
||||
expect(await screen.findByText("policy-uuid-1")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render guardrails_add tags", async () => {
|
||||
defaultProps.getPolicy.mockResolvedValue(basePolicy);
|
||||
vi.mocked(networking.getResolvedGuardrails).mockResolvedValue({ resolved_guardrails: [] });
|
||||
renderWithProviders(<PolicyInfoView {...defaultProps} />);
|
||||
expect(await screen.findByText("guardrail-a")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should call onClose when the Back to Policies button is clicked", async () => {
|
||||
defaultProps.getPolicy.mockResolvedValue(basePolicy);
|
||||
vi.mocked(networking.getResolvedGuardrails).mockResolvedValue({ resolved_guardrails: [] });
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(<PolicyInfoView {...defaultProps} />);
|
||||
await screen.findByText("My Test Policy");
|
||||
await user.click(screen.getByRole("button", { name: /back to policies/i }));
|
||||
expect(defaultProps.onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should call onEdit with the policy when the Edit Policy button is clicked", async () => {
|
||||
defaultProps.getPolicy.mockResolvedValue(basePolicy);
|
||||
vi.mocked(networking.getResolvedGuardrails).mockResolvedValue({ resolved_guardrails: [] });
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(<PolicyInfoView {...defaultProps} isAdmin />);
|
||||
await screen.findByText("My Test Policy");
|
||||
await user.click(screen.getByRole("button", { name: /edit policy/i }));
|
||||
expect(defaultProps.onEdit).toHaveBeenCalledWith(basePolicy);
|
||||
});
|
||||
|
||||
it("should not show the Edit Policy button for non-admins", async () => {
|
||||
defaultProps.getPolicy.mockResolvedValue(basePolicy);
|
||||
vi.mocked(networking.getResolvedGuardrails).mockResolvedValue({ resolved_guardrails: [] });
|
||||
renderWithProviders(<PolicyInfoView {...defaultProps} isAdmin={false} />);
|
||||
await screen.findByText("My Test Policy");
|
||||
expect(screen.queryByRole("button", { name: /edit policy/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display resolved guardrails when returned from the API", async () => {
|
||||
defaultProps.getPolicy.mockResolvedValue(basePolicy);
|
||||
vi.mocked(networking.getResolvedGuardrails).mockResolvedValue({
|
||||
resolved_guardrails: ["resolved-guardrail-x"],
|
||||
});
|
||||
renderWithProviders(<PolicyInfoView {...defaultProps} />);
|
||||
expect(await screen.findByText("resolved-guardrail-x")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display the model condition tag when present", async () => {
|
||||
const policyWithCondition = { ...basePolicy, condition: { model: "gpt-4" } };
|
||||
defaultProps.getPolicy.mockResolvedValue(policyWithCondition);
|
||||
vi.mocked(networking.getResolvedGuardrails).mockResolvedValue({ resolved_guardrails: [] });
|
||||
renderWithProviders(<PolicyInfoView {...defaultProps} />);
|
||||
expect(await screen.findByText("gpt-4")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show 'No model condition' when condition is null", async () => {
|
||||
defaultProps.getPolicy.mockResolvedValue(basePolicy);
|
||||
vi.mocked(networking.getResolvedGuardrails).mockResolvedValue({ resolved_guardrails: [] });
|
||||
renderWithProviders(<PolicyInfoView {...defaultProps} />);
|
||||
expect(await screen.findByText(/no model condition/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show the formatted created_at date", async () => {
|
||||
const policy = { ...basePolicy, created_at: "2024-06-15T12:00:00Z" };
|
||||
defaultProps.getPolicy.mockResolvedValue(policy);
|
||||
vi.mocked(networking.getResolvedGuardrails).mockResolvedValue({ resolved_guardrails: [] });
|
||||
renderWithProviders(<PolicyInfoView {...defaultProps} />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/2024-06-15/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user