test: remove duplicated scenario scaffolding across runtime tests

This commit is contained in:
Peter Steinberger
2026-02-18 03:38:13 +00:00
parent e57628165a
commit 262472ba20
4 changed files with 90 additions and 153 deletions

View File

@@ -359,9 +359,8 @@ describe("runCronIsolatedAgentTurn — skill filter", () => {
"nvidia/deepseek-ai/deepseek-v3.2",
];
it("preserves defaults when agent overrides primary as string", async () => {
resolveAgentConfigMock.mockReturnValue({ model: "anthropic/claude-sonnet-4-5" });
async function expectPrimaryOverridePreservesDefaults(modelOverride: unknown) {
resolveAgentConfigMock.mockReturnValue({ model: modelOverride });
const result = await runCronIsolatedAgentTurn(
makeParams({
cfg: {
@@ -383,34 +382,14 @@ describe("runCronIsolatedAgentTurn — skill filter", () => {
| undefined;
expect(model?.primary).toBe("anthropic/claude-sonnet-4-5");
expect(model?.fallbacks).toEqual(defaultFallbacks);
}
it("preserves defaults when agent overrides primary as string", async () => {
await expectPrimaryOverridePreservesDefaults("anthropic/claude-sonnet-4-5");
});
it("preserves defaults when agent overrides primary in object form", async () => {
resolveAgentConfigMock.mockReturnValue({
model: { primary: "anthropic/claude-sonnet-4-5" },
});
const result = await runCronIsolatedAgentTurn(
makeParams({
cfg: {
agents: {
defaults: {
model: { primary: "openai-codex/gpt-5.3-codex", fallbacks: defaultFallbacks },
},
},
},
agentId: "scout",
}),
);
expect(result.status).toBe("ok");
expect(runWithModelFallbackMock).toHaveBeenCalledOnce();
const callCfg = runWithModelFallbackMock.mock.calls[0][0].cfg;
const model = callCfg?.agents?.defaults?.model as
| { primary?: string; fallbacks?: string[] }
| undefined;
expect(model?.primary).toBe("anthropic/claude-sonnet-4-5");
expect(model?.fallbacks).toEqual(defaultFallbacks);
await expectPrimaryOverridePreservesDefaults({ primary: "anthropic/claude-sonnet-4-5" });
});
});
});

View File

@@ -40,6 +40,29 @@ function createMockSessionContent(
.join("\n");
}
async function runNewWithPreviousSessionEntry(params: {
tempDir: string;
previousSessionEntry: { sessionId: string; sessionFile?: string };
cfg?: OpenClawConfig;
}): Promise<{ files: string[]; memoryContent: string }> {
const event = createHookEvent("command", "new", "agent:main:main", {
cfg:
params.cfg ??
({
agents: { defaults: { workspace: params.tempDir } },
} satisfies OpenClawConfig),
previousSessionEntry: params.previousSessionEntry,
});
await handler(event);
const memoryDir = path.join(params.tempDir, "memory");
const files = await fs.readdir(memoryDir);
const memoryContent =
files.length > 0 ? await fs.readFile(path.join(memoryDir, files[0]), "utf-8") : "";
return { files, memoryContent };
}
async function runNewWithPreviousSession(params: {
sessionContent: string;
cfg?: (tempDir: string) => OpenClawConfig;
@@ -60,21 +83,14 @@ async function runNewWithPreviousSession(params: {
agents: { defaults: { workspace: tempDir } },
} satisfies OpenClawConfig);
const event = createHookEvent("command", "new", "agent:main:main", {
const { files, memoryContent } = await runNewWithPreviousSessionEntry({
tempDir,
cfg,
previousSessionEntry: {
sessionId: "test-123",
sessionFile,
},
});
await handler(event);
const memoryDir = path.join(tempDir, "memory");
const files = await fs.readdir(memoryDir);
const memoryContent =
files.length > 0 ? await fs.readFile(path.join(memoryDir, files[0]), "utf-8") : "";
return { tempDir, files, memoryContent };
}
@@ -277,25 +293,14 @@ describe("session-memory hook", () => {
content: resetContent,
});
const cfg = {
agents: { defaults: { workspace: tempDir } },
} satisfies OpenClawConfig;
const event = createHookEvent("command", "new", "agent:main:main", {
cfg,
const { memoryContent } = await runNewWithPreviousSessionEntry({
tempDir,
previousSessionEntry: {
sessionId: "test-123",
sessionFile: activeSessionFile,
},
});
await handler(event);
const memoryDir = path.join(tempDir, "memory");
const files = await fs.readdir(memoryDir);
expect(files.length).toBe(1);
const memoryContent = await fs.readFile(path.join(memoryDir, files[0]), "utf-8");
expect(memoryContent).toContain("user: Message from rotated transcript");
expect(memoryContent).toContain("assistant: Recovered from reset fallback");
});
@@ -408,25 +413,14 @@ describe("session-memory hook", () => {
]),
});
const cfg = {
agents: { defaults: { workspace: tempDir } },
} satisfies OpenClawConfig;
const event = createHookEvent("command", "new", "agent:main:main", {
cfg,
const { memoryContent } = await runNewWithPreviousSessionEntry({
tempDir,
previousSessionEntry: {
sessionId: "test-123",
sessionFile: activeSessionFile,
},
});
await handler(event);
const memoryDir = path.join(tempDir, "memory");
const files = await fs.readdir(memoryDir);
expect(files.length).toBe(1);
const memoryContent = await fs.readFile(path.join(memoryDir, files[0]), "utf-8");
expect(memoryContent).toContain("user: Newest rotated transcript");
expect(memoryContent).toContain("assistant: Newest summary");
expect(memoryContent).not.toContain("Older rotated transcript");

View File

@@ -48,16 +48,19 @@ function resolve(params: {
});
}
function buildDispatchContextPayload(params: { cfg: OpenClawConfig; message: IMessagePayload }) {
const { cfg, message } = params;
const groupHistories = new Map();
function resolveDispatchDecision(params: {
cfg: OpenClawConfig;
message: IMessagePayload;
groupHistories?: Parameters<typeof resolveIMessageInboundDecision>[0]["groupHistories"];
}) {
const groupHistories = params.groupHistories ?? new Map();
const decision = resolveIMessageInboundDecision({
cfg,
cfg: params.cfg,
accountId: "default",
message,
message: params.message,
opts: {},
messageText: message.text ?? "",
bodyText: message.text ?? "",
messageText: params.message.text ?? "",
bodyText: params.message.text ?? "",
allowFrom: ["*"],
groupAllowFrom: [],
groupPolicy: "open",
@@ -70,6 +73,12 @@ function buildDispatchContextPayload(params: { cfg: OpenClawConfig; message: IMe
if (decision.kind !== "dispatch") {
throw new Error("expected dispatch decision");
}
return { decision, groupHistories };
}
function buildDispatchContextPayload(params: { cfg: OpenClawConfig; message: IMessagePayload }) {
const { cfg, message } = params;
const { decision, groupHistories } = resolveDispatchDecision({ cfg, message });
const { ctxPayload } = buildIMessageInboundContext({
cfg,
@@ -167,25 +176,7 @@ describe("imessage monitor gating + envelope builders", () => {
text: "hello",
is_group: false,
};
const decision = resolveIMessageInboundDecision({
cfg,
accountId: "default",
message,
opts: {},
messageText: message.text ?? "",
bodyText: message.text ?? "",
allowFrom: ["*"],
groupAllowFrom: [],
groupPolicy: "open",
dmPolicy: "open",
storeAllowFrom: [],
historyLimit: 0,
groupHistories,
});
expect(decision.kind).toBe("dispatch");
if (decision.kind !== "dispatch") {
throw new Error("expected dispatch decision");
}
const { decision } = resolveDispatchDecision({ cfg, message, groupHistories });
expect(decision.isGroup).toBe(true);
expect(decision.route.sessionKey).toBe("agent:main:imessage:group:2");
});

View File

@@ -34,6 +34,13 @@ type Registered = {
methods: Map<string, unknown>;
tools: unknown[];
};
type RegisterVoiceCall = (api: Record<string, unknown>) => void | Promise<void>;
type RegisterCliContext = {
program: Command;
config: Record<string, unknown>;
workspaceDir?: string;
logger: typeof noopLogger;
};
function setup(config: Record<string, unknown>): Registered {
const methods = new Map<string, unknown>();
@@ -59,6 +66,34 @@ function setup(config: Record<string, unknown>): Registered {
return { methods, tools };
}
async function registerVoiceCallCli(program: Command) {
const { register } = plugin as unknown as {
register: RegisterVoiceCall;
};
await register({
id: "voice-call",
name: "Voice Call",
description: "test",
version: "0",
source: "test",
config: {},
pluginConfig: { provider: "mock" },
runtime: { tts: { textToSpeechTelephony: vi.fn() } },
logger: noopLogger,
registerGatewayMethod: () => {},
registerTool: () => {},
registerCli: (fn: (ctx: RegisterCliContext) => void) =>
fn({
program,
config: {},
workspaceDir: undefined,
logger: noopLogger,
}),
registerService: () => {},
resolvePath: (p: string) => p,
});
}
describe("voice-call plugin", () => {
beforeEach(() => {
runtimeStub = {
@@ -145,9 +180,6 @@ describe("voice-call plugin", () => {
});
it("CLI latency summarizes turn metrics from JSONL", async () => {
const { register } = plugin as unknown as {
register: (api: Record<string, unknown>) => void | Promise<void>;
};
const program = new Command();
const tmpFile = path.join(os.tmpdir(), `voicecall-latency-${Date.now()}.jsonl`);
fs.writeFileSync(
@@ -162,35 +194,7 @@ describe("voice-call plugin", () => {
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
try {
await register({
id: "voice-call",
name: "Voice Call",
description: "test",
version: "0",
source: "test",
config: {},
pluginConfig: { provider: "mock" },
runtime: { tts: { textToSpeechTelephony: vi.fn() } },
logger: noopLogger,
registerGatewayMethod: () => {},
registerTool: () => {},
registerCli: (
fn: (ctx: {
program: Command;
config: Record<string, unknown>;
workspaceDir?: string;
logger: typeof noopLogger;
}) => void,
) =>
fn({
program,
config: {},
workspaceDir: undefined,
logger: noopLogger,
}),
registerService: () => {},
resolvePath: (p: string) => p,
});
await registerVoiceCallCli(program);
await program.parseAsync(["voicecall", "latency", "--file", tmpFile, "--last", "10"], {
from: "user",
@@ -208,40 +212,9 @@ describe("voice-call plugin", () => {
});
it("CLI start prints JSON", async () => {
const { register } = plugin as unknown as {
register: (api: Record<string, unknown>) => void | Promise<void>;
};
const program = new Command();
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
await register({
id: "voice-call",
name: "Voice Call",
description: "test",
version: "0",
source: "test",
config: {},
pluginConfig: { provider: "mock" },
runtime: { tts: { textToSpeechTelephony: vi.fn() } },
logger: noopLogger,
registerGatewayMethod: () => {},
registerTool: () => {},
registerCli: (
fn: (ctx: {
program: Command;
config: Record<string, unknown>;
workspaceDir?: string;
logger: typeof noopLogger;
}) => void,
) =>
fn({
program,
config: {},
workspaceDir: undefined,
logger: noopLogger,
}),
registerService: () => {},
resolvePath: (p: string) => p,
});
await registerVoiceCallCli(program);
await program.parseAsync(["voicecall", "start", "--to", "+1", "--message", "Hello"], {
from: "user",