mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 12:22:14 +03:00
- Remove `new Request('http://localhost/api/keys')` default arg from GET handler in src/app/api/keys/route.ts (line 26)
- Fix api-key-reveal-route.test.mjs to pass explicit Request instead of calling GET() with no args
- Add --output-dir coverage to all c8 scripts in package.json
- Add coverage.reportsDirectory: 'coverage' to vitest.config.ts and vitest.mcp.config.ts
- Fix CHANGELOG.md structure (# Changelog + [Unreleased] to top)
- Remove 30+ stale coverage-* directories from project root
- Coverage: Statements 78.76% | Branches 72.75% | Functions 80.93% | Lines 78.76% (all thresholds passed)
204 lines
6.2 KiB
JavaScript
204 lines
6.2 KiB
JavaScript
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(), "omniroute-memory-store-"));
|
|
process.env.DATA_DIR = TEST_DATA_DIR;
|
|
|
|
const core = await import("../../src/lib/db/core.ts");
|
|
const store = await import("../../src/lib/memory/store.ts");
|
|
const { MemoryType } = await import("../../src/lib/memory/types.ts");
|
|
|
|
async function resetStorage() {
|
|
core.resetDbInstance();
|
|
|
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
try {
|
|
if (fs.existsSync(TEST_DATA_DIR)) {
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
|
}
|
|
break;
|
|
} catch (error) {
|
|
if ((error?.code === "EBUSY" || error?.code === "EPERM") && attempt < 9) {
|
|
await new Promise((resolve) => setTimeout(resolve, 50 * (attempt + 1)));
|
|
} else {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
|
}
|
|
|
|
function insertMemoryRow({
|
|
id,
|
|
apiKeyId = "key-a",
|
|
sessionId = "session-a",
|
|
type = "factual",
|
|
key = "memory:key",
|
|
content = "stored content",
|
|
metadata = "{}",
|
|
createdAt = new Date().toISOString(),
|
|
updatedAt = createdAt,
|
|
expiresAt = null,
|
|
}) {
|
|
const db = core.getDbInstance();
|
|
db.prepare(
|
|
`INSERT INTO memories (
|
|
id, api_key_id, session_id, type, key, content, metadata, created_at, updated_at, expires_at
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
).run(id, apiKeyId, sessionId, type, key, content, metadata, createdAt, updatedAt, expiresAt);
|
|
}
|
|
|
|
test.beforeEach(async () => {
|
|
await resetStorage();
|
|
});
|
|
|
|
test.after(async () => {
|
|
core.resetDbInstance();
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
|
});
|
|
|
|
test("memory store CRUD round-trip persists to the memories table and invalidates cache on update/delete", async () => {
|
|
const expiresAt = new Date("2026-04-06T00:00:00.000Z");
|
|
const created = await store.createMemory({
|
|
apiKeyId: "key-a",
|
|
sessionId: "session-a",
|
|
type: MemoryType.FACTUAL,
|
|
key: "preference:theme",
|
|
content: "User prefers a compact dashboard",
|
|
metadata: { source: "test" },
|
|
expiresAt,
|
|
});
|
|
|
|
const fetched = await store.getMemory(created.id);
|
|
assert.equal(fetched?.apiKeyId, "key-a");
|
|
assert.equal(fetched?.key, "preference:theme");
|
|
assert.deepEqual(fetched?.metadata, { source: "test" });
|
|
assert.equal(fetched?.expiresAt?.toISOString(), expiresAt.toISOString());
|
|
|
|
const updated = await store.updateMemory(created.id, {
|
|
type: MemoryType.EPISODIC,
|
|
key: "decision:layout",
|
|
content: "Switched to the analytics-first layout",
|
|
metadata: { source: "update" },
|
|
expiresAt: null,
|
|
});
|
|
assert.equal(updated, true);
|
|
|
|
const refreshed = await store.getMemory(created.id);
|
|
assert.equal(refreshed?.type, MemoryType.EPISODIC);
|
|
assert.equal(refreshed?.content, "Switched to the analytics-first layout");
|
|
assert.deepEqual(refreshed?.metadata, { source: "update" });
|
|
assert.equal(refreshed?.expiresAt, null);
|
|
|
|
assert.equal(await store.deleteMemory(created.id), true);
|
|
assert.equal(await store.getMemory(created.id), null);
|
|
assert.equal(await store.deleteMemory(created.id), false);
|
|
});
|
|
|
|
test("getMemory returns null for invalid identifiers and tolerates malformed metadata rows", async () => {
|
|
assert.equal(await store.getMemory(""), null);
|
|
assert.equal(await store.getMemory("missing-id"), null);
|
|
|
|
insertMemoryRow({
|
|
id: "broken-metadata",
|
|
metadata: "{not-json",
|
|
content: "Corrupt metadata should not break reads",
|
|
});
|
|
|
|
const fetched = await store.getMemory("broken-metadata");
|
|
assert.equal(fetched?.content, "Corrupt metadata should not break reads");
|
|
assert.deepEqual(fetched?.metadata, {});
|
|
});
|
|
|
|
test("updateMemory returns false for missing ids and listMemories handles an empty store", async () => {
|
|
assert.equal(await store.updateMemory("missing-id", { content: "noop" }), false);
|
|
assert.deepEqual(await store.listMemories({ apiKeyId: "missing-key" }), []);
|
|
});
|
|
|
|
test("listMemories filters by api key, type and session while preserving newest-first ordering", async () => {
|
|
insertMemoryRow({
|
|
id: "mem-1",
|
|
apiKeyId: "key-a",
|
|
sessionId: "session-a",
|
|
type: "factual",
|
|
key: "pref:1",
|
|
content: "Older factual memory",
|
|
createdAt: "2026-04-01T00:00:00.000Z",
|
|
updatedAt: "2026-04-01T00:00:00.000Z",
|
|
});
|
|
insertMemoryRow({
|
|
id: "mem-2",
|
|
apiKeyId: "key-a",
|
|
sessionId: "session-a",
|
|
type: "episodic",
|
|
key: "decision:1",
|
|
content: "Newest episodic memory",
|
|
createdAt: "2026-04-03T00:00:00.000Z",
|
|
updatedAt: "2026-04-03T00:00:00.000Z",
|
|
});
|
|
insertMemoryRow({
|
|
id: "mem-3",
|
|
apiKeyId: "key-b",
|
|
sessionId: "session-b",
|
|
type: "semantic",
|
|
key: "semantic:1",
|
|
content: "Different api key",
|
|
createdAt: "2026-04-02T00:00:00.000Z",
|
|
updatedAt: "2026-04-02T00:00:00.000Z",
|
|
});
|
|
|
|
const allForKeyA = await store.listMemories({ apiKeyId: "key-a" });
|
|
const onlySessionA = await store.listMemories({ apiKeyId: "key-a", sessionId: "session-a" });
|
|
const onlyEpisodic = await store.listMemories({ apiKeyId: "key-a", type: MemoryType.EPISODIC });
|
|
|
|
assert.deepEqual(
|
|
allForKeyA.map((memory) => memory.id),
|
|
["mem-2", "mem-1"]
|
|
);
|
|
assert.deepEqual(
|
|
onlySessionA.map((memory) => memory.id),
|
|
["mem-2", "mem-1"]
|
|
);
|
|
assert.deepEqual(
|
|
onlyEpisodic.map((memory) => memory.id),
|
|
["mem-2"]
|
|
);
|
|
});
|
|
|
|
test("listMemories supports limit and offset pagination even when only offset is provided", async () => {
|
|
insertMemoryRow({
|
|
id: "page-1",
|
|
content: "oldest",
|
|
createdAt: "2026-04-01T00:00:00.000Z",
|
|
updatedAt: "2026-04-01T00:00:00.000Z",
|
|
});
|
|
insertMemoryRow({
|
|
id: "page-2",
|
|
content: "middle",
|
|
createdAt: "2026-04-02T00:00:00.000Z",
|
|
updatedAt: "2026-04-02T00:00:00.000Z",
|
|
});
|
|
insertMemoryRow({
|
|
id: "page-3",
|
|
content: "newest",
|
|
createdAt: "2026-04-03T00:00:00.000Z",
|
|
updatedAt: "2026-04-03T00:00:00.000Z",
|
|
});
|
|
|
|
const paged = await store.listMemories({ apiKeyId: "key-a", limit: 1, offset: 1 });
|
|
const offsetOnly = await store.listMemories({ apiKeyId: "key-a", offset: 1 });
|
|
|
|
assert.deepEqual(
|
|
paged.map((memory) => memory.id),
|
|
["page-2"]
|
|
);
|
|
assert.deepEqual(
|
|
offsetOnly.map((memory) => memory.id),
|
|
["page-2", "page-1"]
|
|
);
|
|
});
|