Files
OmniRoute/tests/unit/chatcore-memory-skills-injection.test.ts
Diego Rodrigues de Sa e Souza 3d4f3e4960 test(infra): retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) (#11968)
* 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.
2026-08-29 01:17:40 -03:00

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();
});