diff --git a/ui/litellm-dashboard/src/components/policies/PolicySelector.test.tsx b/ui/litellm-dashboard/src/components/policies/PolicySelector.test.tsx new file mode 100644 index 0000000000..a05e7e45f5 --- /dev/null +++ b/ui/litellm-dashboard/src/components/policies/PolicySelector.test.tsx @@ -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_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( + + ); + expect(screen.getByRole("combobox")).toBeInTheDocument(); + }); + + it("should fetch policies on mount with the given access token", async () => { + vi.mocked(networking.getPoliciesList).mockResolvedValue({ policies: [] }); + renderWithProviders(); + 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( + + ); + await waitFor(() => { + expect(onPoliciesLoaded).toHaveBeenCalledWith(policies); + }); + }); + + it("should show a disabled placeholder when disabled prop is true", () => { + vi.mocked(networking.getPoliciesList).mockResolvedValue({ policies: [] }); + renderWithProviders( + + ); + expect(screen.getByRole("combobox")).toBeDisabled(); + }); + + it("should not fetch policies when accessToken is empty", () => { + renderWithProviders(); + expect(networking.getPoliciesList).not.toHaveBeenCalled(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/policies/attachment_table.test.tsx b/ui/litellm-dashboard/src/components/policies/attachment_table.test.tsx new file mode 100644 index 0000000000..4372983b71 --- /dev/null +++ b/ui/litellm-dashboard/src/components/policies/attachment_table.test.tsx @@ -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: () =>