mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-13 18:32:12 +03:00
* test(infra): retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) Two shards on release/v3.8.51 went red in one day with the same signature — "ENOTEMPTY, Directory not empty: /tmp/omniroute-<test>-XXXXXX" — from combo-same-provider-cascade (Unit Tests fast-path 4/4, on a PR that touches only .github/) and auth-policy-embeddings-webfetch-7785 (the 20k-test TIA step). Both pass alone and on re-run: the cleanup races something still writing into the directory (SQLite WAL/-shm checkpoint, a worker, the backup) and under a loaded hosted runner the window opens. 1154 test files do their own cleanup with fs.rmSync(dir, { recursive: true, force: true }); 57 already asked for retries. One-shot codemod (scripts/ad-hoc/codemod-rm-maxretries.mjs, kept for the record): every rm / rmSync / rmdirSync option object with `recursive: true` and no `maxRetries` gains `maxRetries: 5, retryDelay: 100` — Node itself then retries ENOTEMPTY/EBUSY/EPERM for up to ~0.5 s before giving up. 2243 call sites in 1292 files under tests/, the shared tests/_setup/isolateDataDir.ts exit hook included. Only the option object changes: no call site, assertion or import is touched. Validation: prettier and ESLint (with the frozen suppressions) clean on all 1292 files; a random 20-file sample runs green (quota-redis-store hangs identically on the untouched tree — it needs a Redis on localhost, an environment matter). The four unit shards on this PR are the full run. * fix(quality): let check-forgotten-sibling-tests read a 1,000-file diff The gate shells out to `git diff` through execFileSync with Node's default 1 MB maxBuffer; the 1,292-file codemod in this PR is the first diff large enough to overflow it, and the gate died with `spawnSync git ENOBUFS` before comparing anything. 64 MB is far above any real PR and costs nothing when unused.
247 lines
9.5 KiB
TypeScript
247 lines
9.5 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
|
|
// Isolated DATA_DIR set BEFORE importing anything that touches the DB
|
|
// (injectMemoryAndSkills -> getMemorySettings / retrieveMemories / injectSkills).
|
|
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-mem-skills-"));
|
|
process.env.DATA_DIR = TEST_DATA_DIR;
|
|
|
|
const { getSkillsProviderForFormat, injectMemoryAndSkills } =
|
|
await import("../../open-sse/handlers/chatCore/memorySkillsInjection.ts");
|
|
const { FORMATS } = await import("../../open-sse/translator/formats.ts");
|
|
const core = await import("../../src/lib/db/core.ts");
|
|
|
|
test.after(() => {
|
|
core.resetDbInstance();
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
});
|
|
|
|
// ─── getSkillsProviderForFormat (pure switch) ────────────────────────────────
|
|
|
|
test("getSkillsProviderForFormat maps CLAUDE -> anthropic", () => {
|
|
assert.equal(getSkillsProviderForFormat(FORMATS.CLAUDE), "anthropic");
|
|
});
|
|
|
|
test("getSkillsProviderForFormat maps GEMINI -> google", () => {
|
|
assert.equal(getSkillsProviderForFormat(FORMATS.GEMINI), "google");
|
|
});
|
|
|
|
test("getSkillsProviderForFormat maps OPENAI and any unknown format -> openai (default)", () => {
|
|
assert.equal(getSkillsProviderForFormat(FORMATS.OPENAI), "openai");
|
|
// any other / unknown format falls through to the default branch
|
|
assert.equal(getSkillsProviderForFormat("removed-google-cli"), "openai");
|
|
assert.equal(getSkillsProviderForFormat("codex"), "openai");
|
|
assert.equal(getSkillsProviderForFormat("totally-unknown"), "openai");
|
|
assert.equal(getSkillsProviderForFormat(""), "openai");
|
|
});
|
|
|
|
// ─── injectMemoryAndSkills ───────────────────────────────────────────────────
|
|
|
|
test("injectMemoryAndSkills with memoryOwnerId=null skips both branches and returns the body unchanged", async () => {
|
|
// memoryOwnerId is null -> memorySettings stays null -> the memory guard is false
|
|
// (no getMemorySettings/retrieveMemories) AND the skills guard (memorySettings?.skillsEnabled)
|
|
// is false. The body is returned verbatim with memorySettings=null.
|
|
const body: Record<string, unknown> = {
|
|
model: "gpt-4o",
|
|
messages: [{ role: "user", content: "hello world" }],
|
|
};
|
|
|
|
const result = await injectMemoryAndSkills({
|
|
body,
|
|
memoryOwnerId: null,
|
|
provider: "openai",
|
|
effectiveModel: "gpt-4o",
|
|
sourceFormat: FORMATS.OPENAI,
|
|
targetFormat: FORMATS.OPENAI,
|
|
backgroundReason: null,
|
|
log: null,
|
|
});
|
|
|
|
assert.equal(result.memorySettings, null, "memorySettings is null when no owner is provided");
|
|
// body is returned as-is (same reference, no injection happened)
|
|
assert.equal(result.body, body);
|
|
assert.deepEqual(result.body.messages, [{ role: "user", content: "hello world" }]);
|
|
assert.equal("tools" in result.body, false, "no skills were injected");
|
|
});
|
|
|
|
test("injectMemoryAndSkills with an empty DB resolves settings, finds nothing to inject, returns body unchanged", async () => {
|
|
// memoryOwnerId is set -> getMemorySettings() resolves DB defaults (enabled, skillsEnabled).
|
|
// The body has NO `messages` array (only `input`), so shouldInjectMemory() returns false and
|
|
// the memory-retrieval branch is skipped. The skills branch runs injectSkills(), but the
|
|
// empty DB registry has no skills, so mergedTools.length == existingTools.length and the body
|
|
// is NOT cloned/mutated. This exercises the realistic "nothing to inject" path end-to-end.
|
|
const log = {
|
|
debug: (..._args: unknown[]) => {
|
|
/* swallow */
|
|
},
|
|
};
|
|
const body: Record<string, unknown> = {
|
|
model: "gpt-4o",
|
|
input: [{ role: "user", content: "no messages array here" }],
|
|
};
|
|
|
|
const result = await injectMemoryAndSkills({
|
|
body,
|
|
memoryOwnerId: "owner-empty-db",
|
|
provider: "openai",
|
|
effectiveModel: "gpt-4o",
|
|
sourceFormat: FORMATS.OPENAI,
|
|
targetFormat: FORMATS.OPENAI,
|
|
backgroundReason: null,
|
|
log,
|
|
});
|
|
|
|
// memorySettings was resolved (defaults) — it is a real object, not null.
|
|
assert.ok(result.memorySettings, "memorySettings resolved from DB defaults");
|
|
// PRD-2026-06-19: memory is now OFF by default (skills still default on).
|
|
assert.equal(result.memorySettings.enabled, false);
|
|
assert.equal(result.memorySettings.skillsEnabled, true);
|
|
// No skills in the empty registry -> body returned unchanged (same reference).
|
|
assert.equal(result.body, body);
|
|
assert.equal("tools" in result.body, false, "no skills injected from an empty registry");
|
|
});
|
|
|
|
test("injectMemoryAndSkills resolves cleanly for a CLAUDE-format body with no owner (provider-mapping path)", async () => {
|
|
// Characterizes the no-owner short-circuit for a non-OpenAI source format. Nothing is
|
|
// injected; the function just returns the body untouched and memorySettings=null.
|
|
const body: Record<string, unknown> = {
|
|
model: "claude-3-5-sonnet",
|
|
messages: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
};
|
|
|
|
const result = await injectMemoryAndSkills({
|
|
body,
|
|
memoryOwnerId: null,
|
|
provider: "claude",
|
|
effectiveModel: "claude-3-5-sonnet",
|
|
sourceFormat: FORMATS.CLAUDE,
|
|
targetFormat: FORMATS.CLAUDE,
|
|
backgroundReason: "background-task",
|
|
log: null,
|
|
});
|
|
|
|
assert.equal(result.memorySettings, null);
|
|
assert.equal(result.body, body);
|
|
});
|
|
|
|
test("injectMemoryAndSkills injects memory tools when memory is enabled", async () => {
|
|
const { updateSettings } = await import("../../src/lib/db/settings.ts");
|
|
const { invalidateMemorySettingsCache } = await import("../../src/lib/memory/settings.ts");
|
|
const { MEMORY_BUILTIN_TOOL_NAMES } = await import("../../src/lib/skills/memoryBuiltins.ts");
|
|
|
|
await updateSettings({ memoryEnabled: true, memoryMaxTokens: 2000 });
|
|
invalidateMemorySettingsCache();
|
|
|
|
const body: Record<string, unknown> = {
|
|
model: "gpt-4o",
|
|
messages: [{ role: "user", content: "hello" }],
|
|
tools: [{ type: "function", function: { name: "some_client_tool", description: "x" } }],
|
|
};
|
|
|
|
const result = await injectMemoryAndSkills({
|
|
body,
|
|
memoryOwnerId: "owner-mem-on",
|
|
provider: "openai",
|
|
effectiveModel: "gpt-4o",
|
|
sourceFormat: FORMATS.OPENAI,
|
|
targetFormat: FORMATS.OPENAI,
|
|
backgroundReason: null,
|
|
log: { debug: () => {} },
|
|
});
|
|
|
|
assert.equal(result.memorySettings?.enabled, true);
|
|
const toolNames = (result.body.tools as { function?: { name?: string }; name?: string }[]).map(
|
|
(tool) => tool.function?.name ?? tool.name
|
|
);
|
|
for (const memoryTool of MEMORY_BUILTIN_TOOL_NAMES) {
|
|
assert.ok(
|
|
toolNames.includes(memoryTool),
|
|
`expected ${memoryTool} to be injected into body.tools`
|
|
);
|
|
}
|
|
assert.ok(toolNames.includes("some_client_tool"), "client tools are preserved");
|
|
|
|
invalidateMemorySettingsCache();
|
|
});
|
|
|
|
test("injectMemoryAndSkills does not inject server memory tools for stream requests", async () => {
|
|
const { updateSettings } = await import("../../src/lib/db/settings.ts");
|
|
const { invalidateMemorySettingsCache } = await import("../../src/lib/memory/settings.ts");
|
|
const { MEMORY_BUILTIN_TOOL_NAMES } = await import("../../src/lib/skills/memoryBuiltins.ts");
|
|
|
|
await updateSettings({ memoryEnabled: true, memoryMaxTokens: 2000 });
|
|
invalidateMemorySettingsCache();
|
|
|
|
const body: Record<string, unknown> = {
|
|
model: "gpt-4o",
|
|
stream: true,
|
|
messages: [{ role: "user", content: "hello" }],
|
|
};
|
|
|
|
const result = await injectMemoryAndSkills({
|
|
body,
|
|
memoryOwnerId: "owner-stream",
|
|
provider: "openai",
|
|
effectiveModel: "gpt-4o",
|
|
sourceFormat: FORMATS.OPENAI,
|
|
targetFormat: FORMATS.OPENAI,
|
|
backgroundReason: null,
|
|
log: { debug: () => {} },
|
|
});
|
|
|
|
assert.equal(result.memorySettings?.enabled, true);
|
|
const tools =
|
|
(result.body.tools as { function?: { name?: string }; name?: string }[] | undefined) ?? [];
|
|
const toolNames = tools.map((tool) => tool.function?.name ?? tool.name);
|
|
for (const memoryTool of MEMORY_BUILTIN_TOOL_NAMES) {
|
|
assert.equal(
|
|
toolNames.includes(memoryTool),
|
|
false,
|
|
`expected ${memoryTool} to be absent for stream requests (client-side MCP path)`
|
|
);
|
|
}
|
|
|
|
invalidateMemorySettingsCache();
|
|
});
|
|
|
|
test("injectMemoryAndSkills does not inject memory tools when memory is disabled", async () => {
|
|
const { updateSettings } = await import("../../src/lib/db/settings.ts");
|
|
const { invalidateMemorySettingsCache } = await import("../../src/lib/memory/settings.ts");
|
|
const { MEMORY_BUILTIN_TOOL_NAMES } = await import("../../src/lib/skills/memoryBuiltins.ts");
|
|
|
|
await updateSettings({ memoryEnabled: false });
|
|
invalidateMemorySettingsCache();
|
|
|
|
const body: Record<string, unknown> = {
|
|
model: "gpt-4o",
|
|
messages: [{ role: "user", content: "hello" }],
|
|
};
|
|
|
|
const result = await injectMemoryAndSkills({
|
|
body,
|
|
memoryOwnerId: "owner-mem-off",
|
|
provider: "openai",
|
|
effectiveModel: "gpt-4o",
|
|
sourceFormat: FORMATS.OPENAI,
|
|
targetFormat: FORMATS.OPENAI,
|
|
backgroundReason: null,
|
|
log: { debug: () => {} },
|
|
});
|
|
|
|
const tools =
|
|
(result.body.tools as { function?: { name?: string }; name?: string }[] | undefined) ?? [];
|
|
const toolNames = tools.map((tool) => tool.function?.name ?? tool.name);
|
|
for (const memoryTool of MEMORY_BUILTIN_TOOL_NAMES) {
|
|
assert.equal(
|
|
toolNames.includes(memoryTool),
|
|
false,
|
|
`expected ${memoryTool} to be absent when memory is disabled`
|
|
);
|
|
}
|
|
|
|
invalidateMemorySettingsCache();
|
|
});
|