mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 19:28:28 +00:00
test: merge model picker tests into native command suite
This commit is contained in:
@@ -1,135 +0,0 @@
|
|||||||
import { beforeAll, describe, expect, it } from "vitest";
|
|
||||||
import { normalizeTestText } from "../../test/helpers/normalize-text.js";
|
|
||||||
import { loadSessionStore } from "../config/sessions.js";
|
|
||||||
import {
|
|
||||||
installTriggerHandlingE2eTestHooks,
|
|
||||||
makeCfg,
|
|
||||||
withTempHome,
|
|
||||||
} from "./reply.triggers.trigger-handling.test-harness.js";
|
|
||||||
|
|
||||||
let getReplyFromConfig: typeof import("./reply.js").getReplyFromConfig;
|
|
||||||
beforeAll(async () => {
|
|
||||||
({ getReplyFromConfig } = await import("./reply.js"));
|
|
||||||
});
|
|
||||||
|
|
||||||
installTriggerHandlingE2eTestHooks();
|
|
||||||
|
|
||||||
const DEFAULT_SESSION_KEY = "telegram:slash:111";
|
|
||||||
|
|
||||||
function requireSessionStorePath(cfg: { session?: { store?: string } }): string {
|
|
||||||
const storePath = cfg.session?.store;
|
|
||||||
if (!storePath) {
|
|
||||||
throw new Error("expected session store path");
|
|
||||||
}
|
|
||||||
return storePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeTelegramModelCommand(body: string, sessionKey = DEFAULT_SESSION_KEY) {
|
|
||||||
return {
|
|
||||||
Body: body,
|
|
||||||
From: "telegram:111",
|
|
||||||
To: "telegram:111",
|
|
||||||
ChatType: "direct" as const,
|
|
||||||
Provider: "telegram" as const,
|
|
||||||
Surface: "telegram" as const,
|
|
||||||
SessionKey: sessionKey,
|
|
||||||
CommandAuthorized: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function firstReplyText(reply: Awaited<ReturnType<typeof getReplyFromConfig>>) {
|
|
||||||
return Array.isArray(reply) ? (reply[0]?.text ?? "") : (reply?.text ?? "");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runModelCommand(home: string, body: string, sessionKey = DEFAULT_SESSION_KEY) {
|
|
||||||
const cfg = makeCfg(home);
|
|
||||||
const res = await getReplyFromConfig(makeTelegramModelCommand(body, sessionKey), {}, cfg);
|
|
||||||
const text = firstReplyText(res);
|
|
||||||
return {
|
|
||||||
cfg,
|
|
||||||
sessionKey,
|
|
||||||
text,
|
|
||||||
normalized: normalizeTestText(text),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("trigger handling", () => {
|
|
||||||
it("shows a /model summary and points to /models", async () => {
|
|
||||||
await withTempHome(async (home) => {
|
|
||||||
const { normalized } = await runModelCommand(home, "/model");
|
|
||||||
|
|
||||||
expect(normalized).toContain("Current: anthropic/claude-opus-4-5");
|
|
||||||
expect(normalized).toContain("/model <provider/model> to switch");
|
|
||||||
expect(normalized).toContain("Tap below to browse models");
|
|
||||||
expect(normalized).toContain("/model status for details");
|
|
||||||
expect(normalized).not.toContain("reasoning");
|
|
||||||
expect(normalized).not.toContain("image");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("aliases /model list to /models", async () => {
|
|
||||||
await withTempHome(async (home) => {
|
|
||||||
const { normalized } = await runModelCommand(home, "/model list");
|
|
||||||
|
|
||||||
expect(normalized).toContain("Providers:");
|
|
||||||
expect(normalized).toContain("Use: /models <provider>");
|
|
||||||
expect(normalized).toContain("Switch: /model <provider/model>");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("selects the exact provider/model pair for openrouter", async () => {
|
|
||||||
await withTempHome(async (home) => {
|
|
||||||
const { cfg, sessionKey, normalized } = await runModelCommand(
|
|
||||||
home,
|
|
||||||
"/model openrouter/anthropic/claude-opus-4-5",
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(normalized).toContain("Model set to openrouter/anthropic/claude-opus-4-5");
|
|
||||||
|
|
||||||
const store = loadSessionStore(requireSessionStorePath(cfg));
|
|
||||||
expect(store[sessionKey]?.providerOverride).toBe("openrouter");
|
|
||||||
expect(store[sessionKey]?.modelOverride).toBe("anthropic/claude-opus-4-5");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("rejects invalid /model <#> selections", async () => {
|
|
||||||
await withTempHome(async (home) => {
|
|
||||||
const { cfg, sessionKey, normalized } = await runModelCommand(home, "/model 99");
|
|
||||||
|
|
||||||
expect(normalized).toContain("Numeric model selection is not supported in chat.");
|
|
||||||
expect(normalized).toContain("Browse: /models or /models <provider>");
|
|
||||||
expect(normalized).toContain("Switch: /model <provider/model>");
|
|
||||||
|
|
||||||
const store = loadSessionStore(requireSessionStorePath(cfg));
|
|
||||||
expect(store[sessionKey]?.providerOverride).toBeUndefined();
|
|
||||||
expect(store[sessionKey]?.modelOverride).toBeUndefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("resets to the default model via /model <provider/model>", async () => {
|
|
||||||
await withTempHome(async (home) => {
|
|
||||||
const { cfg, sessionKey, normalized } = await runModelCommand(
|
|
||||||
home,
|
|
||||||
"/model anthropic/claude-opus-4-5",
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(normalized).toContain("Model reset to default (anthropic/claude-opus-4-5)");
|
|
||||||
|
|
||||||
const store = loadSessionStore(requireSessionStorePath(cfg));
|
|
||||||
expect(store[sessionKey]?.providerOverride).toBeUndefined();
|
|
||||||
expect(store[sessionKey]?.modelOverride).toBeUndefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("selects a model via /model <provider/model>", async () => {
|
|
||||||
await withTempHome(async (home) => {
|
|
||||||
const { cfg, sessionKey, normalized } = await runModelCommand(home, "/model openai/gpt-5.2");
|
|
||||||
|
|
||||||
expect(normalized).toContain("Model set to openai/gpt-5.2");
|
|
||||||
|
|
||||||
const store = loadSessionStore(requireSessionStorePath(cfg));
|
|
||||||
expect(store[sessionKey]?.providerOverride).toBe("openai");
|
|
||||||
expect(store[sessionKey]?.modelOverride).toBe("gpt-5.2");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||||
|
import { normalizeTestText } from "../../test/helpers/normalize-text.js";
|
||||||
import type { OpenClawConfig } from "../config/config.js";
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
import { loadSessionStore } from "../config/sessions.js";
|
import { loadSessionStore } from "../config/sessions.js";
|
||||||
import {
|
import {
|
||||||
@@ -30,7 +31,125 @@ afterAll(() => {
|
|||||||
|
|
||||||
installTriggerHandlingE2eTestHooks();
|
installTriggerHandlingE2eTestHooks();
|
||||||
|
|
||||||
|
const DEFAULT_SESSION_KEY = "telegram:slash:111";
|
||||||
|
|
||||||
|
function requireSessionStorePath(cfg: { session?: { store?: string } }): string {
|
||||||
|
const storePath = cfg.session?.store;
|
||||||
|
if (!storePath) {
|
||||||
|
throw new Error("expected session store path");
|
||||||
|
}
|
||||||
|
return storePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeTelegramModelCommand(body: string, sessionKey = DEFAULT_SESSION_KEY) {
|
||||||
|
return {
|
||||||
|
Body: body,
|
||||||
|
From: "telegram:111",
|
||||||
|
To: "telegram:111",
|
||||||
|
ChatType: "direct" as const,
|
||||||
|
Provider: "telegram" as const,
|
||||||
|
Surface: "telegram" as const,
|
||||||
|
SessionKey: sessionKey,
|
||||||
|
CommandAuthorized: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstReplyText(reply: Awaited<ReturnType<typeof getReplyFromConfig>>) {
|
||||||
|
return Array.isArray(reply) ? (reply[0]?.text ?? "") : (reply?.text ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runModelCommand(home: string, body: string, sessionKey = DEFAULT_SESSION_KEY) {
|
||||||
|
const cfg = makeCfg(home);
|
||||||
|
const res = await getReplyFromConfig(makeTelegramModelCommand(body, sessionKey), {}, cfg);
|
||||||
|
const text = firstReplyText(res);
|
||||||
|
return {
|
||||||
|
cfg,
|
||||||
|
sessionKey,
|
||||||
|
text,
|
||||||
|
normalized: normalizeTestText(text),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe("trigger handling", () => {
|
describe("trigger handling", () => {
|
||||||
|
it("shows a /model summary and points to /models", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
const { normalized } = await runModelCommand(home, "/model");
|
||||||
|
|
||||||
|
expect(normalized).toContain("Current: anthropic/claude-opus-4-5");
|
||||||
|
expect(normalized).toContain("/model <provider/model> to switch");
|
||||||
|
expect(normalized).toContain("Tap below to browse models");
|
||||||
|
expect(normalized).toContain("/model status for details");
|
||||||
|
expect(normalized).not.toContain("reasoning");
|
||||||
|
expect(normalized).not.toContain("image");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("aliases /model list to /models", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
const { normalized } = await runModelCommand(home, "/model list");
|
||||||
|
|
||||||
|
expect(normalized).toContain("Providers:");
|
||||||
|
expect(normalized).toContain("Use: /models <provider>");
|
||||||
|
expect(normalized).toContain("Switch: /model <provider/model>");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("selects the exact provider/model pair for openrouter", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
const { cfg, sessionKey, normalized } = await runModelCommand(
|
||||||
|
home,
|
||||||
|
"/model openrouter/anthropic/claude-opus-4-5",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(normalized).toContain("Model set to openrouter/anthropic/claude-opus-4-5");
|
||||||
|
|
||||||
|
const store = loadSessionStore(requireSessionStorePath(cfg));
|
||||||
|
expect(store[sessionKey]?.providerOverride).toBe("openrouter");
|
||||||
|
expect(store[sessionKey]?.modelOverride).toBe("anthropic/claude-opus-4-5");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects invalid /model <#> selections", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
const { cfg, sessionKey, normalized } = await runModelCommand(home, "/model 99");
|
||||||
|
|
||||||
|
expect(normalized).toContain("Numeric model selection is not supported in chat.");
|
||||||
|
expect(normalized).toContain("Browse: /models or /models <provider>");
|
||||||
|
expect(normalized).toContain("Switch: /model <provider/model>");
|
||||||
|
|
||||||
|
const store = loadSessionStore(requireSessionStorePath(cfg));
|
||||||
|
expect(store[sessionKey]?.providerOverride).toBeUndefined();
|
||||||
|
expect(store[sessionKey]?.modelOverride).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resets to the default model via /model <provider/model>", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
const { cfg, sessionKey, normalized } = await runModelCommand(
|
||||||
|
home,
|
||||||
|
"/model anthropic/claude-opus-4-5",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(normalized).toContain("Model reset to default (anthropic/claude-opus-4-5)");
|
||||||
|
|
||||||
|
const store = loadSessionStore(requireSessionStorePath(cfg));
|
||||||
|
expect(store[sessionKey]?.providerOverride).toBeUndefined();
|
||||||
|
expect(store[sessionKey]?.modelOverride).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("selects a model via /model <provider/model>", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
const { cfg, sessionKey, normalized } = await runModelCommand(home, "/model openai/gpt-5.2");
|
||||||
|
|
||||||
|
expect(normalized).toContain("Model set to openai/gpt-5.2");
|
||||||
|
|
||||||
|
const store = loadSessionStore(requireSessionStorePath(cfg));
|
||||||
|
expect(store[sessionKey]?.providerOverride).toBe("openai");
|
||||||
|
expect(store[sessionKey]?.modelOverride).toBe("gpt-5.2");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("targets the active session for native /stop", async () => {
|
it("targets the active session for native /stop", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
const cfg = makeCfg(home);
|
const cfg = makeCfg(home);
|
||||||
|
|||||||
Reference in New Issue
Block a user