mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-16 08:17:51 +00:00
Resolve org alias on teams table
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import React from "react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { fetchAvailableModelsForTeamOrKey } from "./key_team_helpers/fetch_available_models_team_key";
|
||||
import { fetchMCPAccessGroups, getGuardrailsList, teamCreateCall } from "./networking";
|
||||
import OldTeams from "./OldTeams";
|
||||
|
||||
const mockTeamInfoView = vi.fn();
|
||||
const mockUseOrganizations = vi.fn();
|
||||
|
||||
vi.mock("./networking", () => ({
|
||||
teamCreateCall: vi.fn(),
|
||||
@@ -57,6 +60,25 @@ vi.mock("@/components/team/team_info", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/app/(dashboard)/hooks/organizations/useOrganizations", () => ({
|
||||
useOrganizations: () => mockUseOrganizations(),
|
||||
}));
|
||||
|
||||
const createQueryClient = () => {
|
||||
return new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const renderWithQueryClient = (component: React.ReactElement) => {
|
||||
const queryClient = createQueryClient();
|
||||
return render(<QueryClientProvider client={queryClient}>{component}</QueryClientProvider>);
|
||||
};
|
||||
|
||||
describe("OldTeams - handleCreate organization handling", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
@@ -64,6 +86,7 @@ describe("OldTeams - handleCreate organization handling", () => {
|
||||
vi.mocked(fetchAvailableModelsForTeamOrKey).mockResolvedValue([]);
|
||||
vi.mocked(fetchMCPAccessGroups).mockResolvedValue([]);
|
||||
vi.mocked(getGuardrailsList).mockResolvedValue({ guardrails: [] });
|
||||
mockUseOrganizations.mockReturnValue({ data: null });
|
||||
});
|
||||
|
||||
it("should not include organization_id when it's an empty string", async () => {
|
||||
@@ -274,7 +297,8 @@ describe("OldTeams - handleCreate organization handling", () => {
|
||||
});
|
||||
|
||||
it("should clear the delete modal when the cancel button is clicked", async () => {
|
||||
render(
|
||||
mockUseOrganizations.mockReturnValue({ data: [] });
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={[
|
||||
{
|
||||
@@ -310,10 +334,11 @@ describe("OldTeams - handleCreate organization handling", () => {
|
||||
describe("OldTeams - empty state", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockUseOrganizations.mockReturnValue({ data: [] });
|
||||
});
|
||||
|
||||
it("should display empty state message when teams array is empty", () => {
|
||||
render(
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={[]}
|
||||
searchParams={{}}
|
||||
@@ -330,7 +355,7 @@ describe("OldTeams - empty state", () => {
|
||||
});
|
||||
|
||||
it("should display empty state message when teams is null", () => {
|
||||
render(
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={null}
|
||||
searchParams={{}}
|
||||
@@ -347,7 +372,7 @@ describe("OldTeams - empty state", () => {
|
||||
});
|
||||
|
||||
it("should not display empty state when teams array has items", () => {
|
||||
render(
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={[
|
||||
{
|
||||
@@ -511,10 +536,11 @@ describe("OldTeams - premium props", () => {
|
||||
vi.mocked(fetchAvailableModelsForTeamOrKey).mockResolvedValue([]);
|
||||
vi.mocked(fetchMCPAccessGroups).mockResolvedValue([]);
|
||||
vi.mocked(getGuardrailsList).mockResolvedValue({ guardrails: [] });
|
||||
mockUseOrganizations.mockReturnValue({ data: [] });
|
||||
});
|
||||
|
||||
it("passes premiumUser flag to TeamInfoView", async () => {
|
||||
render(
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={[
|
||||
{
|
||||
@@ -558,10 +584,11 @@ describe("OldTeams - premium props", () => {
|
||||
describe("OldTeams - Default Team Settings tab visibility", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockUseOrganizations.mockReturnValue({ data: [] });
|
||||
});
|
||||
|
||||
it("should show Default Team Settings tab for Admin role", () => {
|
||||
render(
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={[
|
||||
{
|
||||
@@ -591,7 +618,7 @@ describe("OldTeams - Default Team Settings tab visibility", () => {
|
||||
});
|
||||
|
||||
it("should show Default Team Settings tab for proxy_admin role", () => {
|
||||
render(
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={[
|
||||
{
|
||||
@@ -621,7 +648,7 @@ describe("OldTeams - Default Team Settings tab visibility", () => {
|
||||
});
|
||||
|
||||
it("should not show Default Team Settings tab for proxy_admin_viewer role", () => {
|
||||
render(
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={[
|
||||
{
|
||||
@@ -651,7 +678,7 @@ describe("OldTeams - Default Team Settings tab visibility", () => {
|
||||
});
|
||||
|
||||
it("should not show Default Team Settings tab for Admin Viewer role", () => {
|
||||
render(
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={[
|
||||
{
|
||||
@@ -685,12 +712,13 @@ describe("OldTeams - models dropdown options", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(fetchAvailableModelsForTeamOrKey).mockResolvedValue(["gpt-4", "gpt-3.5-turbo"]);
|
||||
mockUseOrganizations.mockReturnValue({ data: [] });
|
||||
});
|
||||
|
||||
it("should not render all-proxy-models option in models select", async () => {
|
||||
vi.mocked(fetchAvailableModelsForTeamOrKey).mockResolvedValue(["gpt-4", "gpt-3.5-turbo"]);
|
||||
|
||||
render(
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={[]}
|
||||
searchParams={{}}
|
||||
@@ -718,3 +746,127 @@ describe("OldTeams - models dropdown options", () => {
|
||||
expect(allProxyModelsOption).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("OldTeams - organization alias display", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockUseOrganizations.mockReturnValue({ data: [] });
|
||||
});
|
||||
|
||||
it("should display organization alias instead of organization id", () => {
|
||||
const mockOrganizations = [
|
||||
{
|
||||
organization_id: "org-123",
|
||||
organization_alias: "Test Organization",
|
||||
budget_id: "budget-1",
|
||||
metadata: {},
|
||||
models: [],
|
||||
spend: 0,
|
||||
model_spend: {},
|
||||
created_at: new Date().toISOString(),
|
||||
created_by: "user-1",
|
||||
updated_at: new Date().toISOString(),
|
||||
updated_by: "user-1",
|
||||
litellm_budget_table: null,
|
||||
teams: null,
|
||||
users: null,
|
||||
members: null,
|
||||
},
|
||||
];
|
||||
|
||||
mockUseOrganizations.mockReturnValue({ data: mockOrganizations });
|
||||
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={[
|
||||
{
|
||||
team_id: "1",
|
||||
team_alias: "Test Team",
|
||||
organization_id: "org-123",
|
||||
models: ["gpt-4"],
|
||||
max_budget: 100,
|
||||
budget_duration: "1d",
|
||||
tpm_limit: 1000,
|
||||
rpm_limit: 1000,
|
||||
created_at: new Date().toISOString(),
|
||||
keys: [],
|
||||
members_with_roles: [],
|
||||
},
|
||||
]}
|
||||
searchParams={{}}
|
||||
accessToken="test-token"
|
||||
setTeams={vi.fn()}
|
||||
userID="user-123"
|
||||
userRole="Admin"
|
||||
organizations={mockOrganizations}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText("Test Organization")).toBeInTheDocument();
|
||||
expect(screen.queryByText("org-123")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display organization id when alias is not found", () => {
|
||||
mockUseOrganizations.mockReturnValue({ data: [] });
|
||||
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={[
|
||||
{
|
||||
team_id: "1",
|
||||
team_alias: "Test Team",
|
||||
organization_id: "org-unknown",
|
||||
models: ["gpt-4"],
|
||||
max_budget: 100,
|
||||
budget_duration: "1d",
|
||||
tpm_limit: 1000,
|
||||
rpm_limit: 1000,
|
||||
created_at: new Date().toISOString(),
|
||||
keys: [],
|
||||
members_with_roles: [],
|
||||
},
|
||||
]}
|
||||
searchParams={{}}
|
||||
accessToken="test-token"
|
||||
setTeams={vi.fn()}
|
||||
userID="user-123"
|
||||
userRole="Admin"
|
||||
organizations={[]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText("org-unknown")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display N/A when organization_id is null", () => {
|
||||
mockUseOrganizations.mockReturnValue({ data: [] });
|
||||
|
||||
renderWithQueryClient(
|
||||
<OldTeams
|
||||
teams={[
|
||||
{
|
||||
team_id: "1",
|
||||
team_alias: "Test Team",
|
||||
organization_id: null as any,
|
||||
models: ["gpt-4"],
|
||||
max_budget: 100,
|
||||
budget_duration: "1d",
|
||||
tpm_limit: 1000,
|
||||
rpm_limit: 1000,
|
||||
created_at: new Date().toISOString(),
|
||||
keys: [],
|
||||
members_with_roles: [],
|
||||
},
|
||||
]}
|
||||
searchParams={{}}
|
||||
accessToken="test-token"
|
||||
setTeams={vi.fn()}
|
||||
userID="user-123"
|
||||
userRole="Admin"
|
||||
organizations={[]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText("N/A")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useOrganizations } from "@/app/(dashboard)/hooks/organizations/useOrganizations";
|
||||
import AvailableTeamsPanel from "@/components/team/available_teams";
|
||||
import TeamInfoView from "@/components/team/team_info";
|
||||
import TeamSSOSettings from "@/components/TeamSSOSettings";
|
||||
@@ -149,6 +150,18 @@ const getAdminOrganizations = (
|
||||
return [];
|
||||
};
|
||||
|
||||
const getOrganizationAlias = (
|
||||
organizationId: string | null | undefined,
|
||||
organizations: Organization[] | null | undefined,
|
||||
): string => {
|
||||
if (!organizationId || !organizations) {
|
||||
return organizationId || "N/A";
|
||||
}
|
||||
|
||||
const organization = organizations.find((org) => org.organization_id === organizationId);
|
||||
return organization?.organization_alias || organizationId;
|
||||
};
|
||||
|
||||
// @deprecated
|
||||
const Teams: React.FC<TeamProps> = ({
|
||||
teams,
|
||||
@@ -161,6 +174,7 @@ const Teams: React.FC<TeamProps> = ({
|
||||
premiumUser = false,
|
||||
}) => {
|
||||
console.log(`organizations: ${JSON.stringify(organizations)}`);
|
||||
const { data: organizationsData } = useOrganizations(accessToken);
|
||||
const [lastRefreshed, setLastRefreshed] = useState("");
|
||||
const [currentOrg, setCurrentOrg] = useState<Organization | null>(null);
|
||||
const [currentOrgForCreateTeam, setCurrentOrgForCreateTeam] = useState<Organization | null>(null);
|
||||
@@ -940,7 +954,9 @@ const Teams: React.FC<TeamProps> = ({
|
||||
</div>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>{team.organization_id}</TableCell>
|
||||
<TableCell>
|
||||
{getOrganizationAlias(team.organization_id, organizationsData || organizations)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Text>
|
||||
{perTeamInfo &&
|
||||
|
||||
Reference in New Issue
Block a user