Files
openclaw/test/pr119473-real-runtime-proof.e2e.test.ts
T
Jacqueline Henriksen 5ec4d9f82a fix(agents): seed fresh session identity before transcript append (#119473)
* fix(agents): seed fresh session identity before transcript append

* test(agents): prove fresh session persistence through gateway

* fix(agents): seed session identity on first transcript write

* fix(agents): atomically seed fresh session identity
2026-08-05 15:30:30 -07:00

198 lines
7.3 KiB
TypeScript

import fs from "node:fs/promises";
import { createServer } from "node:http";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { clearConfigCache, clearRuntimeConfigSnapshot } from "../src/config/config.js";
import { clearSessionStoreCacheForTest } from "../src/config/sessions/store-writer-state.js";
import {
disconnectGatewayClient,
startGatewayWithClient,
} from "../src/gateway/test-helpers.e2e.js";
import { buildMockOpenAiResponsesProvider } from "../src/gateway/test-openai-responses-model.js";
import { captureEnv, setTestEnvValue } from "../src/test-utils/env.js";
const envKeys = [
"HOME",
"OPENCLAW_STATE_DIR",
"OPENCLAW_CONFIG_PATH",
"OPENCLAW_GATEWAY_TOKEN",
"OPENCLAW_SKIP_CHANNELS",
"OPENCLAW_SKIP_GMAIL_WATCHER",
"OPENCLAW_SKIP_CRON",
"OPENCLAW_SKIP_CANVAS_HOST",
"OPENCLAW_SKIP_BROWSER_CONTROL_SERVER",
"OPENCLAW_SKIP_PROVIDERS",
"OPENCLAW_BUNDLED_PLUGINS_DIR",
"OPENCLAW_DISABLE_BUNDLED_PLUGINS",
] as const;
describe("PR #119473 real gateway proof", () => {
let tempHome: string | undefined;
afterEach(async () => {
if (tempHome) {
await fs.rm(tempHome, { recursive: true, force: true });
tempHome = undefined;
}
});
it(
"persists the first turn of a fresh SQLite-backed session across gateway restart",
{ timeout: 90_000 },
async () => {
const envSnapshot = captureEnv([...envKeys]);
let providerServer: ReturnType<typeof createServer> | undefined;
let first: Awaited<ReturnType<typeof startGatewayWithClient>> | undefined;
let second: Awaited<ReturnType<typeof startGatewayWithClient>> | undefined;
try {
tempHome = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-pr119473-proof-"));
const stateDir = path.join(tempHome, ".openclaw");
const workspaceDir = path.join(tempHome, "workspace");
const configPath = path.join(stateDir, "openclaw.json");
const bundledPluginsDir = path.join(tempHome, "bundled-plugins");
await Promise.all([
fs.mkdir(workspaceDir, { recursive: true }),
fs.mkdir(bundledPluginsDir, { recursive: true }),
fs.mkdir(path.dirname(configPath), { recursive: true }),
]);
for (const [key, value] of Object.entries({
HOME: tempHome,
OPENCLAW_STATE_DIR: stateDir,
OPENCLAW_CONFIG_PATH: configPath,
OPENCLAW_GATEWAY_TOKEN: "pr119473-proof-token",
OPENCLAW_SKIP_CHANNELS: "1",
OPENCLAW_SKIP_GMAIL_WATCHER: "1",
OPENCLAW_SKIP_CRON: "1",
OPENCLAW_SKIP_CANVAS_HOST: "1",
OPENCLAW_SKIP_BROWSER_CONTROL_SERVER: "1",
OPENCLAW_SKIP_PROVIDERS: "1",
OPENCLAW_BUNDLED_PLUGINS_DIR: bundledPluginsDir,
OPENCLAW_DISABLE_BUNDLED_PLUGINS: "1",
})) {
setTestEnvValue(key, value);
}
providerServer = createServer((_request, response) => {
response.writeHead(200, { "content-type": "text/event-stream" });
const message = {
type: "message",
id: "pr119473-proof-message",
role: "assistant",
status: "completed",
content: [{ type: "output_text", text: "PR119473_RUNTIME_OK", annotations: [] }],
};
response.end(
[
{
type: "response.output_item.added",
output_index: 0,
item: { ...message, status: "in_progress", content: [] },
},
{ type: "response.output_item.done", output_index: 0, item: message },
{
type: "response.completed",
response: {
status: "completed",
usage: { input_tokens: 10, output_tokens: 5, total_tokens: 15 },
},
},
]
.map((event) => `data: ${JSON.stringify(event)}\n\n`)
.concat("data: [DONE]\n\n")
.join(""),
);
});
await new Promise<void>((resolve, reject) => {
providerServer?.once("error", reject);
providerServer?.listen(0, "127.0.0.1", resolve);
});
const providerAddress = providerServer.address();
if (!providerAddress || typeof providerAddress === "string") {
throw new Error("proof provider did not bind a loopback port");
}
const provider = buildMockOpenAiResponsesProvider(
`http://127.0.0.1:${providerAddress.port}/v1`,
);
const cfg = {
agents: {
defaults: {
workspace: workspaceDir,
skipBootstrap: true,
model: { primary: provider.modelRef },
models: {
[provider.modelRef]: { params: { transport: "sse", openaiWsWarmup: false } },
},
},
entries: { main: { default: true } },
},
models: { mode: "replace", providers: { [provider.providerId]: provider.config } },
gateway: { auth: { mode: "token", token: "pr119473-proof-token" } },
};
const sessionKey = "agent:main:pr119473-fresh-runtime";
const marker = "PR119473_FIRST_USER_MESSAGE";
first = await startGatewayWithClient({
cfg,
configPath,
token: "pr119473-proof-token",
clientDisplayName: "pr119473-proof-first-start",
});
const started = await first.client.request<{ runId?: string; status?: string }>(
"chat.send",
{
sessionKey,
message: marker,
deliver: false,
idempotencyKey: "pr119473-proof-first-turn",
},
);
expect(started.status).toBe("started");
expect(started.runId).toEqual(expect.any(String));
await expect(
first.client.request(
"agent.wait",
{ runId: started.runId, timeoutMs: 30_000 },
{ timeoutMs: 35_000 },
),
).resolves.toMatchObject({ status: "ok" });
await disconnectGatewayClient(first.client);
await first.server.close({ reason: "PR #119473 proof restart" });
first = undefined;
clearRuntimeConfigSnapshot();
clearConfigCache();
second = await startGatewayWithClient({
cfg,
configPath,
token: "pr119473-proof-token",
clientDisplayName: "pr119473-proof-after-restart",
});
const history = await second.client.request<{ messages?: unknown[] }>("chat.history", {
sessionKey,
limit: 20,
});
expect(JSON.stringify(history.messages ?? [])).toContain(marker);
} finally {
if (first) {
await disconnectGatewayClient(first.client).catch(() => undefined);
await first.server.close().catch(() => undefined);
}
if (second) {
await disconnectGatewayClient(second.client).catch(() => undefined);
await second.server.close().catch(() => undefined);
}
if (providerServer?.listening) {
await new Promise<void>((resolve) => {
providerServer?.close(() => resolve());
});
}
envSnapshot.restore();
clearRuntimeConfigSnapshot();
clearConfigCache();
clearSessionStoreCacheForTest();
}
},
);
});