* refactor: move Discord channel implementation to extensions/discord/src/ Move all Discord source files from src/discord/ to extensions/discord/src/, following the extension migration pattern. Source files in src/discord/ are replaced with re-export shims. Channel-plugin files from src/channels/plugins/*/discord* are similarly moved and shimmed. - Copy all .ts source files preserving subdirectory structure (monitor/, voice/) - Move channel-plugin files (actions, normalize, onboarding, outbound, status-issues) - Fix all relative imports to use correct paths from new location - Create re-export shims at original locations for backward compatibility - Delete test files from shim locations (tests live in extension now) - Update tsconfig.plugin-sdk.dts.json rootDir from "src" to "." to accommodate extension files outside src/ - Update write-plugin-sdk-entry-dts.ts to match new declaration output paths * fix: add importOriginal to thread-bindings session-meta mock for extensions test * style: fix formatting in thread-bindings lifecycle test
68 lines
2.3 KiB
TypeScript
68 lines
2.3 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import {
|
|
readDiscordModelPickerRecentModels,
|
|
recordDiscordModelPickerRecentModel,
|
|
} from "./model-picker-preferences.js";
|
|
|
|
const tempDirs: string[] = [];
|
|
|
|
async function createStateEnv(): Promise<NodeJS.ProcessEnv> {
|
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-model-picker-"));
|
|
tempDirs.push(dir);
|
|
return { ...process.env, OPENCLAW_STATE_DIR: dir };
|
|
}
|
|
|
|
afterEach(async () => {
|
|
await Promise.all(
|
|
tempDirs.splice(0).map(async (dir) => {
|
|
await fs.rm(dir, { recursive: true, force: true });
|
|
}),
|
|
);
|
|
});
|
|
|
|
describe("discord model picker preferences", () => {
|
|
it("records recent models in recency order without duplicates", async () => {
|
|
const env = await createStateEnv();
|
|
const scope = { userId: "123" };
|
|
|
|
await recordDiscordModelPickerRecentModel({ env, scope, modelRef: "openai/gpt-4o" });
|
|
await recordDiscordModelPickerRecentModel({ env, scope, modelRef: "openai/gpt-4.1" });
|
|
await recordDiscordModelPickerRecentModel({ env, scope, modelRef: "openai/gpt-4o" });
|
|
|
|
const recent = await readDiscordModelPickerRecentModels({ env, scope });
|
|
expect(recent).toEqual(["openai/gpt-4o", "openai/gpt-4.1"]);
|
|
});
|
|
|
|
it("filters recent models using an allowlist", async () => {
|
|
const env = await createStateEnv();
|
|
const scope = { userId: "456" };
|
|
|
|
await recordDiscordModelPickerRecentModel({ env, scope, modelRef: "openai/gpt-4o" });
|
|
await recordDiscordModelPickerRecentModel({ env, scope, modelRef: "openai/gpt-4.1" });
|
|
|
|
const recent = await readDiscordModelPickerRecentModels({
|
|
env,
|
|
scope,
|
|
allowedModelRefs: new Set(["openai/gpt-4.1"]),
|
|
});
|
|
expect(recent).toEqual(["openai/gpt-4.1"]);
|
|
});
|
|
|
|
it("falls back to an empty store when the file is corrupt", async () => {
|
|
const env = await createStateEnv();
|
|
const stateDir = env.OPENCLAW_STATE_DIR as string;
|
|
const filePath = path.join(stateDir, "discord", "model-picker-preferences.json");
|
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
await fs.writeFile(filePath, "{not-json", "utf-8");
|
|
|
|
const recent = await readDiscordModelPickerRecentModels({
|
|
env,
|
|
scope: { userId: "789" },
|
|
});
|
|
expect(recent).toEqual([]);
|
|
});
|
|
});
|