114 lines
8.0 KiB
TypeScript
114 lines
8.0 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { startManagerServer } from "../../mgr/server.js";
|
|
import { MemoryAgentRunStore } from "../../mgr/store.js";
|
|
import { ManagerClient } from "../../mgr/client.js";
|
|
import { buildSessionPvcSpec, createSessionPvc, deleteSessionPvc, getSessionPvcSummary, refreshSessionPvcSummary, runSessionStorageGc, sessionPvcLabelValueFor, sessionPvcNameFor, sanitizeSessionIdForPvc } from "../../mgr/session-pvc.js";
|
|
import type { KubectlHandler, SessionPvcOptions } from "../../mgr/session-pvc.js";
|
|
import type { SelfTestCase } from "../harness.js";
|
|
|
|
function makeFakeKubectl(): KubectlHandler {
|
|
return ({ args, stdin }) => {
|
|
if (args[0] === "create") {
|
|
const manifest = stdin ? JSON.parse(stdin) : {};
|
|
return { stdout: JSON.stringify({ apiVersion: "v1", kind: "PersistentVolumeClaim", metadata: { name: manifest.metadata?.name, namespace: manifest.metadata?.namespace }, status: { phase: "Bound" } }), stderr: "", exitCode: 0 };
|
|
}
|
|
if (args[0] === "get") {
|
|
return { stdout: JSON.stringify({ apiVersion: "v1", kind: "PersistentVolumeClaim", metadata: { name: args[2] }, status: { phase: "Bound", capacity: { storage: "1Gi" } } }), stderr: "", exitCode: 0 };
|
|
}
|
|
if (args[0] === "delete") {
|
|
return { stdout: "", stderr: "", exitCode: 0 };
|
|
}
|
|
return { stdout: "{}", stderr: "", exitCode: 0 };
|
|
};
|
|
}
|
|
|
|
const selfTest: SelfTestCase = async () => {
|
|
const fakeKubectl = makeFakeKubectl();
|
|
const opts: SessionPvcOptions = { kubectlHandler: fakeKubectl, defaultCodexRolloutSubdir: "sessions" };
|
|
const store = new MemoryAgentRunStore();
|
|
const sessionId = "sess_test_pvc_001";
|
|
store.upsertSession({ sessionId, tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex", conversationId: null, threadId: null, metadata: {}, expiresAt: new Date(Date.now() + 86_400_000).toISOString(), codexRolloutSubdir: "sessions" });
|
|
const summary = await createSessionPvc({ store, sessionId, options: opts });
|
|
assert.equal(summary.pvcName, sessionPvcNameFor(sessionId));
|
|
assert.equal(summary.pvcPhase, "Bound");
|
|
assert.equal(summary.codexRolloutSubdir, "sessions");
|
|
assert.equal(sessionPvcNameFor("sess_with_underscores_001"), "agentrun-v01-session-sess-with-underscores-001");
|
|
assert.equal(sessionPvcNameFor("Sess.UPPER.001"), "agentrun-v01-session-sess-upper-001");
|
|
assert.equal(sessionPvcNameFor("---"), "agentrun-v01-session-default");
|
|
const longSessionId = "ses_agentrun_dsflash_go_0606efb7_60fd_4f38_9532_398b46e54808-reset-trcmqlrsnc2r";
|
|
const longSessionLabel = sessionPvcLabelValueFor(longSessionId);
|
|
assert.ok(longSessionLabel.length <= 63);
|
|
assert.notEqual(longSessionLabel, longSessionId);
|
|
assert.match(longSessionLabel, /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/u);
|
|
const previousRuntimeNamespace = process.env.AGENTRUN_RUNTIME_NAMESPACE;
|
|
try {
|
|
delete process.env.AGENTRUN_RUNTIME_NAMESPACE;
|
|
assert.equal(buildSessionPvcSpec({ sessionId: "sess_default_namespace", options: opts }).namespace, "agentrun-v01");
|
|
process.env.AGENTRUN_RUNTIME_NAMESPACE = "agentrun-v02";
|
|
assert.equal(buildSessionPvcSpec({ sessionId: "sess_runtime_namespace", options: opts }).namespace, "agentrun-v02");
|
|
} finally {
|
|
if (previousRuntimeNamespace === undefined) delete process.env.AGENTRUN_RUNTIME_NAMESPACE;
|
|
else process.env.AGENTRUN_RUNTIME_NAMESPACE = previousRuntimeNamespace;
|
|
}
|
|
const after = await store.getSession(sessionId);
|
|
assert.equal(after?.storageKind, "pvc");
|
|
assert.equal(after?.storagePvcName, summary.pvcName);
|
|
|
|
const readSummary = await getSessionPvcSummary({ store, sessionId, options: opts });
|
|
assert.equal(readSummary.pvcName, summary.pvcName);
|
|
assert.equal(readSummary.pvcPhase, "Bound");
|
|
assert.equal(readSummary.storageSizeBytes, 1024 * 1024 * 1024);
|
|
|
|
await refreshSessionPvcSummary({ store, sessionId, summary: { ...summary, storageFilesCount: 7, storageSha256: "abc123" }, options: opts });
|
|
const refreshed = await store.getSession(sessionId);
|
|
assert.equal(refreshed?.storageFilesCount, 7);
|
|
assert.equal(refreshed?.storageSha256, "abc123");
|
|
|
|
const evicted = await deleteSessionPvc({ store, sessionId, options: opts });
|
|
assert.equal(evicted.storageKind, "evicted");
|
|
const evictedSession = await store.getSession(sessionId);
|
|
assert.equal(evictedSession?.storageKind, "evicted");
|
|
assert.ok(evictedSession?.storageEvictedAt);
|
|
|
|
const now = Date.now();
|
|
const expiredId = "sess_expired_001";
|
|
const activeId = "sess_active_001";
|
|
store.upsertSession({ sessionId: expiredId, tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex", conversationId: null, threadId: null, metadata: {}, expiresAt: new Date(now - 60_000).toISOString(), codexRolloutSubdir: "sessions" });
|
|
await store.refreshSessionStorage({ sessionId: expiredId, storageKind: "pvc", pvcName: sessionPvcNameFor(expiredId), codexRolloutSubdir: "sessions" });
|
|
store.upsertSession({ sessionId: activeId, tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex", conversationId: null, threadId: null, metadata: {}, expiresAt: new Date(now - 60_000).toISOString(), codexRolloutSubdir: "sessions" });
|
|
await store.refreshSessionStorage({ sessionId: activeId, storageKind: "pvc", pvcName: sessionPvcNameFor(activeId), codexRolloutSubdir: "sessions" });
|
|
const activeRun = store.createRun({ tenantId: "hwlab", projectId: "pikasTech/HWLAB", workspaceRef: { kind: "opaque", path: "." }, providerId: "G14", backendProfile: "codex", executionPolicy: { sandbox: "worktree", approval: "never", timeoutMs: 600_000, network: "restricted", secretScope: {} }, traceSink: null, sessionRef: { sessionId: activeId }, resourceBundleRef: null });
|
|
store.createCommand(activeRun.id, { type: "turn", payload: { prompt: "test" } });
|
|
const cycle = await runSessionStorageGc({ store, options: opts, now, maxSessions: 10 });
|
|
assert.ok(cycle.scanned >= 1);
|
|
assert.ok(cycle.deleted >= 1);
|
|
const expiredAfter = await store.getSession(expiredId);
|
|
assert.equal(expiredAfter?.storageKind, "evicted");
|
|
const activeAfter = await store.getSession(activeId);
|
|
assert.equal(activeAfter?.storageKind, "pvc");
|
|
|
|
const restStore = new MemoryAgentRunStore();
|
|
const server = await startManagerServer({ port: 0, host: "127.0.0.1", sourceCommit: "self-test", store: restStore, sessionPvcOptions: { kubectlHandler: fakeKubectl } });
|
|
try {
|
|
const client = new ManagerClient(server.baseUrl);
|
|
const create = await client.post("/api/v1/sessions", { sessionId: "sess_rest_create_001", tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex" }) as { action: string; pvc: { pvcName: string; pvcPhase: string } };
|
|
assert.equal(create.action, "session-created");
|
|
assert.ok(create.pvc.pvcName.startsWith("agentrun-v01-session-"));
|
|
const existing = await client.post("/api/v1/sessions", { sessionId: "sess_rest_create_001", tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex" }) as { action: string };
|
|
assert.equal(existing.action, "session-exists");
|
|
const recovered = await client.post("/api/v1/sessions", { sessionId: "sess_rest_create_002", tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex" }) as { action: string };
|
|
assert.equal(recovered.action, "session-created");
|
|
const recoveredFromNone = await client.post("/api/v1/sessions", { sessionId: "sess_rest_create_003", tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex" }) as { action: string };
|
|
assert.equal(recoveredFromNone.action, "session-created");
|
|
await restStore.refreshSessionStorage({ sessionId: "sess_rest_create_003", storageKind: "none", codexRolloutSubdir: "sessions" });
|
|
const recoveredAgain = await client.post("/api/v1/sessions", { sessionId: "sess_rest_create_003", tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex" }) as { action: string };
|
|
assert.equal(recoveredAgain.action, "session-storage-recovered");
|
|
} finally {
|
|
if (server.server.listening) await new Promise<void>((resolve) => server.server.close(() => resolve()));
|
|
}
|
|
|
|
return { name: "mgr-session-pvc", tests: ["session-pvc-create-summary-eviction", "session-pvc-gc", "session-pvc-rest-create"] };
|
|
};
|
|
|
|
export default selfTest;
|