disclaw/tests/integration/db-init.test.ts
Nick Tabeling 3dd75ddc30
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
refactor(DIS-108): extract DB schema to src/db/schema.sql (SSOT)
Move all CREATE TABLE/INDEX DDL from database.ts to schema.sql.
database.ts loads and executes schema.sql via db.exec().
Add integration test verifying idempotent schema init.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 17:50:14 +02:00

97 lines
2.9 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach } from "vitest";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import { DisclawDatabase } from "../../src/db/database";
/**
* Integration tests for DisclawDatabase schema initialization.
*
* Each test uses a fresh SQLite file in os.tmpdir() to ensure full isolation.
* The DB file is cleaned up after each test.
*/
function makeTmpDbPath(): string {
return path.join(os.tmpdir(), `disclaw-test-${Date.now()}-${Math.random().toString(36).slice(2)}.db`);
}
describe("db-init: schema initialization", () => {
let dbPath: string;
let db: DisclawDatabase;
beforeEach(() => {
dbPath = makeTmpDbPath();
});
afterEach(() => {
try {
db.close();
} catch {
// ignore if already closed
}
try {
fs.unlinkSync(dbPath);
} catch {
// best-effort cleanup
}
});
// (a) All expected tables exist after initialization
it("creates all expected tables on first init", () => {
db = new DisclawDatabase(dbPath);
// Query sqlite_master for user-created tables
const rawDb = (db as unknown as { db: import("better-sqlite3").Database }).db;
const tables = rawDb
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name")
.all() as { name: string }[];
const tableNames = tables.map((t) => t.name);
expect(tableNames).toContain("workspaces");
expect(tableNames).toContain("conversations");
});
// (b) Initializing the same DB path twice is idempotent (IF NOT EXISTS)
it("is idempotent when initialized twice (IF NOT EXISTS)", () => {
db = new DisclawDatabase(dbPath);
db.close();
// Second initialization must not throw
expect(() => {
db = new DisclawDatabase(dbPath);
}).not.toThrow();
const rawDb = (db as unknown as { db: import("better-sqlite3").Database }).db;
const tables = rawDb
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name")
.all() as { name: string }[];
const tableNames = tables.map((t) => t.name);
expect(tableNames).toContain("workspaces");
expect(tableNames).toContain("conversations");
});
// (c) Basic CRUD: create a workspace and retrieve it by channel_id
it("supports basic workspace CRUD", () => {
db = new DisclawDatabase(dbPath);
const created = db.createWorkspace(
"channel-001",
"guild-001",
"test-agent",
"/tmp/test-agent"
);
expect(created.id).toBeGreaterThan(0);
expect(created.channel_id).toBe("channel-001");
expect(created.guild_id).toBe("guild-001");
expect(created.agent_name).toBe("test-agent");
expect(created.workspace_path).toBe("/tmp/test-agent");
expect(created.created_at).toBeTruthy();
const fetched = db.getWorkspaceByChannelId("channel-001");
expect(fetched).toBeDefined();
expect(fetched!.id).toBe(created.id);
expect(fetched!.agent_name).toBe("test-agent");
});
});