Resolve org alias on teams table

This commit is contained in:
yuneng-jiang
2025-12-23 18:16:30 -08:00
parent d98defe63f
commit 8b4c2da9ea
2 changed files with 179 additions and 11 deletions
@@ -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 &&