feat: v2
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { listAuditEvents } from "../../command/audit";
|
||||
import { main } from "../../command/main";
|
||||
import { getOperation, listOperations } from "../../command/operations";
|
||||
import {
|
||||
addUser,
|
||||
deleteUser,
|
||||
listUsers,
|
||||
revokeUserSessions,
|
||||
setUserDisabled,
|
||||
updateUser,
|
||||
} from "../../command/users";
|
||||
import type { ApiRequestInit } from "../../lib/api";
|
||||
|
||||
type Call = { path: string; init?: ApiRequestInit };
|
||||
|
||||
function requestReturning<T>(result: T, calls: Call[]) {
|
||||
return async <R>(path: string, init?: ApiRequestInit): Promise<R> => {
|
||||
calls.push({ path, init });
|
||||
return result as unknown as R;
|
||||
};
|
||||
}
|
||||
|
||||
const user = {
|
||||
username: "alice",
|
||||
roles: ["admin"],
|
||||
disabled: false,
|
||||
updatedAt: "2026-09-02T10:00:00.000Z",
|
||||
};
|
||||
|
||||
describe("user administration commands", () => {
|
||||
test("lists and creates users through authenticated API routes", async () => {
|
||||
const calls: Call[] = [];
|
||||
expect(
|
||||
await listUsers(requestReturning({ items: [user] }, calls)),
|
||||
).toContain("alice");
|
||||
expect(
|
||||
await addUser(
|
||||
"alice",
|
||||
"secret",
|
||||
["admin"],
|
||||
requestReturning(user, calls),
|
||||
),
|
||||
).toContain("admin");
|
||||
|
||||
expect(calls).toEqual([
|
||||
{ path: "/users", init: undefined },
|
||||
{
|
||||
path: "/users",
|
||||
init: {
|
||||
method: "POST",
|
||||
json: { username: "alice", password: "secret", roles: ["admin"] },
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("updates roles, passwords, and enabled state with PATCH", async () => {
|
||||
const calls: Call[] = [];
|
||||
const request = requestReturning(user, calls);
|
||||
await updateUser(
|
||||
"alice/example",
|
||||
{ roles: ["operator"], password: "new" },
|
||||
request,
|
||||
);
|
||||
await setUserDisabled("alice", true, request);
|
||||
await setUserDisabled("alice", false, request);
|
||||
|
||||
expect(calls).toEqual([
|
||||
{
|
||||
path: "/users/alice%2Fexample",
|
||||
init: {
|
||||
method: "PATCH",
|
||||
json: { roles: ["operator"], password: "new" },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/users/alice",
|
||||
init: { method: "PATCH", json: { disabled: true } },
|
||||
},
|
||||
{
|
||||
path: "/users/alice",
|
||||
init: { method: "PATCH", json: { disabled: false } },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("requires confirmation for deletion and supports session revocation", async () => {
|
||||
const calls: Call[] = [];
|
||||
const request = requestReturning(undefined, calls);
|
||||
expect(await deleteUser("alice", false, request)).toBe(
|
||||
"Deletion cancelled",
|
||||
);
|
||||
expect(await deleteUser("alice", true, request)).toBe("Deleted user alice");
|
||||
expect(
|
||||
await revokeUserSessions(
|
||||
"alice",
|
||||
requestReturning({ username: "alice", revoked: 2 }, calls),
|
||||
),
|
||||
).toBe("Revoked 2 sessions for alice");
|
||||
|
||||
expect(calls).toEqual([
|
||||
{ path: "/users/alice", init: { method: "DELETE" } },
|
||||
{
|
||||
path: "/users/alice/sessions/revoke",
|
||||
init: { method: "POST" },
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
const operation = {
|
||||
metadata: {
|
||||
name: "operation-1",
|
||||
creationTimestamp: "2026-09-02T10:00:00.000Z",
|
||||
},
|
||||
spec: { workspaceId: "team/shop", action: "restart" },
|
||||
status: { state: "succeeded", result: { deployments: ["web"] } },
|
||||
};
|
||||
|
||||
describe("operations and audit commands", () => {
|
||||
test("lists filtered operations and gets operation details", async () => {
|
||||
const calls: Call[] = [];
|
||||
const listing = await listOperations(
|
||||
"team/shop",
|
||||
requestReturning({ items: [operation] }, calls),
|
||||
);
|
||||
const detail = await getOperation(
|
||||
"operation/1",
|
||||
requestReturning(operation, calls),
|
||||
);
|
||||
|
||||
expect(listing).toContain("restart");
|
||||
expect(detail).toContain('Result: {"deployments":["web"]}');
|
||||
expect(calls).toEqual([
|
||||
{ path: "/operations?workspaceId=team%2Fshop", init: undefined },
|
||||
{ path: "/operations/operation%2F1", init: undefined },
|
||||
]);
|
||||
});
|
||||
|
||||
test("lists audit events with an optional workspace filter", async () => {
|
||||
const calls: Call[] = [];
|
||||
const output = await listAuditEvents(
|
||||
"team/shop",
|
||||
requestReturning(
|
||||
{
|
||||
items: [
|
||||
{
|
||||
metadata: {
|
||||
name: "audit-1",
|
||||
creationTimestamp: "2026-09-02T10:00:00.000Z",
|
||||
},
|
||||
spec: {
|
||||
actor: { username: "alice" },
|
||||
action: "workspace.restart",
|
||||
workspaceId: "team/shop",
|
||||
outcome: "success",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
calls,
|
||||
),
|
||||
);
|
||||
|
||||
expect(output).toContain("alice");
|
||||
expect(output).toContain("workspace.restart");
|
||||
expect(calls).toEqual([
|
||||
{ path: "/audit?workspaceId=team%2Fshop", init: undefined },
|
||||
]);
|
||||
});
|
||||
|
||||
test("registers administration command groups", async () => {
|
||||
const subCommands = await Promise.resolve(main.subCommands);
|
||||
expect(Object.keys(subCommands ?? {})).toEqual(
|
||||
expect.arrayContaining(["users", "operations", "audit"]),
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user