import fs from "node:fs/promises" ;
import os from "node:os" ;
import path from "node:path" ;
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest" ;
import type { OpenClawConfig } from "../../config/config.js" ;
import {
buildFastReplyCommandContext,
initFastReplySessionState,
markCompleteReplyConfig,
withFastReplyConfig,
} from "./get-reply-fast-path.js" ;
import {
buildGetReplyCtx,
createGetReplySessionState,
expectResolvedTelegramTimezone,
registerGetReplyRuntimeOverrides,
} from "./get-reply.test-fixtures.js" ;
import { loadGetReplyModuleForTest } from "./get-reply.test-loader.js" ;
import "./get-reply.test-runtime-mocks.js" ;
const mocks = vi.hoisted(() => ({
ensureAgentWorkspace: vi.fn(),
initSessionState: vi.fn(),
resolveReplyDirectives: vi.fn(),
}));
vi.mock("../../agents/workspace.js" , () => ({
DEFAULT_AGENT_WORKSPACE_DIR: "/tmp/openclaw-workspace" ,
ensureAgentWorkspace: (...args: unknown[]) => mocks.ensureAgentWorkspace(...args),
}));
registerGetReplyRuntimeOverrides(mocks);
let getReplyFromConfig: typeof import ("./get-reply.js" ).getReplyFromConfig;
let loadConfigMock: typeof import ("../../config/config.js" ).loadConfig;
let runPreparedReplyMock: typeof import ("./get-reply-run.js" ).runPreparedReply;
async function loadGetReplyRuntimeForTest() {
({ getReplyFromConfig } = await loadGetReplyModuleForTest({ cacheKey: import .meta.url }));
({ loadConfig: loadConfigMock } = await import ("../../config/config.js" ));
({ runPreparedReply: runPreparedReplyMock } = await import ("./get-reply-run.js" ));
}
describe("getReplyFromConfig fast test bootstrap" , () => {
beforeAll(async () => {
await loadGetReplyRuntimeForTest();
});
beforeEach(() => {
vi.stubEnv("OPENCLAW_TEST_FAST" , "1" );
mocks.ensureAgentWorkspace.mockReset();
mocks.initSessionState.mockReset();
mocks.resolveReplyDirectives.mockReset();
vi.mocked(loadConfigMock).mockReset();
vi.mocked(runPreparedReplyMock).mockReset();
vi.mocked(loadConfigMock).mockReturnValue({});
mocks.resolveReplyDirectives.mockResolvedValue({ kind: "reply" , reply: { text: "ok" } });
vi.mocked(runPreparedReplyMock).mockResolvedValue({ text: "ok" });
mocks.initSessionState.mockResolvedValue(createGetReplySessionState());
});
afterEach(() => {
vi.unstubAllEnvs();
});
it("fails fast on unmarked config overrides in strict fast-test mode" , async () => {
await expect(
getReplyFromConfig(buildGetReplyCtx(), undefined, {} as OpenClawConfig),
).rejects.toThrow(/withFastReplyConfig\(\)\/markCompleteReplyConfig\(\)/);
expect(vi.mocked(loadConfigMock)).not.toHaveBeenCalled();
});
it("skips loadConfig, workspace bootstrap, and session bootstrap for marked test configs" , async () => {
const home = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-fast-reply-" ));
const cfg = markCompleteReplyConfig({
agents: {
defaults: {
model: "anthropic/claude-opus-4-6" ,
workspace: path.join(home, "openclaw" ),
},
},
channels: { telegram: { allowFrom: ["*" ] } },
session: { store: path.join(home, "sessions.json" ) },
} as OpenClawConfig);
await expect(getReplyFromConfig(buildGetReplyCtx(), undefined, cfg)).resolves.toEqual({
text: "ok" ,
});
expect(vi.mocked(loadConfigMock)).not.toHaveBeenCalled();
expect(mocks.ensureAgentWorkspace).not.toHaveBeenCalled();
expect(mocks.initSessionState).not.toHaveBeenCalled();
expect(mocks.resolveReplyDirectives).not.toHaveBeenCalled();
expect(vi.mocked(runPreparedReplyMock)).toHaveBeenCalledWith(
expect.objectContaining({
cfg,
}),
);
});
it("still merges partial config overrides against loadConfig()" , async () => {
vi.stubEnv("OPENCLAW_ALLOW_SLOW_REPLY_TESTS" , "1" );
vi.mocked(loadConfigMock).mockReturnValue({
channels: {
telegram: {
botToken: "resolved-telegram-token" ,
},
},
} satisfies OpenClawConfig);
await getReplyFromConfig(buildGetReplyCtx(), undefined, {
agents: {
defaults: {
userTimezone: "America/New_York" ,
},
},
} as OpenClawConfig);
expect(vi.mocked(loadConfigMock)).toHaveBeenCalledOnce();
expect(mocks.initSessionState).toHaveBeenCalledOnce();
expectResolvedTelegramTimezone(mocks.resolveReplyDirectives);
});
it("marks configs through withFastReplyConfig()" , async () => {
const cfg = withFastReplyConfig({ session: { store: "/tmp/sessions.json" } } as OpenClawConfig);
await expect(getReplyFromConfig(buildGetReplyCtx(), undefined, cfg)).resolves.toEqual({
text: "ok" ,
});
expect(vi.mocked(loadConfigMock)).not.toHaveBeenCalled();
expect(mocks.resolveReplyDirectives).not.toHaveBeenCalled();
expect(vi.mocked(runPreparedReplyMock)).toHaveBeenCalledOnce();
});
it("uses native command target session keys during fast bootstrap" , () => {
const result = initFastReplySessionState({
ctx: buildGetReplyCtx({
SessionKey: "telegram:slash:123" ,
CommandSource: "native" ,
CommandTargetSessionKey: "agent:main:main" ,
}),
cfg: { session: { store: "/tmp/sessions.json" } } as OpenClawConfig,
agentId: "main" ,
commandAuthorized: true ,
workspaceDir: "/tmp/workspace" ,
});
expect(result.sessionKey).toBe("agent:main:main" );
expect(result.sessionCtx.SessionKey).toBe("agent:main:main" );
});
it("maps explicit gateway origin into command context" , () => {
const command = buildFastReplyCommandContext({
ctx: buildGetReplyCtx({
Provider: "internal" ,
Surface: "internal" ,
OriginatingChannel: "slack" ,
OriginatingTo: "user:U123" ,
From: undefined,
To: undefined,
SenderId: "gateway-client" ,
}),
cfg: {} as OpenClawConfig,
sessionKey: "main" ,
isGroup: false ,
triggerBodyNormalized: "/codex bind" ,
commandAuthorized: true ,
});
expect(command.channel).toBe("slack" );
expect(command.channelId).toBe("slack" );
expect(command.from).toBe("gateway-client" );
expect(command.to).toBe("user:U123" );
});
it("keeps the existing session for /reset newline soft during fast bootstrap" , async () => {
const home = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-fast-reset-newline-soft-" ));
const storePath = path.join(home, "sessions.json" );
const sessionKey = "agent:main:telegram:123" ;
await fs.writeFile(
storePath,
JSON.stringify({
[sessionKey]: {
sessionId: "existing-fast-reset-newline-soft" ,
updatedAt: Date.now(),
},
}),
"utf8" ,
);
const result = initFastReplySessionState({
ctx: buildGetReplyCtx({
Body: "/reset \nsoft" ,
RawBody: "/reset \nsoft" ,
CommandBody: "/reset \nsoft" ,
SessionKey: sessionKey,
}),
cfg: { session: { store: storePath } } as OpenClawConfig,
agentId: "main" ,
commandAuthorized: true ,
workspaceDir: home,
});
expect(result.resetTriggered).toBe(false );
expect(result.isNewSession).toBe(false );
expect(result.sessionId).toBe("existing-fast-reset-newline-soft" );
});
it("keeps the existing session for /reset: soft during fast bootstrap" , async () => {
const home = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-fast-reset-colon-soft-" ));
const storePath = path.join(home, "sessions.json" );
const sessionKey = "agent:main:telegram:123" ;
await fs.writeFile(
storePath,
JSON.stringify({
[sessionKey]: {
sessionId: "existing-fast-reset-colon-soft" ,
updatedAt: Date.now(),
},
}),
"utf8" ,
);
const result = initFastReplySessionState({
ctx: buildGetReplyCtx({
Body: "/reset: soft" ,
RawBody: "/reset: soft" ,
CommandBody: "/reset: soft" ,
SessionKey: sessionKey,
}),
cfg: { session: { store: storePath } } as OpenClawConfig,
agentId: "main" ,
commandAuthorized: true ,
workspaceDir: home,
});
expect(result.resetTriggered).toBe(false );
expect(result.isNewSession).toBe(false );
expect(result.sessionId).toBe("existing-fast-reset-colon-soft" );
});
});
Messung V0.5 in Prozent C=99 H=99 G=98
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland