Files
HaBraid/src/__tests__/database.test.ts
Contributor 5d5336757d feat: HaBraid v0.1.0 — host-following memory visualization engine for Obsidian
- Hybrid BM25 + HNSW vector search with context enrichment
- Knowledge graph with entities, relations, and community detection
- Host-following LLM route with fallback backends
- 3-tier lint system (static + HNSW dup + contradiction detection)
- Q&A Synthesis (Karpathy LLM Wiki pattern)
- 16 MCP tools for agent-driven workflows
- Incremental wiki generation with checkpointing
- SQLite-backed item store with FTS5 + vector indexes
2026-04-18 15:46:42 +09:00

124 lines
3.8 KiB
TypeScript

/**
* Database schema tests for HaBraid.
*
* Tests that the full schema migration runs correctly and produces
* all expected tables, indexes, and triggers.
*/
import Database from "better-sqlite3";
import { getSchemaVersion } from "../db/database.js";
import { createTestDb } from "./setup.js";
describe("Database Schema", () => {
let db: Database.Database;
beforeEach(() => {
db = createTestDb();
});
afterEach(() => {
db.close();
});
it("should have schema version 7 after migration", () => {
const version = getSchemaVersion(db);
expect(version).toBe(7);
});
it("should have all expected tables", () => {
const tables = db
.prepare(
"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name",
)
.all() as Array<{ name: string }>;
const tableNames = tables.map((t) => t.name);
expect(tableNames).toContain("items");
expect(tableNames).toContain("schema_version");
expect(tableNames).toContain("item_vectors");
expect(tableNames).toContain("kg_entities");
expect(tableNames).toContain("kg_relations");
expect(tableNames).toContain("contradictions");
expect(tableNames).toContain("kg_communities");
expect(tableNames).toContain("kg_community_members");
});
it("should have FTS5 virtual table", () => {
const tables = db
.prepare(
"SELECT name FROM sqlite_master WHERE type='table' AND name='items_fts'",
)
.all() as Array<{ name: string }>;
expect(tables.length).toBe(1);
expect(tables[0].name).toBe("items_fts");
});
it("should have WAL mode or memory mode", () => {
// In-memory databases report "memory" instead of "wal"
const result = db.pragma("journal_mode") as Array<{ journal_mode: string }>;
expect(["wal", "memory"]).toContain(result[0].journal_mode);
});
it("should have foreign keys enabled", () => {
const result = db.pragma("foreign_keys") as Array<{ foreign_keys: number }>;
expect(result[0].foreign_keys).toBe(1);
});
it("should have all expected triggers", () => {
const triggers = db
.prepare(
"SELECT name FROM sqlite_master WHERE type='trigger' ORDER BY name",
)
.all() as Array<{ name: string }>;
const triggerNames = triggers.map((t) => t.name);
expect(triggerNames).toContain("items_ai");
expect(triggerNames).toContain("items_ad");
expect(triggerNames).toContain("items_au");
});
it("should have expected indexes", () => {
const indexes = db
.prepare(
"SELECT name FROM sqlite_master WHERE type='index' AND name NOT LIKE 'sqlite_%' ORDER BY name",
)
.all() as Array<{ name: string }>;
const indexNames = indexes.map((i) => i.name);
expect(indexNames).toContain("idx_item_vectors_model");
expect(indexNames).toContain("idx_kg_relations_subject");
expect(indexNames).toContain("idx_kg_relations_object");
expect(indexNames).toContain("idx_kg_entities_type");
expect(indexNames).toContain("idx_contradictions_status");
expect(indexNames).toContain("idx_contradictions_item_a");
expect(indexNames).toContain("idx_contradictions_item_b");
expect(indexNames).toContain("idx_contradictions_field");
expect(indexNames).toContain("idx_kg_community_members_entity");
});
it("items table should have content_hash column", () => {
const cols = db.pragma("table_info(items)") as Array<{
name: string;
type: string;
}>;
const colNames = cols.map((c) => c.name);
expect(colNames).toContain("content_hash");
});
it("contradictions table should have slug columns", () => {
const cols = db.pragma("table_info(contradictions)") as Array<{
name: string;
}>;
const colNames = cols.map((c) => c.name);
expect(colNames).toContain("item_a_slug");
expect(colNames).toContain("item_b_slug");
});
});