Files
hermes-control-panel/test/e2e-smoke.cjs
T
2026-06-06 02:15:41 -06:00

101 lines
3.8 KiB
JavaScript

"use strict"
const test = require("node:test")
const assert = require("node:assert/strict")
const BASE_URL = process.env.HERMES_E2E_URL || "http://127.0.0.1:7843"
const ADMIN_USERNAME = process.env.HERMES_ADMIN_USERNAME || "admin"
const ADMIN_PASSWORD = process.env.HERMES_ADMIN_PASSWORD || ""
const PRE_API_URL = process.env.HERMES_PRE_E2E_URL || "http://127.0.0.1:8645"
const POST_API_URL = process.env.HERMES_POST_E2E_URL || "http://127.0.0.1:8646"
test("e2e smoke test — full flow", async (t) => {
if (!process.env.RUN_E2E) {
t.skip("RUN_E2E not set — skipping e2e smoke test")
return
}
// Step 1: Login to control plane
const loginRes = await fetch(`${BASE_URL}/api/admin/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: ADMIN_USERNAME, password: ADMIN_PASSWORD })
})
assert.equal(loginRes.status, 200, "login should succeed")
const cookieHeader = loginRes.headers.get("set-cookie")
assert.ok(cookieHeader, "should set session cookie")
const sessionCookie = cookieHeader.split(";")[0]
// Step 2: Create a pre-only API user
const createRes = await fetch(`${BASE_URL}/api/admin/api-users`, {
method: "POST",
headers: { "Content-Type": "application/json", Cookie: sessionCookie },
body: JSON.stringify({
displayName: "E2E Test User",
allowPre: true,
allowPost: false,
requestsPerMinute: 60,
monthlyTokenLimit: 1000000
})
})
assert.equal(createRes.status, 200, "create API user should succeed")
const { user, plaintextKey } = await createRes.json()
assert.ok(plaintextKey.startsWith("hms_"), "key should start with hms_")
// Step 3: Call pre API successfully
const preRes = await fetch(`${PRE_API_URL}/v1/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${plaintextKey}`
},
body: JSON.stringify({ model: "test", messages: [{ role: "user", content: "hello" }] })
})
assert.ok(preRes.status < 500, "pre API call should not return 5xx (upstream may fail but gateway should respond)")
// Step 4: Call post API and receive 403
const postRes = await fetch(`${POST_API_URL}/v1/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${plaintextKey}`
},
body: JSON.stringify({ model: "test", messages: [{ role: "user", content: "hello" }] })
})
assert.equal(postRes.status, 403, "pre-only key should get 403 on post gateway")
// Step 5: Rotate the key
const rotateRes = await fetch(`${BASE_URL}/api/admin/api-users/${user.id}/rotate`, {
method: "POST",
headers: { Cookie: sessionCookie }
})
assert.equal(rotateRes.status, 200, "rotate should succeed")
const { plaintextKey: newKey } = await rotateRes.json()
// Old key should now get 410
const oldKeyRes = await fetch(`${PRE_API_URL}/v1/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${plaintextKey}`
},
body: JSON.stringify({ model: "test", messages: [] })
})
assert.equal(oldKeyRes.status, 410, "old key should return 410 after rotation")
// Step 6: Download JSONL and verify
const logsRes = await fetch(
`${BASE_URL}/api/admin/logs/download?api_user_id=${user.id}`,
{ headers: { Cookie: sessionCookie } }
)
assert.equal(logsRes.status, 200, "logs download should succeed")
const contentType = logsRes.headers.get("content-type")
assert.match(contentType, /ndjson/, "response should be ndjson")
const body = await logsRes.text()
// There should be at least one JSONL line from the pre API call
const lines = body.trim().split("\n").filter(Boolean)
assert.ok(lines.length >= 1, "should have at least one log entry")
})