test(e2e): nerve sense schema (closes #158) #168
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* E2E-style tests for `nerve sense schema` with a temp HOME and a real sense SQLite file.
|
||||
* `getNerveRoot()` uses `os.homedir()`, which respects `process.env.HOME` on POSIX.
|
||||
*/
|
||||
|
||||
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { DatabaseSync } from "node:sqlite";
|
||||
|
||||
import { runCommand } from "citty";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { senseCommand } from "../commands/sense.js";
|
||||
|
||||
const SENSE_NAME = "e2e-schema-sense";
|
||||
|
||||
function createFakeSenseDb(nerveRoot: string): void {
|
||||
const sensesDir = join(nerveRoot, "data", "senses");
|
||||
mkdirSync(sensesDir, { recursive: true });
|
||||
const dbPath = join(sensesDir, `${SENSE_NAME}.db`);
|
||||
const db = new DatabaseSync(dbPath);
|
||||
db.exec(
|
||||
"CREATE TABLE _signals(id INTEGER PRIMARY KEY, sense TEXT, timestamp INTEGER, payload TEXT)",
|
||||
);
|
||||
db.exec("CREATE TABLE _migrations (name TEXT PRIMARY KEY)");
|
||||
db.close();
|
||||
}
|
||||
|
||||
describe("nerve sense schema CLI (runCommand + temp HOME)", () => {
|
||||
let prevHome: string | undefined;
|
||||
let fakeHome: string;
|
||||
let stdoutSpy: ReturnType<typeof vi.spyOn> | null;
|
||||
let capturedStdout: string;
|
||||
|
||||
beforeEach(() => {
|
||||
capturedStdout = "";
|
||||
stdoutSpy = vi
|
||||
.spyOn(process.stdout, "write")
|
||||
.mockImplementation(
|
||||
(chunk: string | Uint8Array, enc?: BufferEncoding, cb?: (err?: Error | null) => void) => {
|
||||
if (typeof chunk === "string") {
|
||||
capturedStdout += chunk;
|
||||
} else {
|
||||
capturedStdout += Buffer.from(chunk).toString(typeof enc === "string" ? enc : "utf8");
|
||||
}
|
||||
if (typeof cb === "function") {
|
||||
cb();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
);
|
||||
|
||||
prevHome = process.env.HOME;
|
||||
fakeHome = mkdtempSync(join(tmpdir(), "nerve-sense-schema-e2e-"));
|
||||
process.env.HOME = fakeHome;
|
||||
|
||||
const nerveRoot = join(fakeHome, ".uncaged-nerve");
|
||||
createFakeSenseDb(nerveRoot);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
stdoutSpy?.mockRestore();
|
||||
stdoutSpy = null;
|
||||
if (prevHome === undefined) {
|
||||
// biome-ignore lint/performance/noDelete: semantically correct for env cleanup
|
||||
delete process.env.HOME;
|
||||
} else {
|
||||
process.env.HOME = prevHome;
|
||||
}
|
||||
rmSync(fakeHome, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("prints CREATE TABLE statements for the sense database", async () => {
|
||||
await runCommand(senseCommand, { rawArgs: ["schema", SENSE_NAME] });
|
||||
expect(capturedStdout).toMatch(/CREATE TABLE/i);
|
||||
});
|
||||
|
||||
it("includes the _signals table in output", async () => {
|
||||
await runCommand(senseCommand, { rawArgs: ["schema", SENSE_NAME] });
|
||||
expect(capturedStdout).toContain("_signals");
|
||||
});
|
||||
|
||||
it("with --json prints a valid JSON array of SQL strings", async () => {
|
||||
await runCommand(senseCommand, { rawArgs: ["schema", SENSE_NAME, "--json"] });
|
||||
const parsed: unknown = JSON.parse(capturedStdout.trim());
|
||||
expect(Array.isArray(parsed)).toBe(true);
|
||||
const arr = parsed as unknown[];
|
||||
expect(arr.length).toBeGreaterThanOrEqual(1);
|
||||
for (const item of arr) {
|
||||
expect(typeof item).toBe("string");
|
||||
expect(item).toMatch(/CREATE TABLE/i);
|
||||
}
|
||||
const joined = arr.join("\n");
|
||||
expect(joined).toContain("_signals");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user