mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-16 20:02:45 +03:00
* test(infra): retry recursive temp-dir removal on main (main twin of #11968)
`main` has been red since b342c1a361 on the vitest and integration gates:
✖ tests/unit/autoCombo/provider-family-combos.test.ts > auto/<family>
✖ chat pipeline applies Codex OAuth fingerprint and priority tier inside combos
Both call resetStorage() from beforeEach, which does an fs.rmSync(TEST_DATA_DIR,
{recursive: true, force: true}) with no retry, and intermittently loses the race
with a not-yet-released SQLite handle (ENOTEMPTY).
release/v3.8.51 fixed this in #11968 with a mechanical codemod adding
maxRetries/retryDelay to every recursive rm/rmSync/rmdirSync under tests/, but
that PR landed only on the release branch. Because main only receives work at
the release squash, it stayed broken for the whole cycle — and repo-wide gates
then turn every open PR into main red on checks unrelated to their diff.
This is the --base main twin: re-runs the same codemod that already shipped on
the release branch (scripts/ad-hoc/codemod-rm-maxretries.mjs), so the two
branches converge on identical test-teardown semantics. Test-only; no product
logic is touched.
The remaining three failures reported on #12133 (unit full suite exceeding its
4800s ceiling, package-artifact exceeding 1200s, and the boot-smoke that is
skipped as a consequence) are runner-contention timeouts, not code defects —
validate-release-green.mjs runs those heavy gates concurrently on one shared
hosted runner. There is no fix to port for those.
* chore(scripts): carry the rm-maxretries codemod onto main alongside its output
The codemod that generated the previous commit lives in the repo on
release/v3.8.51 (added by #11968) but was never on main. Bringing it over keeps
the tool next to the change it produced, so the transformation stays
reproducible and auditable from either branch.
204 lines
7.5 KiB
TypeScript
204 lines
7.5 KiB
TypeScript
/**
|
|
* tests/unit/memory-retrieve-preview.test.ts
|
|
*
|
|
* Plan 21 F5 — retrieval.ts: retrievePreview function.
|
|
*
|
|
* Cases:
|
|
* A) retrievePreview returns correct bundle shape for exact strategy
|
|
* B) retrievePreview with semantic strategy + no vec → fallbackReason non-null
|
|
* C) retrievePreview with apiKeyId=null → tests global scope (all memories)
|
|
* D) retrievePreview respects the limit parameter
|
|
* E) retrievePreview respects maxTokens budget
|
|
* F) retrievePreview with empty DB returns empty items
|
|
*/
|
|
|
|
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";
|
|
|
|
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omr-retrieve-preview-"));
|
|
process.env.DATA_DIR = TEST_DATA_DIR;
|
|
process.env.DISABLE_SQLITE_AUTO_BACKUP = "true";
|
|
process.env.VECTOR_STORE_DISABLE_VEC = "true";
|
|
|
|
const core = await import("../../src/lib/db/core.ts");
|
|
|
|
function cleanup() {
|
|
core.resetDbInstance();
|
|
if (fs.existsSync(TEST_DATA_DIR)) {
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
}
|
|
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
|
}
|
|
|
|
test.afterEach(() => cleanup());
|
|
test.after(() => {
|
|
if (fs.existsSync(TEST_DATA_DIR)) {
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
}
|
|
});
|
|
|
|
function insertMemory(
|
|
db: ReturnType<typeof core.getDbInstance>,
|
|
id: string,
|
|
apiKeyId: string,
|
|
content: string,
|
|
key?: string
|
|
) {
|
|
db.prepare(
|
|
`INSERT INTO memories (id, api_key_id, session_id, type, key, content, metadata, created_at, updated_at, expires_at)
|
|
VALUES (?, ?, ?, 'factual', ?, ?, '{}', datetime('now'), datetime('now'), NULL)`
|
|
).run(id, apiKeyId, "", key ?? `key-${id}`, content);
|
|
}
|
|
|
|
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
|
|
test("retrievePreview: exact strategy returns correct RetrievePreviewBundle shape", async () => {
|
|
const db = core.getDbInstance();
|
|
insertMemory(db, "prev-1", "api-prev", "TypeScript is great for large projects.");
|
|
insertMemory(db, "prev-2", "api-prev", "JavaScript is flexible and dynamic.");
|
|
|
|
const { retrievePreview } = await import("../../src/lib/memory/retrieval.ts");
|
|
|
|
const bundle = await retrievePreview("api-prev", "TypeScript", {
|
|
strategy: "exact",
|
|
maxTokens: 2000,
|
|
limit: 10,
|
|
});
|
|
|
|
// Structural assertions
|
|
assert.ok(Array.isArray(bundle.items), "items must be an array");
|
|
assert.ok(typeof bundle.totalTokens === "number", "totalTokens must be a number");
|
|
assert.equal(bundle.budgetMaxTokens, 2000, "budgetMaxTokens must match the passed maxTokens");
|
|
|
|
const res = bundle.resolution;
|
|
assert.equal(res.strategyUsed, "exact");
|
|
assert.equal(res.rerankApplied, false);
|
|
assert.ok("fallbackReason" in res, "resolution must have fallbackReason field");
|
|
assert.ok("vectorStore" in res, "resolution must have vectorStore field");
|
|
assert.ok("embeddingSource" in res, "resolution must have embeddingSource field");
|
|
assert.ok("embeddingModel" in res, "resolution must have embeddingModel field");
|
|
});
|
|
|
|
test("retrievePreview: each item has required fields (tier, score, tokens, memory)", async () => {
|
|
const db = core.getDbInstance();
|
|
insertMemory(db, "item-1", "api-item", "Content about machine learning techniques.");
|
|
insertMemory(db, "item-2", "api-item", "Content about deep learning frameworks.");
|
|
|
|
const { retrievePreview } = await import("../../src/lib/memory/retrieval.ts");
|
|
|
|
const bundle = await retrievePreview("api-item", "machine learning", {
|
|
strategy: "exact",
|
|
maxTokens: 2000,
|
|
limit: 10,
|
|
});
|
|
|
|
for (const item of bundle.items) {
|
|
assert.ok("memory" in item, "item must have memory");
|
|
assert.ok("score" in item, "item must have score");
|
|
assert.ok("tokens" in item, "item must have tokens");
|
|
assert.ok("tier" in item, "item must have tier");
|
|
assert.ok("vecScore" in item, "item must have vecScore");
|
|
assert.ok("ftsScore" in item, "item must have ftsScore");
|
|
assert.equal(typeof item.tokens, "number", "tokens must be a number");
|
|
assert.equal(typeof item.score, "number", "score must be a number");
|
|
// tier should be one of the valid values
|
|
assert.ok(
|
|
["fts5", "vector", "hybrid-rrf", "qdrant"].includes(item.tier),
|
|
`tier '${item.tier}' must be a valid tier value`
|
|
);
|
|
}
|
|
});
|
|
|
|
test("retrievePreview: semantic strategy with no vec store → fallbackReason is non-null", async () => {
|
|
const db = core.getDbInstance();
|
|
insertMemory(db, "sem-prev-1", "api-smprev", "Astronomy is the study of celestial bodies.");
|
|
|
|
const { retrievePreview } = await import("../../src/lib/memory/retrieval.ts");
|
|
|
|
const bundle = await retrievePreview("api-smprev", "celestial bodies", {
|
|
strategy: "semantic",
|
|
maxTokens: 2000,
|
|
limit: 10,
|
|
});
|
|
|
|
// No embedding source configured → fallback
|
|
assert.ok(
|
|
bundle.resolution.fallbackReason !== null ||
|
|
bundle.resolution.strategyUsed !== "semantic",
|
|
"semantic preview with no vec store should indicate fallback"
|
|
);
|
|
assert.ok(Array.isArray(bundle.items), "items must be array even in fallback");
|
|
});
|
|
|
|
test("retrievePreview: apiKeyId=null scopes to all memories", async () => {
|
|
const db = core.getDbInstance();
|
|
insertMemory(db, "global-1", "api-g1", "Global memory one.");
|
|
insertMemory(db, "global-2", "api-g2", "Global memory two.");
|
|
|
|
const { retrievePreview } = await import("../../src/lib/memory/retrieval.ts");
|
|
|
|
const bundle = await retrievePreview(null, "global", {
|
|
strategy: "exact",
|
|
maxTokens: 2000,
|
|
limit: 10,
|
|
});
|
|
|
|
// Should see memories from both apiKeyIds
|
|
assert.ok(Array.isArray(bundle.items));
|
|
const apiKeyIds = bundle.items.map((i) => i.memory.apiKeyId);
|
|
// At least one item should be present (global scope)
|
|
assert.ok(bundle.items.length >= 0, "global scope must return items array");
|
|
});
|
|
|
|
test("retrievePreview: respects limit parameter", async () => {
|
|
const db = core.getDbInstance();
|
|
for (let i = 1; i <= 10; i++) {
|
|
insertMemory(db, `lim-${i}`, "api-lim", `Memory ${i} content.`, `lim-${i}`);
|
|
}
|
|
|
|
const { retrievePreview } = await import("../../src/lib/memory/retrieval.ts");
|
|
|
|
const bundle = await retrievePreview("api-lim", "memory", {
|
|
strategy: "exact",
|
|
maxTokens: 10000,
|
|
limit: 3,
|
|
});
|
|
|
|
assert.ok(bundle.items.length <= 3, `items.length ${bundle.items.length} must be ≤ limit (3)`);
|
|
});
|
|
|
|
test("retrievePreview: empty DB returns empty items", async () => {
|
|
core.getDbInstance(); // trigger migrations only
|
|
|
|
const { retrievePreview } = await import("../../src/lib/memory/retrieval.ts");
|
|
|
|
const bundle = await retrievePreview("api-empty", "anything", {
|
|
strategy: "exact",
|
|
maxTokens: 2000,
|
|
limit: 10,
|
|
});
|
|
|
|
assert.deepEqual(bundle.items, [], "empty DB should return empty items array");
|
|
assert.equal(bundle.totalTokens, 0);
|
|
});
|
|
|
|
test("retrievePreview: totalTokens equals sum of item.tokens", async () => {
|
|
const db = core.getDbInstance();
|
|
insertMemory(db, "tok-1", "api-tok", "Short text."); // ~3 tokens
|
|
insertMemory(db, "tok-2", "api-tok", "Another short text."); // ~5 tokens
|
|
|
|
const { retrievePreview } = await import("../../src/lib/memory/retrieval.ts");
|
|
|
|
const bundle = await retrievePreview("api-tok", "short", {
|
|
strategy: "exact",
|
|
maxTokens: 2000,
|
|
limit: 10,
|
|
});
|
|
|
|
const sumFromItems = bundle.items.reduce((acc, i) => acc + i.tokens, 0);
|
|
assert.equal(bundle.totalTokens, sumFromItems, "totalTokens must equal sum of item.tokens");
|
|
});
|