test: dedupe and optimize test suites
This commit is contained in:
@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { Command } from "commander";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const getMemorySearchManager = vi.fn();
|
||||
const loadConfig = vi.fn(() => ({}));
|
||||
@@ -20,11 +20,21 @@ vi.mock("../agents/agent-scope.js", () => ({
|
||||
resolveDefaultAgentId,
|
||||
}));
|
||||
|
||||
afterEach(async () => {
|
||||
let registerMemoryCli: typeof import("./memory-cli.js").registerMemoryCli;
|
||||
let defaultRuntime: typeof import("../runtime.js").defaultRuntime;
|
||||
let isVerbose: typeof import("../globals.js").isVerbose;
|
||||
let setVerbose: typeof import("../globals.js").setVerbose;
|
||||
|
||||
beforeAll(async () => {
|
||||
({ registerMemoryCli } = await import("./memory-cli.js"));
|
||||
({ defaultRuntime } = await import("../runtime.js"));
|
||||
({ isVerbose, setVerbose } = await import("../globals.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
getMemorySearchManager.mockReset();
|
||||
process.exitCode = undefined;
|
||||
const { setVerbose } = await import("../globals.js");
|
||||
setVerbose(false);
|
||||
});
|
||||
|
||||
@@ -55,15 +65,45 @@ describe("memory cli", () => {
|
||||
}
|
||||
|
||||
async function runMemoryCli(args: string[]) {
|
||||
const { registerMemoryCli } = await import("./memory-cli.js");
|
||||
const program = new Command();
|
||||
program.name("test");
|
||||
registerMemoryCli(program);
|
||||
await program.parseAsync(["memory", ...args], { from: "user" });
|
||||
}
|
||||
|
||||
async function withQmdIndexDb(content: string, run: (dbPath: string) => Promise<void>) {
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-cli-qmd-index-"));
|
||||
const dbPath = path.join(tmpDir, "index.sqlite");
|
||||
try {
|
||||
await fs.writeFile(dbPath, content, "utf-8");
|
||||
await run(dbPath);
|
||||
} finally {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
async function expectCloseFailureAfterCommand(params: {
|
||||
args: string[];
|
||||
manager: Record<string, unknown>;
|
||||
beforeExpect?: () => void;
|
||||
}) {
|
||||
const close = vi.fn(async () => {
|
||||
throw new Error("close boom");
|
||||
});
|
||||
mockManager({ ...params.manager, close });
|
||||
|
||||
const error = vi.spyOn(defaultRuntime, "error").mockImplementation(() => {});
|
||||
await runMemoryCli(params.args);
|
||||
|
||||
params.beforeExpect?.();
|
||||
expect(close).toHaveBeenCalled();
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Memory manager close failed: close boom"),
|
||||
);
|
||||
expect(process.exitCode).toBeUndefined();
|
||||
}
|
||||
|
||||
it("prints vector status when available", async () => {
|
||||
const { defaultRuntime } = await import("../runtime.js");
|
||||
const close = vi.fn(async () => {});
|
||||
mockManager({
|
||||
probeVectorAvailability: vi.fn(async () => true),
|
||||
@@ -97,7 +137,6 @@ describe("memory cli", () => {
|
||||
});
|
||||
|
||||
it("prints vector error when unavailable", async () => {
|
||||
const { defaultRuntime } = await import("../runtime.js");
|
||||
const close = vi.fn(async () => {});
|
||||
mockManager({
|
||||
probeVectorAvailability: vi.fn(async () => false),
|
||||
@@ -122,7 +161,6 @@ describe("memory cli", () => {
|
||||
});
|
||||
|
||||
it("prints embeddings status when deep", async () => {
|
||||
const { defaultRuntime } = await import("../runtime.js");
|
||||
const close = vi.fn(async () => {});
|
||||
const probeEmbeddingAvailability = vi.fn(async () => ({ ok: true }));
|
||||
mockManager({
|
||||
@@ -141,7 +179,6 @@ describe("memory cli", () => {
|
||||
});
|
||||
|
||||
it("enables verbose logging with --verbose", async () => {
|
||||
const { isVerbose } = await import("../globals.js");
|
||||
const close = vi.fn(async () => {});
|
||||
mockManager({
|
||||
probeVectorAvailability: vi.fn(async () => true),
|
||||
@@ -155,28 +192,16 @@ describe("memory cli", () => {
|
||||
});
|
||||
|
||||
it("logs close failure after status", async () => {
|
||||
const { defaultRuntime } = await import("../runtime.js");
|
||||
const close = vi.fn(async () => {
|
||||
throw new Error("close boom");
|
||||
await expectCloseFailureAfterCommand({
|
||||
args: ["status"],
|
||||
manager: {
|
||||
probeVectorAvailability: vi.fn(async () => true),
|
||||
status: () => makeMemoryStatus({ files: 1, chunks: 1 }),
|
||||
},
|
||||
});
|
||||
mockManager({
|
||||
probeVectorAvailability: vi.fn(async () => true),
|
||||
status: () => makeMemoryStatus({ files: 1, chunks: 1 }),
|
||||
close,
|
||||
});
|
||||
|
||||
const error = vi.spyOn(defaultRuntime, "error").mockImplementation(() => {});
|
||||
await runMemoryCli(["status"]);
|
||||
|
||||
expect(close).toHaveBeenCalled();
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Memory manager close failed: close boom"),
|
||||
);
|
||||
expect(process.exitCode).toBeUndefined();
|
||||
});
|
||||
|
||||
it("reindexes on status --index", async () => {
|
||||
const { defaultRuntime } = await import("../runtime.js");
|
||||
const close = vi.fn(async () => {});
|
||||
const sync = vi.fn(async () => {});
|
||||
const probeEmbeddingAvailability = vi.fn(async () => ({ ok: true }));
|
||||
@@ -197,7 +222,6 @@ describe("memory cli", () => {
|
||||
});
|
||||
|
||||
it("closes manager after index", async () => {
|
||||
const { defaultRuntime } = await import("../runtime.js");
|
||||
const close = vi.fn(async () => {});
|
||||
const sync = vi.fn(async () => {});
|
||||
mockManager({ sync, close });
|
||||
@@ -211,69 +235,51 @@ describe("memory cli", () => {
|
||||
});
|
||||
|
||||
it("logs qmd index file path and size after index", async () => {
|
||||
const { defaultRuntime } = await import("../runtime.js");
|
||||
const close = vi.fn(async () => {});
|
||||
const sync = vi.fn(async () => {});
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-cli-qmd-index-"));
|
||||
const dbPath = path.join(tmpDir, "index.sqlite");
|
||||
await fs.writeFile(dbPath, "sqlite-bytes", "utf-8");
|
||||
mockManager({ sync, status: () => ({ backend: "qmd", dbPath }), close });
|
||||
await withQmdIndexDb("sqlite-bytes", async (dbPath) => {
|
||||
mockManager({ sync, status: () => ({ backend: "qmd", dbPath }), close });
|
||||
|
||||
const log = vi.spyOn(defaultRuntime, "log").mockImplementation(() => {});
|
||||
await runMemoryCli(["index"]);
|
||||
const log = vi.spyOn(defaultRuntime, "log").mockImplementation(() => {});
|
||||
await runMemoryCli(["index"]);
|
||||
|
||||
expectCliSync(sync);
|
||||
expect(log).toHaveBeenCalledWith(expect.stringContaining("QMD index: "));
|
||||
expect(log).toHaveBeenCalledWith("Memory index updated (main).");
|
||||
expect(close).toHaveBeenCalled();
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
expectCliSync(sync);
|
||||
expect(log).toHaveBeenCalledWith(expect.stringContaining("QMD index: "));
|
||||
expect(log).toHaveBeenCalledWith("Memory index updated (main).");
|
||||
expect(close).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("fails index when qmd db file is empty", async () => {
|
||||
const { defaultRuntime } = await import("../runtime.js");
|
||||
const close = vi.fn(async () => {});
|
||||
const sync = vi.fn(async () => {});
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-cli-qmd-index-"));
|
||||
const dbPath = path.join(tmpDir, "index.sqlite");
|
||||
await fs.writeFile(dbPath, "", "utf-8");
|
||||
mockManager({ sync, status: () => ({ backend: "qmd", dbPath }), close });
|
||||
await withQmdIndexDb("", async (dbPath) => {
|
||||
mockManager({ sync, status: () => ({ backend: "qmd", dbPath }), close });
|
||||
|
||||
const error = vi.spyOn(defaultRuntime, "error").mockImplementation(() => {});
|
||||
await runMemoryCli(["index"]);
|
||||
const error = vi.spyOn(defaultRuntime, "error").mockImplementation(() => {});
|
||||
await runMemoryCli(["index"]);
|
||||
|
||||
expectCliSync(sync);
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Memory index failed (main): QMD index file is empty"),
|
||||
);
|
||||
expect(close).toHaveBeenCalled();
|
||||
expect(process.exitCode).toBe(1);
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
expectCliSync(sync);
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Memory index failed (main): QMD index file is empty"),
|
||||
);
|
||||
expect(close).toHaveBeenCalled();
|
||||
expect(process.exitCode).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it("logs close failures without failing the command", async () => {
|
||||
const { defaultRuntime } = await import("../runtime.js");
|
||||
const close = vi.fn(async () => {
|
||||
throw new Error("close boom");
|
||||
});
|
||||
const sync = vi.fn(async () => {});
|
||||
mockManager({ sync, close });
|
||||
|
||||
const error = vi.spyOn(defaultRuntime, "error").mockImplementation(() => {});
|
||||
await runMemoryCli(["index"]);
|
||||
|
||||
expectCliSync(sync);
|
||||
expect(close).toHaveBeenCalled();
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Memory manager close failed: close boom"),
|
||||
);
|
||||
expect(process.exitCode).toBeUndefined();
|
||||
await expectCloseFailureAfterCommand({
|
||||
args: ["index"],
|
||||
manager: { sync },
|
||||
beforeExpect: () => {
|
||||
expectCliSync(sync);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("logs close failure after search", async () => {
|
||||
const { defaultRuntime } = await import("../runtime.js");
|
||||
const close = vi.fn(async () => {
|
||||
throw new Error("close boom");
|
||||
});
|
||||
const search = vi.fn(async () => [
|
||||
{
|
||||
path: "memory/2026-01-12.md",
|
||||
@@ -283,21 +289,16 @@ describe("memory cli", () => {
|
||||
snippet: "Hello",
|
||||
},
|
||||
]);
|
||||
mockManager({ search, close });
|
||||
|
||||
const error = vi.spyOn(defaultRuntime, "error").mockImplementation(() => {});
|
||||
await runMemoryCli(["search", "hello"]);
|
||||
|
||||
expect(search).toHaveBeenCalled();
|
||||
expect(close).toHaveBeenCalled();
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Memory manager close failed: close boom"),
|
||||
);
|
||||
expect(process.exitCode).toBeUndefined();
|
||||
await expectCloseFailureAfterCommand({
|
||||
args: ["search", "hello"],
|
||||
manager: { search },
|
||||
beforeExpect: () => {
|
||||
expect(search).toHaveBeenCalled();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("closes manager after search error", async () => {
|
||||
const { defaultRuntime } = await import("../runtime.js");
|
||||
const close = vi.fn(async () => {});
|
||||
const search = vi.fn(async () => {
|
||||
throw new Error("boom");
|
||||
|
||||
Reference in New Issue
Block a user