test: consolidate directive behavior suites for faster runs

This commit is contained in:
Peter Steinberger
2026-02-23 21:48:12 +00:00
parent b8fc8e7e6d
commit b9f01e8d3f
4 changed files with 226 additions and 278 deletions

View File

@@ -171,24 +171,18 @@ describe("directive behavior", () => {
expect(blockReplies.length).toBe(0);
});
});
it("acks verbose directive immediately with system marker", async () => {
it("handles standalone verbose directives and persistence", async () => {
await withTempHome(async (home) => {
const res = await getReplyFromConfig(
const storePath = sessionStorePath(home);
const enabledRes = await getReplyFromConfig(
{ Body: "/verbose on", From: "+1222", To: "+1222", CommandAuthorized: true },
{},
makeWhatsAppDirectiveConfig(home, { model: "anthropic/claude-opus-4-5" }),
);
expect(replyText(enabledRes)).toMatch(/^⚙️ Verbose logging enabled\./);
const text = replyText(res);
expect(text).toMatch(/^⚙️ Verbose logging enabled\./);
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("persists verbose off when directive is standalone", async () => {
await withTempHome(async (home) => {
const storePath = sessionStorePath(home);
const res = await getReplyFromConfig(
const disabledRes = await getReplyFromConfig(
{ Body: "/verbose off", From: "+1222", To: "+1222", CommandAuthorized: true },
{},
makeWhatsAppDirectiveConfig(
@@ -200,7 +194,7 @@ describe("directive behavior", () => {
),
);
const text = replyText(res);
const text = replyText(disabledRes);
expect(text).toMatch(/Verbose logging disabled\./);
const store = loadSessionStore(storePath);
const entry = Object.values(store)[0];
@@ -208,59 +202,46 @@ describe("directive behavior", () => {
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("updates tool verbose during an in-flight run (toggle on)", async () => {
it("updates tool verbose during in-flight runs for toggle on/off", async () => {
await withTempHome(async (home) => {
const { res } = await runInFlightVerboseToggleCase({
home,
shouldEmitBefore: false,
toggledVerboseLevel: "on",
});
const texts = replyTexts(res);
expect(texts).toContain("done");
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
for (const testCase of [
{
shouldEmitBefore: false,
toggledVerboseLevel: "on" as const,
},
{
shouldEmitBefore: true,
toggledVerboseLevel: "off" as const,
seedVerboseOn: true,
},
]) {
vi.mocked(runEmbeddedPiAgent).mockClear();
const { res } = await runInFlightVerboseToggleCase({
home,
...testCase,
});
const texts = replyTexts(res);
expect(texts).toContain("done");
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
}
});
});
it("updates tool verbose during an in-flight run (toggle off)", async () => {
await withTempHome(async (home) => {
const { res } = await runInFlightVerboseToggleCase({
home,
shouldEmitBefore: true,
toggledVerboseLevel: "off",
seedVerboseOn: true,
});
const texts = replyTexts(res);
expect(texts).toContain("done");
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
});
});
it("shows current think level when /think has no argument", async () => {
it("covers think status and /thinking xhigh support matrix", async () => {
await withTempHome(async (home) => {
const text = await runThinkDirectiveAndGetText(home);
expect(text).toContain("Current thinking level: high");
expect(text).toContain("Options: off, minimal, low, medium, high.");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("accepts /thinking xhigh for codex models", async () => {
await withTempHome(async (home) => {
const texts = await runThinkingDirective(home, "openai-codex/gpt-5.2-codex");
expect(texts).toContain("Thinking level set to xhigh.");
});
});
it("accepts /thinking xhigh for openai gpt-5.2", async () => {
await withTempHome(async (home) => {
const texts = await runThinkingDirective(home, "openai/gpt-5.2");
expect(texts).toContain("Thinking level set to xhigh.");
});
});
it("rejects /thinking xhigh for non-codex models", async () => {
await withTempHome(async (home) => {
const texts = await runThinkingDirective(home, "openai/gpt-4.1-mini");
expect(texts).toContain(
for (const model of ["openai-codex/gpt-5.2-codex", "openai/gpt-5.2"]) {
const texts = await runThinkingDirective(home, model);
expect(texts).toContain("Thinking level set to xhigh.");
}
const unsupportedModelTexts = await runThinkingDirective(home, "openai/gpt-4.1-mini");
expect(unsupportedModelTexts).toContain(
'Thinking level "xhigh" is only supported for openai/gpt-5.2, openai-codex/gpt-5.3-codex, openai-codex/gpt-5.3-codex-spark, openai-codex/gpt-5.2-codex, openai-codex/gpt-5.1-codex, github-copilot/gpt-5.2-codex or github-copilot/gpt-5.2.',
);
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("keeps reserved command aliases from matching after trimming", async () => {
@@ -325,9 +306,9 @@ describe("directive behavior", () => {
expect(prompt).toContain('Use the "demo-skill" skill');
});
});
it("errors on invalid queue options", async () => {
it("reports invalid queue options and current queue settings", async () => {
await withTempHome(async (home) => {
const res = await getReplyFromConfig(
const invalidRes = await getReplyFromConfig(
{
Body: "/queue collect debounce:bogus cap:zero drop:maybe",
From: "+1222",
@@ -344,16 +325,12 @@ describe("directive behavior", () => {
),
);
const text = replyText(res);
expect(text).toContain("Invalid debounce");
expect(text).toContain("Invalid cap");
expect(text).toContain("Invalid drop policy");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("shows current queue settings when /queue has no arguments", async () => {
await withTempHome(async (home) => {
const res = await getReplyFromConfig(
const invalidText = replyText(invalidRes);
expect(invalidText).toContain("Invalid debounce");
expect(invalidText).toContain("Invalid cap");
expect(invalidText).toContain("Invalid drop policy");
const currentRes = await getReplyFromConfig(
{
Body: "/queue",
From: "+1222",
@@ -379,7 +356,7 @@ describe("directive behavior", () => {
),
);
const text = replyText(res);
const text = replyText(currentRes);
expect(text).toContain(
"Current queue settings: mode=collect, debounce=1500ms, cap=9, drop=summarize.",
);

View File

@@ -86,6 +86,7 @@ async function runReasoningDefaultCase(params: {
expectedReasoningLevel: "off" | "on";
thinkingDefault?: "off" | "low" | "medium" | "high";
}) {
vi.mocked(runEmbeddedPiAgent).mockClear();
mockEmbeddedTextResult("done");
mockReasoningCapableCatalog();
@@ -244,11 +245,11 @@ describe("directive behavior", () => {
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("ignores inline /model and uses the default model", async () => {
it("ignores inline /model and /think directives while still running agent content", async () => {
await withTempHome(async (home) => {
mockEmbeddedTextResult("done");
const res = await getReplyFromConfig(
const inlineModelRes = await getReplyFromConfig(
{
Body: "please sync /model openai/gpt-4.1-mini now",
From: "+1004",
@@ -258,31 +259,47 @@ describe("directive behavior", () => {
makeDefaultModelConfig(home),
);
const texts = replyTexts(res);
const texts = replyTexts(inlineModelRes);
expect(texts).toContain("done");
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
const call = vi.mocked(runEmbeddedPiAgent).mock.calls[0]?.[0];
expect(call?.provider).toBe("anthropic");
expect(call?.model).toBe("claude-opus-4-5");
vi.mocked(runEmbeddedPiAgent).mockClear();
mockEmbeddedTextResult("done");
const inlineThinkRes = await getReplyFromConfig(
{
Body: "please sync /think:high now",
From: "+1004",
To: "+2000",
},
{},
makeWhatsAppDirectiveConfig(home, { model: { primary: "anthropic/claude-opus-4-5" } }),
);
expect(replyTexts(inlineThinkRes)).toContain("done");
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
});
});
it("defaults thinking to low for reasoning-capable models without auto-enabling reasoning", async () => {
it("applies reasoning defaults based on thinkingDefault configuration", async () => {
await withTempHome(async (home) => {
await runReasoningDefaultCase({
home,
expectedThinkLevel: "low",
expectedReasoningLevel: "off",
});
});
});
it("keeps auto-reasoning enabled when thinking is explicitly off", async () => {
await withTempHome(async (home) => {
await runReasoningDefaultCase({
home,
expectedThinkLevel: "off",
expectedReasoningLevel: "on",
thinkingDefault: "off",
});
for (const scenario of [
{
expectedThinkLevel: "low" as const,
expectedReasoningLevel: "off" as const,
},
{
expectedThinkLevel: "off" as const,
expectedReasoningLevel: "on" as const,
thinkingDefault: "off" as const,
},
]) {
await runReasoningDefaultCase({
home,
...scenario,
});
}
});
});
it("passes elevated defaults when sender is approved", async () => {
@@ -384,17 +401,14 @@ describe("directive behavior", () => {
expect(call?.reasoningLevel).toBe("off");
});
});
for (const replyTag of ["[[reply_to_current]]", "[[ reply_to_current ]]"]) {
it(`strips ${replyTag} and maps reply_to_current to MessageSid`, async () => {
await withTempHome(async (home) => {
it("handles reply_to_current tags and explicit reply_to precedence", async () => {
await withTempHome(async (home) => {
for (const replyTag of ["[[reply_to_current]]", "[[ reply_to_current ]]"]) {
const payload = await runReplyToCurrentCase(home, `hello ${replyTag}`);
expect(payload?.text).toBe("hello");
expect(payload?.replyToId).toBe("msg-123");
});
});
}
it("prefers explicit reply_to id over reply_to_current", async () => {
await withTempHome(async (home) => {
}
vi.mocked(runEmbeddedPiAgent).mockResolvedValue(
makeEmbeddedTextResult("hi [[reply_to_current]] [[reply_to:abc-456]]"),
);
@@ -415,23 +429,4 @@ describe("directive behavior", () => {
expect(payload?.replyToId).toBe("abc-456");
});
});
it("applies inline think and still runs agent content", async () => {
await withTempHome(async (home) => {
mockEmbeddedTextResult("done");
const res = await getReplyFromConfig(
{
Body: "please sync /think:high now",
From: "+1004",
To: "+2000",
},
{},
makeWhatsAppDirectiveConfig(home, { model: { primary: "anthropic/claude-opus-4-5" } }),
);
const texts = replyTexts(res);
expect(texts).toContain("done");
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
});
});
});

View File

@@ -110,87 +110,84 @@ describe("directive behavior", () => {
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("picks the best fuzzy match when multiple models match", async () => {
it("picks the best fuzzy match for global and provider-scoped minimax queries", async () => {
await withTempHome(async (home) => {
const storePath = path.join(home, "sessions.json");
await getReplyFromConfig(
{ Body: "/model minimax", From: "+1222", To: "+1222", CommandAuthorized: true },
{},
for (const testCase of [
{
agents: {
defaults: {
model: { primary: "minimax/MiniMax-M2.1" },
workspace: path.join(home, "openclaw"),
models: {
"minimax/MiniMax-M2.1": {},
"minimax/MiniMax-M2.1-lightning": {},
"lmstudio/minimax-m2.1-gs32": {},
body: "/model minimax",
storePath: path.join(home, "sessions-global-fuzzy.json"),
config: {
agents: {
defaults: {
model: { primary: "minimax/MiniMax-M2.1" },
workspace: path.join(home, "openclaw"),
models: {
"minimax/MiniMax-M2.1": {},
"minimax/MiniMax-M2.1-lightning": {},
"lmstudio/minimax-m2.1-gs32": {},
},
},
},
models: {
mode: "merge",
providers: {
minimax: {
baseUrl: "https://api.minimax.io/anthropic",
apiKey: "sk-test",
api: "anthropic-messages",
models: [makeModelDefinition("MiniMax-M2.1", "MiniMax M2.1")],
},
lmstudio: {
baseUrl: "http://127.0.0.1:1234/v1",
apiKey: "lmstudio",
api: "openai-responses",
models: [makeModelDefinition("minimax-m2.1-gs32", "MiniMax M2.1 GS32")],
},
},
},
},
models: {
mode: "merge",
providers: {
minimax: {
baseUrl: "https://api.minimax.io/anthropic",
apiKey: "sk-test",
api: "anthropic-messages",
models: [makeModelDefinition("MiniMax-M2.1", "MiniMax M2.1")],
},
lmstudio: {
baseUrl: "http://127.0.0.1:1234/v1",
apiKey: "lmstudio",
api: "openai-responses",
models: [makeModelDefinition("minimax-m2.1-gs32", "MiniMax M2.1 GS32")],
},
},
},
session: { store: storePath },
} as unknown as OpenClawConfig,
);
assertModelSelection(storePath);
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("picks the best fuzzy match within a provider", async () => {
await withTempHome(async (home) => {
const storePath = path.join(home, "sessions.json");
await getReplyFromConfig(
{ Body: "/model minimax/m2.1", From: "+1222", To: "+1222", CommandAuthorized: true },
{},
},
{
agents: {
defaults: {
model: { primary: "minimax/MiniMax-M2.1" },
workspace: path.join(home, "openclaw"),
models: {
"minimax/MiniMax-M2.1": {},
"minimax/MiniMax-M2.1-lightning": {},
body: "/model minimax/m2.1",
storePath: path.join(home, "sessions-provider-fuzzy.json"),
config: {
agents: {
defaults: {
model: { primary: "minimax/MiniMax-M2.1" },
workspace: path.join(home, "openclaw"),
models: {
"minimax/MiniMax-M2.1": {},
"minimax/MiniMax-M2.1-lightning": {},
},
},
},
models: {
mode: "merge",
providers: {
minimax: {
baseUrl: "https://api.minimax.io/anthropic",
apiKey: "sk-test",
api: "anthropic-messages",
models: [
makeModelDefinition("MiniMax-M2.1", "MiniMax M2.1"),
makeModelDefinition("MiniMax-M2.1-lightning", "MiniMax M2.1 Lightning"),
],
},
},
},
},
models: {
mode: "merge",
providers: {
minimax: {
baseUrl: "https://api.minimax.io/anthropic",
apiKey: "sk-test",
api: "anthropic-messages",
models: [
makeModelDefinition("MiniMax-M2.1", "MiniMax M2.1"),
makeModelDefinition("MiniMax-M2.1-lightning", "MiniMax M2.1 Lightning"),
],
},
},
},
session: { store: storePath },
} as unknown as OpenClawConfig,
);
assertModelSelection(storePath);
},
]) {
await getReplyFromConfig(
{ Body: testCase.body, From: "+1222", To: "+1222", CommandAuthorized: true },
{},
{
...testCase.config,
session: { store: testCase.storePath },
} as unknown as OpenClawConfig,
);
assertModelSelection(testCase.storePath);
}
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});

View File

@@ -184,9 +184,9 @@ describe("directive behavior", () => {
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("rejects per-agent elevated when disabled", async () => {
it("enforces per-agent elevated restrictions and status visibility", async () => {
await withTempHome(async (home) => {
const res = await getReplyFromConfig(
const deniedRes = await getReplyFromConfig(
{
Body: "/elevated on",
From: "+1222",
@@ -199,93 +199,10 @@ describe("directive behavior", () => {
{},
makeRestrictedElevatedDisabledConfig(home) as unknown as OpenClawConfig,
);
const deniedText = replyText(deniedRes);
expect(deniedText).toContain("agents.list[].tools.elevated.enabled");
const text = replyText(res);
expect(text).toContain("agents.list[].tools.elevated.enabled");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("requires per-agent allowlist in addition to global", async () => {
await withTempHome(async (home) => {
const res = await getReplyFromConfig(
{
Body: "/elevated on",
From: "+1222",
To: "+1222",
Provider: "whatsapp",
SenderE164: "+1222",
SessionKey: "agent:work:main",
CommandAuthorized: true,
},
{},
makeWorkElevatedAllowlistConfig(home),
);
const text = replyText(res);
expect(text).toContain("agents.list[].tools.elevated.allowFrom.whatsapp");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("allows elevated when both global and per-agent allowlists match", async () => {
await withTempHome(async (home) => {
const res = await getReplyFromConfig(
{
...makeCommandMessage("/elevated on", "+1333"),
SessionKey: "agent:work:main",
},
{},
makeWorkElevatedAllowlistConfig(home),
);
const text = replyText(res);
expect(text).toContain("Elevated mode set to ask");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("warns when elevated is used in direct runtime", async () => {
await withTempHome(async (home) => {
const res = await getReplyFromConfig(
makeCommandMessage("/elevated off"),
{},
makeAllowlistedElevatedConfig(home, { sandbox: { mode: "off" } }),
);
const text = replyText(res);
expect(text).toContain("Elevated mode disabled.");
expect(text).toContain("Runtime is direct; sandboxing does not apply.");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("rejects invalid elevated level", async () => {
await withTempHome(async (home) => {
const res = await getReplyFromConfig(
makeCommandMessage("/elevated maybe"),
{},
makeAllowlistedElevatedConfig(home),
);
const text = replyText(res);
expect(text).toContain("Unrecognized elevated level");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("handles multiple directives in a single message", async () => {
await withTempHome(async (home) => {
const res = await getReplyFromConfig(
makeCommandMessage("/elevated off\n/verbose on"),
{},
makeAllowlistedElevatedConfig(home),
);
const text = replyText(res);
expect(text).toContain("Elevated mode disabled.");
expect(text).toContain("Verbose logging enabled.");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("shows elevated off in status when per-agent elevated is disabled", async () => {
await withTempHome(async (home) => {
const res = await getReplyFromConfig(
const statusRes = await getReplyFromConfig(
{
Body: "/status",
From: "+1222",
@@ -298,9 +215,71 @@ describe("directive behavior", () => {
{},
makeRestrictedElevatedDisabledConfig(home) as unknown as OpenClawConfig,
);
const statusText = replyText(statusRes);
expect(statusText).not.toContain("elevated");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("applies per-agent allowlist requirements before allowing elevated", async () => {
await withTempHome(async (home) => {
const deniedRes = await getReplyFromConfig(
{
...makeCommandMessage("/elevated on", "+1222"),
SessionKey: "agent:work:main",
},
{},
makeWorkElevatedAllowlistConfig(home),
);
const text = replyText(res);
expect(text).not.toContain("elevated");
const deniedText = replyText(deniedRes);
expect(deniedText).toContain("agents.list[].tools.elevated.allowFrom.whatsapp");
const allowedRes = await getReplyFromConfig(
{
...makeCommandMessage("/elevated on", "+1333"),
SessionKey: "agent:work:main",
},
{},
makeWorkElevatedAllowlistConfig(home),
);
const allowedText = replyText(allowedRes);
expect(allowedText).toContain("Elevated mode set to ask");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("handles runtime warning, invalid level, and multi-directive elevated inputs", async () => {
await withTempHome(async (home) => {
for (const scenario of [
{
body: "/elevated off",
config: makeAllowlistedElevatedConfig(home, { sandbox: { mode: "off" } }),
expectedSnippets: [
"Elevated mode disabled.",
"Runtime is direct; sandboxing does not apply.",
],
},
{
body: "/elevated maybe",
config: makeAllowlistedElevatedConfig(home),
expectedSnippets: ["Unrecognized elevated level"],
},
{
body: "/elevated off\n/verbose on",
config: makeAllowlistedElevatedConfig(home),
expectedSnippets: ["Elevated mode disabled.", "Verbose logging enabled."],
},
]) {
const res = await getReplyFromConfig(
makeCommandMessage(scenario.body),
{},
scenario.config,
);
const text = replyText(res);
for (const snippet of scenario.expectedSnippets) {
expect(text).toContain(snippet);
}
}
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});