test(e2e): forward LITELLM_LICENSE to UI e2e proxy (#28398)
* test(e2e): forward LITELLM_LICENSE to UI e2e proxy The UI e2e job ran without LITELLM_LICENSE, so premium_user was always false in the issued login JWT and premium-gated UI surfaces (Team-BYOK Model switch, etc.) couldn't be driven through the UI. Forward the env var from run_e2e.sh and the CircleCI e2e_ui_testing job, and add a sanity test that decodes the admin storage state token and asserts premium_user=true so the wiring fails loudly if it ever regresses. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Update ui/litellm-dashboard/e2e_tests/tests/proxy-admin/license.spec.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
parent
9fcd424318
commit
07bcd2c19e
@ -2477,10 +2477,15 @@ jobs:
|
|||||||
DISABLE_SCHEMA_UPDATE: "true"
|
DISABLE_SCHEMA_UPDATE: "true"
|
||||||
SERVER_ROOT_PATH: ""
|
SERVER_ROOT_PATH: ""
|
||||||
PROXY_LOGOUT_URL: ""
|
PROXY_LOGOUT_URL: ""
|
||||||
|
# LITELLM_LICENSE is forwarded from the project env so premium-gated
|
||||||
|
# UI flows can be exercised. license.spec.ts asserts the resulting
|
||||||
|
# JWT carries premium_user=true; if it ever stops being passed, that
|
||||||
|
# test fails loudly rather than silently regressing premium coverage.
|
||||||
command: |
|
command: |
|
||||||
uv run --no-sync python -m litellm.proxy.proxy_cli \
|
LITELLM_LICENSE="$LITELLM_LICENSE" \
|
||||||
--config ui/litellm-dashboard/e2e_tests/fixtures/config.yml \
|
uv run --no-sync python -m litellm.proxy.proxy_cli \
|
||||||
--port 4000
|
--config ui/litellm-dashboard/e2e_tests/fixtures/config.yml \
|
||||||
|
--port 4000
|
||||||
background: true
|
background: true
|
||||||
- run:
|
- run:
|
||||||
name: Wait for proxy to be ready
|
name: Wait for proxy to be ready
|
||||||
@ -2497,9 +2502,12 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
- run:
|
- run:
|
||||||
name: Run Playwright E2E tests
|
name: Run Playwright E2E tests
|
||||||
|
# Forward LITELLM_LICENSE so license.spec.ts can detect that the
|
||||||
|
# proxy was launched with a license and assert premium_user=true.
|
||||||
command: |
|
command: |
|
||||||
cd ui/litellm-dashboard
|
cd ui/litellm-dashboard
|
||||||
npx playwright test --config e2e_tests/playwright.config.ts
|
LITELLM_LICENSE="$LITELLM_LICENSE" \
|
||||||
|
npx playwright test --config e2e_tests/playwright.config.ts
|
||||||
no_output_timeout: 10m
|
no_output_timeout: 10m
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: ui/litellm-dashboard/test-results
|
path: ui/litellm-dashboard/test-results
|
||||||
|
|||||||
@ -95,6 +95,10 @@ export DISABLE_SCHEMA_UPDATE="true"
|
|||||||
export SERVER_ROOT_PATH=""
|
export SERVER_ROOT_PATH=""
|
||||||
# Prevent logout from redirecting to an external URL
|
# Prevent logout from redirecting to an external URL
|
||||||
export PROXY_LOGOUT_URL=""
|
export PROXY_LOGOUT_URL=""
|
||||||
|
# Forward LITELLM_LICENSE if set in the outer env so premium-gated UI flows
|
||||||
|
# (e.g. Team-BYOK Model switch) can be exercised. Tests that depend on a
|
||||||
|
# premium proxy gate themselves on process.env.LITELLM_LICENSE.
|
||||||
|
export LITELLM_LICENSE="${LITELLM_LICENSE:-}"
|
||||||
|
|
||||||
# --- Rebuild UI from source ---
|
# --- Rebuild UI from source ---
|
||||||
echo "=== Building UI from source ==="
|
echo "=== Building UI from source ==="
|
||||||
|
|||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { ADMIN_STORAGE_PATH } from "../../constants";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanity check that LITELLM_LICENSE is being forwarded to the proxy when set
|
||||||
|
* in the environment (e.g. CircleCI's `e2e_ui_testing` job). The login JWT's
|
||||||
|
* `premium_user` claim is the same value the dashboard reads to enable
|
||||||
|
* premium-gated UI surfaces (Team-BYOK switch, etc.), so asserting it here
|
||||||
|
* catches any future regression where the env var stops being plumbed
|
||||||
|
* through `run_e2e.sh` / `.circleci/config.yml`.
|
||||||
|
*
|
||||||
|
* Skips locally when no license is configured.
|
||||||
|
*/
|
||||||
|
test.describe("Premium license wiring", () => {
|
||||||
|
test("admin session JWT carries premium_user=true when LITELLM_LICENSE is set", () => {
|
||||||
|
test.skip(
|
||||||
|
!process.env.LITELLM_LICENSE,
|
||||||
|
"LITELLM_LICENSE not set in test env — proxy is running unlicensed",
|
||||||
|
);
|
||||||
|
|
||||||
|
const storage = JSON.parse(fs.readFileSync(ADMIN_STORAGE_PATH, "utf-8"));
|
||||||
|
const tokenCookie = storage.cookies?.find((c: { name: string }) => c.name === "token");
|
||||||
|
expect(tokenCookie, "token cookie missing from admin storage state").toBeDefined();
|
||||||
|
|
||||||
|
// Decode the JWT payload (no signature check — we trust globalSetup ran
|
||||||
|
// against our own proxy). Payload is the middle base64url segment.
|
||||||
|
const jwtParts = tokenCookie.value.split(".");
|
||||||
|
expect(jwtParts.length, "token cookie is not a 3-part JWT").toBe(3);
|
||||||
|
const [, payloadB64] = jwtParts;
|
||||||
|
const payload = JSON.parse(
|
||||||
|
Buffer.from(payloadB64, "base64url").toString("utf-8"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(payload.premium_user).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user