Files
yuan-lab-llm--clawmanager/e2e/tests/instances/session-usage-admin.spec.ts
T
heranran 71c7672545 feat: Skill Hub hardening with session usage tracking and egress governance (#163)
* feat(skill-hub): add Skill Hub catalog, publish flow, and lite materialize pipeline

Introduce Skill Hub for browsing, importing, publishing, and installing skills, with lite instance package materialization and runtime sync support.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(skill-hub): remove token-governance hooks from Skill Hub PR

Strip validateManagedRuntimeEnvironmentOverrides, network lock policy sync,
and egress proxy audit wiring that belong to the upcoming token-usage work,
so the Skill Hub branch compiles independently.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(skill-hub): update migration number in materialize docs

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(skill-hub): make RuntimeAgentClient test stub and hub tests compile-safe

Add ResyncInstanceSkills to the runtime pool handler fake client, and harden
skill hub payload helpers/tests against nil storage/instance repos so go test passes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add Skill Hub hardening with session usage tracking and egress governance

Unify Skill Hub runtime sync improvements with session-token observability,
egress network policy, and admin/instance usage reporting for reopenable PR.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(skill-hub): repair CI tests and nested skill install

* fix(ci): restore release deployment configuration

---------

Co-authored-by: heshengran <heshengran@ieisystem.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-17 12:37:01 +08:00

96 lines
3.6 KiB
TypeScript

import { expect, test } from "../../fixtures/test.js";
import { env } from "../../fixtures/env.js";
import {
getAdminSessionUsageOverview,
getInstanceSessionUsage,
listInstances,
login,
registerUser,
} from "../../fixtures/apiClient.js";
import { users } from "../../fixtures/users.js";
function firstManagedRuntimeInstance(instances: Awaited<ReturnType<typeof listInstances>>) {
return instances.instances.find(
(instance) =>
(instance.type === "openclaw" || instance.type === "hermes") &&
instance.status !== "deleting",
);
}
test("@p1 admin session usage overview returns structured payload", async ({ request }) => {
const accessToken = (await login(request, users.admin)).access_token;
const overview = await getAdminSessionUsageOverview(request, accessToken, {
page: 1,
limit: 20,
});
expect(overview.summary).toBeTruthy();
expect(Array.isArray(overview.items)).toBe(true);
expect(typeof overview.total).toBe("number");
expect(typeof overview.page).toBe("number");
expect(typeof overview.limit).toBe("number");
});
test("@p1 admin session usage overview rejects invalid since timestamp", async ({ request }) => {
const accessToken = (await login(request, users.admin)).access_token;
const response = await request.get(`${env.backendUrl}/admin/session-usage/overview`, {
headers: { Authorization: `Bearer ${accessToken}` },
params: { since: "not-a-date" },
});
expect(response.status()).toBe(400);
});
test("@p1 instance session usage accepts since query parameter", async ({ request }) => {
const accessToken = (await login(request, users.admin)).access_token;
const instances = await listInstances(request, accessToken, { limit: 100 });
const instance = firstManagedRuntimeInstance(instances);
test.skip(!instance, "No openclaw/hermes instance available for since-filter test");
const since = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
const data = await getInstanceSessionUsage(request, accessToken, instance!.id, {
page: 1,
limit: 20,
since,
});
expect(data.summary).toBeTruthy();
expect(Array.isArray(data.items)).toBe(true);
});
test("@p1 instance session usage rejects invalid since timestamp", async ({ request }) => {
const accessToken = (await login(request, users.admin)).access_token;
const instances = await listInstances(request, accessToken, { limit: 100 });
const instance = firstManagedRuntimeInstance(instances);
test.skip(!instance, "No openclaw/hermes instance available for invalid since test");
const response = await request.get(
`${env.backendUrl}/instances/${instance!.id}/session-usage`,
{
headers: { Authorization: `Bearer ${accessToken}` },
params: { since: "bad-timestamp" },
},
);
expect(response.status()).toBe(400);
});
test("@p2 non-admin cannot access session usage overview", async ({ request }) => {
await registerUser(request, users.user);
const accessToken = (await login(request, users.user)).access_token;
const response = await request.get(`${env.backendUrl}/admin/session-usage/overview`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
expect(response.status()).toBe(403);
});
test("@p1 session usage rejects until before since", async ({ request }) => {
const accessToken = (await login(request, users.admin)).access_token;
const response = await request.get(`${env.backendUrl}/admin/session-usage/overview`, {
headers: { Authorization: `Bearer ${accessToken}` },
params: {
since: "2026-07-10T00:00:00Z",
until: "2026-07-01T00:00:00Z",
},
});
expect(response.status()).toBe(400);
});