From 4e7b0fcfdfbbec35843ceae2b52de4599851cc94 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 5 Mar 2026 14:00:46 -0800 Subject: [PATCH] [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 --- .../policies/PolicySelector.test.tsx | 121 +++++++++++++++ .../policies/attachment_table.test.tsx | 142 ++++++++++++++++++ .../policies/build_attachment_data.test.ts | 82 ++++++++++ .../policies/impact_preview_alert.test.tsx | 78 ++++++++++ .../components/policies/policy_info.test.tsx | 134 +++++++++++++++++ 5 files changed, 557 insertions(+) create mode 100644 ui/litellm-dashboard/src/components/policies/PolicySelector.test.tsx create mode 100644 ui/litellm-dashboard/src/components/policies/attachment_table.test.tsx create mode 100644 ui/litellm-dashboard/src/components/policies/build_attachment_data.test.ts create mode 100644 ui/litellm-dashboard/src/components/policies/impact_preview_alert.test.tsx create mode 100644 ui/litellm-dashboard/src/components/policies/policy_info.test.tsx 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: () =>