test(e2e/ui): give the seeded users passwords that pass the default password policy

The default password policy from #39381 (12+ chars with upper, lower, number, and special) makes globalSetup's POST /user/update with password "test" fail with a 400, so every role login in the UI e2e suite has been failing since that merge. Seed the roles, the noteam@test.local login, and the second-admin spec with policy-compliant passwords and update the scrypt hash in seed.sql to match.
This commit is contained in:
mateo-berri 2026-09-02 16:58:53 -07:00
parent c9562e1dd9
commit e641864e6f
5 changed files with 19 additions and 16 deletions

View file

@ -30,6 +30,7 @@ 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";
export const E2E_TEAM_ADMIN_USER_ID = "e2e-team-admin";
export const E2E_SEEDED_USER_PASSWORD = "E2e-Test-Pass-2026!";
// Key aliases for seeded test keys (match seed.sql)
export const E2E_UPDATE_LIMITS_KEY_ALIAS = "e2eUpdateLimitsKey";

View file

@ -24,18 +24,18 @@ INSERT INTO "LiteLLM_OrganizationTable" (
'e2e-proxy-admin', 'e2e-proxy-admin'
);
-- 4. Users (password hash is scrypt of "test")
-- 4. Users (password hash is scrypt of E2E_SEEDED_USER_PASSWORD from constants.ts)
INSERT INTO "LiteLLM_UserTable" ("user_id", "user_email", "user_role", "teams", "password")
VALUES
('e2e-proxy-admin', 'admin@test.local', 'proxy_admin', '{"e2e-team-crud"}', 'scrypt:MU5CcTAi6rVK1HfY1rVPEWq6r4sxg837eq9dG4n5Q6BhDJ44442+seC6LAhLEAYr'),
('e2e-admin-viewer', 'adminviewer@test.local', 'proxy_admin_viewer', '{}', 'scrypt:MU5CcTAi6rVK1HfY1rVPEWq6r4sxg837eq9dG4n5Q6BhDJ44442+seC6LAhLEAYr'),
('e2e-internal-user', 'internal@test.local', 'internal_user', '{"e2e-team-crud","e2e-team-org","e2e-team-keygen"}', 'scrypt:MU5CcTAi6rVK1HfY1rVPEWq6r4sxg837eq9dG4n5Q6BhDJ44442+seC6LAhLEAYr'),
('e2e-internal-viewer', 'viewer@test.local', 'internal_user_viewer', '{"e2e-team-crud"}', 'scrypt:MU5CcTAi6rVK1HfY1rVPEWq6r4sxg837eq9dG4n5Q6BhDJ44442+seC6LAhLEAYr'),
('e2e-team-admin', 'teamadmin@test.local', 'internal_user', '{"e2e-team-crud","e2e-team-delete"}', 'scrypt:MU5CcTAi6rVK1HfY1rVPEWq6r4sxg837eq9dG4n5Q6BhDJ44442+seC6LAhLEAYr'),
('e2e-invitable-user', 'invitable@test.local', 'internal_user', '{}', 'scrypt:MU5CcTAi6rVK1HfY1rVPEWq6r4sxg837eq9dG4n5Q6BhDJ44442+seC6LAhLEAYr'),
('e2e-internal-noteam', 'noteam@test.local', 'internal_user', '{}', 'scrypt:MU5CcTAi6rVK1HfY1rVPEWq6r4sxg837eq9dG4n5Q6BhDJ44442+seC6LAhLEAYr'),
('e2e-invitable-by-team-admin', 'invitable-team@test.local', 'internal_user', '{}', 'scrypt:MU5CcTAi6rVK1HfY1rVPEWq6r4sxg837eq9dG4n5Q6BhDJ44442+seC6LAhLEAYr'),
('e2e-removable-member', 'removable@test.local', 'internal_user', '{"e2e-team-crud"}', 'scrypt:MU5CcTAi6rVK1HfY1rVPEWq6r4sxg837eq9dG4n5Q6BhDJ44442+seC6LAhLEAYr');
('e2e-proxy-admin', 'admin@test.local', 'proxy_admin', '{"e2e-team-crud"}', 'scrypt:KdnTJwPb3gswdqSznPE5CC6apeFIMycd6BG7yRWndZa3QZPcVs37y7jvrQCaPUNq'),
('e2e-admin-viewer', 'adminviewer@test.local', 'proxy_admin_viewer', '{}', 'scrypt:KdnTJwPb3gswdqSznPE5CC6apeFIMycd6BG7yRWndZa3QZPcVs37y7jvrQCaPUNq'),
('e2e-internal-user', 'internal@test.local', 'internal_user', '{"e2e-team-crud","e2e-team-org","e2e-team-keygen"}', 'scrypt:KdnTJwPb3gswdqSznPE5CC6apeFIMycd6BG7yRWndZa3QZPcVs37y7jvrQCaPUNq'),
('e2e-internal-viewer', 'viewer@test.local', 'internal_user_viewer', '{"e2e-team-crud"}', 'scrypt:KdnTJwPb3gswdqSznPE5CC6apeFIMycd6BG7yRWndZa3QZPcVs37y7jvrQCaPUNq'),
('e2e-team-admin', 'teamadmin@test.local', 'internal_user', '{"e2e-team-crud","e2e-team-delete"}', 'scrypt:KdnTJwPb3gswdqSznPE5CC6apeFIMycd6BG7yRWndZa3QZPcVs37y7jvrQCaPUNq'),
('e2e-invitable-user', 'invitable@test.local', 'internal_user', '{}', 'scrypt:KdnTJwPb3gswdqSznPE5CC6apeFIMycd6BG7yRWndZa3QZPcVs37y7jvrQCaPUNq'),
('e2e-internal-noteam', 'noteam@test.local', 'internal_user', '{}', 'scrypt:KdnTJwPb3gswdqSznPE5CC6apeFIMycd6BG7yRWndZa3QZPcVs37y7jvrQCaPUNq'),
('e2e-invitable-by-team-admin', 'invitable-team@test.local', 'internal_user', '{}', 'scrypt:KdnTJwPb3gswdqSznPE5CC6apeFIMycd6BG7yRWndZa3QZPcVs37y7jvrQCaPUNq'),
('e2e-removable-member', 'removable@test.local', 'internal_user', '{"e2e-team-crud"}', 'scrypt:KdnTJwPb3gswdqSznPE5CC6apeFIMycd6BG7yRWndZa3QZPcVs37y7jvrQCaPUNq');
-- 5. Teams (members_with_roles is required JSON)
INSERT INTO "LiteLLM_TeamTable" (

View file

@ -1,6 +1,7 @@
import {
ADMIN_STORAGE_PATH,
ADMIN_VIEWER_STORAGE_PATH,
E2E_SEEDED_USER_PASSWORD,
INTERNAL_USER_STORAGE_PATH,
INTERNAL_VIEWER_STORAGE_PATH,
TEAM_ADMIN_STORAGE_PATH,
@ -23,22 +24,22 @@ export const users: Record<Role, { email: string; password: string; seedApiRole?
},
[Role.ProxyAdminViewer]: {
email: "adminviewer@test.local",
password: "test",
password: E2E_SEEDED_USER_PASSWORD,
seedApiRole: "proxy_admin_viewer",
},
[Role.InternalUser]: {
email: "internal@test.local",
password: "test",
password: E2E_SEEDED_USER_PASSWORD,
seedApiRole: "internal_user",
},
[Role.InternalUserViewer]: {
email: "viewer@test.local",
password: "test",
password: E2E_SEEDED_USER_PASSWORD,
seedApiRole: "internal_user_viewer",
},
[Role.TeamAdmin]: {
email: "teamadmin@test.local",
password: "test",
password: E2E_SEEDED_USER_PASSWORD,
seedApiRole: "internal_user",
},
};

View file

@ -1,6 +1,7 @@
import { test, expect } from "@playwright/test";
import { navigateToPage } from "../../helpers/navigation";
import { Page } from "../../fixtures/pages";
import { E2E_SEEDED_USER_PASSWORD } from "../../constants";
/**
* Logs in fresh inside the test rather than reusing a stored session because
@ -15,7 +16,7 @@ test.describe("Internal User with no team memberships", () => {
// Log in via the form as the no-team seeded user.
await page.goto("/ui/login");
await page.getByPlaceholder("Enter your username").fill("noteam@test.local");
await page.getByPlaceholder("Enter your password").fill("test");
await page.getByPlaceholder("Enter your password").fill(E2E_SEEDED_USER_PASSWORD);
await page.getByRole("button", { name: "Login", exact: true }).click();
await expect(page.getByRole("complementary").getByText("Virtual Keys")).toBeVisible({ timeout: 30_000 });
expect(new URL(page.url()).pathname).not.toMatch(/\/connect$/);

View file

@ -10,7 +10,7 @@ test.describe("Second proxy admin", () => {
test("an invited admin can log in, mint a key, and call a model with it", async ({ page, browser, request }) => {
const suffix = Date.now();
const email = `second-admin-${suffix}@test.local`;
const password = "e2e-second-admin-password";
const password = "E2e-Second-Admin-Pass-1!";
const auth = { Authorization: `Bearer ${masterKey()}` };
const inviteAdminUser = async (): Promise<string> => {