mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
test(e2e): assert internal-user navbar identity is scoped to that user (#29077)
* test(e2e): assert internal-user navbar identity is scoped to that user The existing login.spec.ts only checks the admin's navbar identity. This adds the symmetric check for the internal user — verifying the account button + dropdown surface the internal user's email, id, and role, and that no admin-scoped values leak through. * test(e2e): harden navbar identity test per review feedback Locate the user dropdown panel by a data-testid on the popupRender div instead of Ant Design internal + Tailwind class names, so styling refactors no longer risk breaking the identity-scoping assertions. Source the seeded user emails/ids from shared constants (match seed.sql) instead of hardcoding them inline.
This commit is contained in:
parent
892838963c
commit
37e6e2da1c
3 changed files with 55 additions and 1 deletions
|
|
@ -5,6 +5,12 @@ export const INTERNAL_USER_STORAGE_PATH = "internalUser.storageState.json";
|
|||
export const INTERNAL_VIEWER_STORAGE_PATH = "internalViewer.storageState.json";
|
||||
export const TEAM_ADMIN_STORAGE_PATH = "teamAdmin.storageState.json";
|
||||
|
||||
// Seeded user identities (match seed.sql)
|
||||
export const E2E_PROXY_ADMIN_USER_ID = "e2e-proxy-admin";
|
||||
export const E2E_PROXY_ADMIN_EMAIL = "admin@test.local";
|
||||
export const E2E_INTERNAL_USER_ID = "e2e-internal-user";
|
||||
export const E2E_INTERNAL_USER_EMAIL = "internal@test.local";
|
||||
|
||||
// Key aliases for seeded test keys (match seed.sql)
|
||||
export const E2E_UPDATE_LIMITS_KEY_ALIAS = "e2eUpdateLimitsKey";
|
||||
export const E2E_DELETE_KEY_ALIAS = "e2eDeleteKey";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
import { test, expect } from "@playwright/test";
|
||||
import {
|
||||
E2E_INTERNAL_USER_EMAIL,
|
||||
E2E_INTERNAL_USER_ID,
|
||||
E2E_PROXY_ADMIN_EMAIL,
|
||||
E2E_PROXY_ADMIN_USER_ID,
|
||||
INTERNAL_USER_STORAGE_PATH,
|
||||
} from "../../constants";
|
||||
|
||||
const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
|
||||
test.describe("Navbar identity scoping", () => {
|
||||
test.use({ storageState: INTERNAL_USER_STORAGE_PATH });
|
||||
|
||||
test("Internal user navbar dropdown shows their own role and user id, not the admin's", async ({ page }) => {
|
||||
await page.goto("/ui");
|
||||
await expect(page.getByText("Virtual Keys")).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
// The account menu button carries the user's role and email/id in its
|
||||
// aria-label (see UserDropdown.tsx). Match by partial role.
|
||||
const accountButton = page.locator('button[aria-label^="Account menu"]').first();
|
||||
await expect(accountButton).toHaveAttribute("aria-label", /Internal User/, { timeout: 5_000 });
|
||||
await expect(accountButton).toHaveAttribute(
|
||||
"aria-label",
|
||||
new RegExp(
|
||||
`signed in as (${escapeRegExp(E2E_INTERNAL_USER_EMAIL)}|${escapeRegExp(E2E_INTERNAL_USER_ID)})`,
|
||||
),
|
||||
{ timeout: 5_000 },
|
||||
);
|
||||
|
||||
// Open the dropdown (UserDropdown configures trigger=["click"]).
|
||||
await accountButton.click();
|
||||
|
||||
// Locate the panel by its test id (data-testid on the popupRender div in
|
||||
// UserDropdown.tsx) rather than Ant/Tailwind class names, so styling
|
||||
// refactors don't silently break the identity-scoping assertions below.
|
||||
const popup = page.getByTestId("user-dropdown-panel");
|
||||
await expect(popup).toBeVisible({ timeout: 5_000 });
|
||||
|
||||
// The popup must show the internal user's identity — not the seeded
|
||||
// proxy admin's email/id, which would indicate a session/scope leak.
|
||||
await expect(popup.getByText(E2E_INTERNAL_USER_EMAIL)).toBeVisible({ timeout: 5_000 });
|
||||
await expect(popup.getByText(E2E_INTERNAL_USER_ID)).toBeVisible({ timeout: 5_000 });
|
||||
await expect(popup.getByText("Internal User", { exact: true })).toBeVisible({ timeout: 5_000 });
|
||||
await expect(popup.getByText(E2E_PROXY_ADMIN_EMAIL)).toHaveCount(0);
|
||||
await expect(popup.getByText(E2E_PROXY_ADMIN_USER_ID)).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
|
|
@ -221,7 +221,7 @@ const UserDropdown: React.FC<UserDropdownProps> = ({ onLogout }) => {
|
|||
trigger={["click"]}
|
||||
menu={{ items: userItems }}
|
||||
popupRender={(menu) => (
|
||||
<div className="rounded-lg bg-white shadow-lg">
|
||||
<div className="rounded-lg bg-white shadow-lg" data-testid="user-dropdown-panel">
|
||||
{renderUserInfoSection()}
|
||||
<Divider style={{ margin: 0 }} />
|
||||
{React.cloneElement(menu as React.ReactElement, {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue