89 lines
2.9 KiB
TypeScript
89 lines
2.9 KiB
TypeScript
/**
|
|
* Smoke / integration tests for `nerve workflow` and `nerve thread` citty handlers with a real HOME
|
|
* layout and logs.db. `loadDaemonModule` is mocked so tests use workspace
|
|
* `@uncaged/nerve-store` directly (no ~/.uncaged-nerve daemon install required).
|
|
*/
|
|
|
|
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
|
|
import { runCommand } from "citty";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("../workspace-daemon.js", async () => {
|
|
const { createLogStore } = await import("@uncaged/nerve-store");
|
|
return {
|
|
loadDaemonModule: vi.fn(async () => ({ createLogStore })),
|
|
};
|
|
});
|
|
|
|
import { createLogStore } from "@uncaged/nerve-store";
|
|
|
|
import { threadCommand } from "../commands/thread.js";
|
|
|
|
describe("nerve thread CLI (runCommand + temp HOME)", () => {
|
|
let prevHome: string | undefined;
|
|
let fakeHome: string;
|
|
let stdoutSpy: ReturnType<typeof vi.spyOn> | null;
|
|
|
|
beforeEach(() => {
|
|
stdoutSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
|
|
prevHome = process.env.HOME;
|
|
fakeHome = mkdtempSync(join(tmpdir(), "nerve-wf-cli-e2e-"));
|
|
process.env.HOME = fakeHome;
|
|
|
|
const nerveRoot = join(fakeHome, ".uncaged-nerve");
|
|
mkdirSync(join(nerveRoot, "data"), { recursive: true });
|
|
const dbPath = join(nerveRoot, "data", "logs.db");
|
|
const store = createLogStore(dbPath);
|
|
|
|
store.upsertWorkflowRun(
|
|
{ source: "workflow", type: "started", refId: "e2e-run", payload: "{}", timestamp: 5000 },
|
|
{ runId: "e2e-run", workflow: "demo", status: "completed", timestamp: 5000, exitCode: 0 },
|
|
);
|
|
store.append({
|
|
source: "workflow",
|
|
type: "completed",
|
|
refId: "e2e-run",
|
|
payload: null,
|
|
timestamp: 5001,
|
|
});
|
|
store.append({
|
|
source: "workflow",
|
|
type: "thread_command_event",
|
|
refId: "e2e-run",
|
|
payload: JSON.stringify({ type: "step", role: "bot", content: "hello" }),
|
|
timestamp: 5100,
|
|
});
|
|
store.close();
|
|
});
|
|
|
|
afterEach(() => {
|
|
stdoutSpy?.mockRestore();
|
|
stdoutSpy = null;
|
|
if (prevHome === undefined) {
|
|
process.env.HOME = undefined;
|
|
} else {
|
|
process.env.HOME = prevHome;
|
|
}
|
|
rmSync(fakeHome, { recursive: true, force: true });
|
|
});
|
|
|
|
it("thread list --all completes without throwing", async () => {
|
|
await expect(runCommand(threadCommand, { rawArgs: ["list", "--all"] })).resolves.toBeDefined();
|
|
});
|
|
|
|
it("thread inspect <runId> completes without throwing", async () => {
|
|
await expect(
|
|
runCommand(threadCommand, { rawArgs: ["inspect", "e2e-run", "--limit", "10"] }),
|
|
).resolves.toBeDefined();
|
|
});
|
|
|
|
it("thread show <runId> completes without throwing (role rounds path)", async () => {
|
|
await expect(
|
|
runCommand(threadCommand, { rawArgs: ["show", "e2e-run", "--budget", "50000"] }),
|
|
).resolves.toBeDefined();
|
|
});
|
|
});
|