| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877 |
- /**
- * sdk.test.ts - Unit tests for the QMD SDK (library mode)
- *
- * Tests the public API exposed via `@tobilu/qmd` (src/index.ts).
- * Uses inline config (no YAML files) to verify the SDK works self-contained.
- */
- import { describe, test, expect, beforeAll, afterAll, beforeEach, afterEach } from "vitest";
- import { mkdtemp, writeFile, mkdir, rm } from "node:fs/promises";
- import { tmpdir } from "node:os";
- import { join } from "node:path";
- import { existsSync, writeFileSync, mkdirSync, readFileSync } from "node:fs";
- import YAML from "yaml";
- import {
- createStore,
- type QMDStore,
- type CollectionConfig,
- type StoreOptions,
- } from "../src/index.js";
- // =============================================================================
- // Test Helpers
- // =============================================================================
- let testDir: string;
- let docsDir: string;
- let notesDir: string;
- beforeAll(async () => {
- testDir = await mkdtemp(join(tmpdir(), "qmd-sdk-test-"));
- docsDir = join(testDir, "docs");
- notesDir = join(testDir, "notes");
- // Create test directories with sample markdown files
- await mkdir(docsDir, { recursive: true });
- await mkdir(notesDir, { recursive: true });
- await writeFile(join(docsDir, "readme.md"), "# Getting Started\n\nThis is the getting started guide for the project.\n");
- await writeFile(join(docsDir, "auth.md"), "# Authentication\n\nAuthentication uses JWT tokens for session management.\nUsers log in with email and password.\n");
- await writeFile(join(docsDir, "api.md"), "# API Reference\n\n## Endpoints\n\n### POST /login\nAuthenticate a user.\n\n### GET /users\nList all users.\n");
- await writeFile(join(notesDir, "meeting-2025-01.md"), "# January Planning Meeting\n\nDiscussed Q1 roadmap and resource allocation.\n");
- await writeFile(join(notesDir, "meeting-2025-02.md"), "# February Standup\n\nReviewed sprint progress. Authentication feature is on track.\n");
- await writeFile(join(notesDir, "ideas.md"), "# Project Ideas\n\n- Build a search engine\n- Create a knowledge base\n- Implement vector search\n");
- });
- afterAll(async () => {
- try {
- await rm(testDir, { recursive: true, force: true });
- } catch {
- // Ignore cleanup errors
- }
- });
- function freshDbPath(): string {
- return join(testDir, `test-${Date.now()}-${Math.random().toString(36).slice(2)}.sqlite`);
- }
- // =============================================================================
- // Constructor Tests
- // =============================================================================
- describe("createStore", () => {
- test("creates store with inline config", () => {
- const store = createStore({
- dbPath: freshDbPath(),
- config: {
- collections: {
- docs: { path: docsDir, pattern: "**/*.md" },
- },
- },
- });
- expect(store).toBeDefined();
- expect(store.dbPath).toBeTruthy();
- expect(store.internal).toBeDefined();
- store.close();
- });
- test("creates store with YAML config file", () => {
- const configPath = join(testDir, "test-config.yml");
- const config: CollectionConfig = {
- collections: {
- docs: { path: docsDir, pattern: "**/*.md" },
- },
- };
- writeFileSync(configPath, YAML.stringify(config));
- const store = createStore({
- dbPath: freshDbPath(),
- configPath,
- });
- expect(store).toBeDefined();
- store.close();
- });
- test("throws if dbPath is missing", () => {
- expect(() =>
- createStore({ dbPath: "", config: { collections: {} } })
- ).toThrow("dbPath is required");
- });
- test("throws if neither configPath nor config is provided", () => {
- expect(() =>
- createStore({ dbPath: freshDbPath() } as StoreOptions)
- ).toThrow("Either configPath or config is required");
- });
- test("throws if both configPath and config are provided", () => {
- expect(() =>
- createStore({
- dbPath: freshDbPath(),
- configPath: "/some/path.yml",
- config: { collections: {} },
- })
- ).toThrow("Provide either configPath or config, not both");
- });
- test("creates database file on disk", () => {
- const dbPath = freshDbPath();
- const store = createStore({
- dbPath,
- config: { collections: {} },
- });
- expect(existsSync(dbPath)).toBe(true);
- store.close();
- });
- test("store.dbPath matches the provided path", () => {
- const dbPath = freshDbPath();
- const store = createStore({
- dbPath,
- config: { collections: {} },
- });
- expect(store.dbPath).toBe(dbPath);
- store.close();
- });
- });
- // =============================================================================
- // Collection Management Tests
- // =============================================================================
- describe("collection management", () => {
- let store: QMDStore;
- beforeEach(() => {
- store = createStore({
- dbPath: freshDbPath(),
- config: { collections: {} },
- });
- });
- afterEach(() => {
- store.close();
- });
- test("addCollection adds a collection to inline config", () => {
- store.addCollection("docs", { path: docsDir, pattern: "**/*.md" });
- const collections = store.listCollections();
- const names = collections.map(c => c.name);
- expect(names).toContain("docs");
- });
- test("addCollection with default pattern", () => {
- store.addCollection("notes", { path: notesDir });
- const collections = store.listCollections();
- expect(collections.find(c => c.name === "notes")).toBeDefined();
- });
- test("removeCollection removes existing collection", () => {
- store.addCollection("docs", { path: docsDir, pattern: "**/*.md" });
- const removed = store.removeCollection("docs");
- expect(removed).toBe(true);
- const collections = store.listCollections();
- expect(collections.map(c => c.name)).not.toContain("docs");
- });
- test("removeCollection returns false for non-existent collection", () => {
- const removed = store.removeCollection("nonexistent");
- expect(removed).toBe(false);
- });
- test("renameCollection renames a collection", () => {
- store.addCollection("old-name", { path: docsDir, pattern: "**/*.md" });
- const renamed = store.renameCollection("old-name", "new-name");
- expect(renamed).toBe(true);
- const names = store.listCollections().map(c => c.name);
- expect(names).toContain("new-name");
- expect(names).not.toContain("old-name");
- });
- test("renameCollection returns false for non-existent source", () => {
- const renamed = store.renameCollection("nonexistent", "new-name");
- expect(renamed).toBe(false);
- });
- test("renameCollection throws if target exists", () => {
- store.addCollection("a", { path: docsDir, pattern: "**/*.md" });
- store.addCollection("b", { path: notesDir, pattern: "**/*.md" });
- expect(() => store.renameCollection("a", "b")).toThrow("already exists");
- });
- test("listCollections returns empty array for empty config", () => {
- const collections = store.listCollections();
- expect(collections).toEqual([]);
- });
- test("multiple collections can be added", () => {
- store.addCollection("docs", { path: docsDir, pattern: "**/*.md" });
- store.addCollection("notes", { path: notesDir, pattern: "**/*.md" });
- const names = store.listCollections().map(c => c.name);
- expect(names).toContain("docs");
- expect(names).toContain("notes");
- expect(names).toHaveLength(2);
- });
- });
- // =============================================================================
- // Context Management Tests
- // =============================================================================
- describe("context management", () => {
- let store: QMDStore;
- beforeEach(() => {
- store = createStore({
- dbPath: freshDbPath(),
- config: {
- collections: {
- docs: { path: docsDir, pattern: "**/*.md" },
- notes: { path: notesDir, pattern: "**/*.md" },
- },
- },
- });
- });
- afterEach(() => {
- store.close();
- });
- test("addContext adds context to a collection path", () => {
- const added = store.addContext("docs", "/auth", "Authentication docs");
- expect(added).toBe(true);
- const contexts = store.listContexts();
- expect(contexts).toContainEqual({
- collection: "docs",
- path: "/auth",
- context: "Authentication docs",
- });
- });
- test("addContext returns false for non-existent collection", () => {
- const added = store.addContext("nonexistent", "/path", "Some context");
- expect(added).toBe(false);
- });
- test("removeContext removes existing context", () => {
- store.addContext("docs", "/auth", "Authentication docs");
- const removed = store.removeContext("docs", "/auth");
- expect(removed).toBe(true);
- const contexts = store.listContexts();
- expect(contexts.find(c => c.path === "/auth")).toBeUndefined();
- });
- test("removeContext returns false for non-existent context", () => {
- const removed = store.removeContext("docs", "/nonexistent");
- expect(removed).toBe(false);
- });
- test("setGlobalContext sets and retrieves global context", () => {
- store.setGlobalContext("Global knowledge base");
- const global = store.getGlobalContext();
- expect(global).toBe("Global knowledge base");
- });
- test("setGlobalContext with undefined clears it", () => {
- store.setGlobalContext("Some context");
- store.setGlobalContext(undefined);
- const global = store.getGlobalContext();
- expect(global).toBeUndefined();
- });
- test("listContexts includes global context", () => {
- store.setGlobalContext("Global context");
- const contexts = store.listContexts();
- expect(contexts).toContainEqual({
- collection: "*",
- path: "/",
- context: "Global context",
- });
- });
- test("listContexts returns contexts across multiple collections", () => {
- store.addContext("docs", "/", "Documentation");
- store.addContext("notes", "/", "Personal notes");
- const contexts = store.listContexts();
- expect(contexts.filter(c => c.path === "/")).toHaveLength(2);
- });
- test("multiple contexts on same collection", () => {
- store.addContext("docs", "/auth", "Auth docs");
- store.addContext("docs", "/api", "API docs");
- const contexts = store.listContexts().filter(c => c.collection === "docs");
- expect(contexts).toHaveLength(2);
- expect(contexts.map(c => c.path).sort()).toEqual(["/api", "/auth"]);
- });
- test("addContext overwrites existing context for same path", () => {
- store.addContext("docs", "/auth", "Old context");
- store.addContext("docs", "/auth", "New context");
- const contexts = store.listContexts().filter(c => c.path === "/auth");
- expect(contexts).toHaveLength(1);
- expect(contexts[0]!.context).toBe("New context");
- });
- });
- // =============================================================================
- // Inline Config Isolation Tests
- // =============================================================================
- describe("inline config isolation", () => {
- test("inline config does not write any files to disk", () => {
- const configDir = join(testDir, "should-not-exist");
- const store = createStore({
- dbPath: freshDbPath(),
- config: {
- collections: {
- docs: { path: docsDir, pattern: "**/*.md" },
- },
- },
- });
- store.addCollection("notes", { path: notesDir, pattern: "**/*.md" });
- store.addContext("docs", "/", "Documentation");
- expect(existsSync(configDir)).toBe(false);
- store.close();
- });
- test("inline config mutations persist within session", () => {
- const store = createStore({
- dbPath: freshDbPath(),
- config: { collections: {} },
- });
- store.addCollection("docs", { path: docsDir, pattern: "**/*.md" });
- store.addContext("docs", "/", "My docs");
- // Verify the mutations are visible
- const collections = store.listCollections();
- expect(collections.map(c => c.name)).toContain("docs");
- const contexts = store.listContexts();
- expect(contexts).toContainEqual({
- collection: "docs",
- path: "/",
- context: "My docs",
- });
- store.close();
- });
- test("two stores with different inline configs are independent", () => {
- const store1 = createStore({
- dbPath: freshDbPath(),
- config: {
- collections: {
- docs: { path: docsDir, pattern: "**/*.md" },
- },
- },
- });
- // Close first store (resets config source)
- store1.close();
- const store2 = createStore({
- dbPath: freshDbPath(),
- config: {
- collections: {
- notes: { path: notesDir, pattern: "**/*.md" },
- },
- },
- });
- const names = store2.listCollections().map(c => c.name);
- expect(names).toContain("notes");
- expect(names).not.toContain("docs");
- store2.close();
- });
- });
- // =============================================================================
- // YAML Config File Tests
- // =============================================================================
- describe("YAML config file mode", () => {
- test("loads collections from YAML file", () => {
- const configPath = join(testDir, `config-${Date.now()}.yml`);
- const config: CollectionConfig = {
- collections: {
- docs: { path: docsDir, pattern: "**/*.md" },
- notes: { path: notesDir, pattern: "**/*.md" },
- },
- };
- writeFileSync(configPath, YAML.stringify(config));
- const store = createStore({ dbPath: freshDbPath(), configPath });
- const names = store.listCollections().map(c => c.name);
- expect(names).toContain("docs");
- expect(names).toContain("notes");
- store.close();
- });
- test("addCollection persists to YAML file", () => {
- const configPath = join(testDir, `config-persist-${Date.now()}.yml`);
- writeFileSync(configPath, YAML.stringify({ collections: {} }));
- const store = createStore({ dbPath: freshDbPath(), configPath });
- store.addCollection("newcol", { path: docsDir, pattern: "**/*.md" });
- store.close();
- // Read the YAML file directly and verify
- const raw = readFileSync(configPath, "utf-8");
- const parsed = YAML.parse(raw) as CollectionConfig;
- expect(parsed.collections).toHaveProperty("newcol");
- expect(parsed.collections.newcol!.path).toBe(docsDir);
- });
- test("context persists to YAML file", () => {
- const configPath = join(testDir, `config-ctx-${Date.now()}.yml`);
- writeFileSync(configPath, YAML.stringify({
- collections: { docs: { path: docsDir, pattern: "**/*.md" } },
- }));
- const store = createStore({ dbPath: freshDbPath(), configPath });
- store.addContext("docs", "/api", "API documentation");
- store.close();
- const raw = readFileSync(configPath, "utf-8");
- const parsed = YAML.parse(raw) as CollectionConfig;
- expect(parsed.collections.docs!.context).toEqual({ "/api": "API documentation" });
- });
- test("non-existent config file returns empty collections", () => {
- const configPath = join(testDir, "nonexistent-config.yml");
- const store = createStore({ dbPath: freshDbPath(), configPath });
- const collections = store.listCollections();
- expect(collections).toEqual([]);
- store.close();
- });
- });
- // =============================================================================
- // Search Tests (BM25 - no LLM needed)
- // =============================================================================
- describe("search (BM25)", () => {
- let store: QMDStore;
- let dbPath: string;
- beforeAll(() => {
- dbPath = join(testDir, "search-test.sqlite");
- store = createStore({
- dbPath,
- config: {
- collections: {
- docs: { path: docsDir, pattern: "**/*.md" },
- notes: { path: notesDir, pattern: "**/*.md" },
- },
- },
- });
- // Index documents manually using internal store
- const now = new Date().toISOString();
- const { internal } = store;
- const fs = require("fs");
- // Index docs collection
- for (const file of ["readme.md", "auth.md", "api.md"]) {
- const fullPath = join(docsDir, file);
- const content = fs.readFileSync(fullPath, "utf-8");
- const hash = require("crypto").createHash("sha256").update(content).digest("hex");
- const title = content.match(/^#\s+(.+)/m)?.[1] || file;
- internal.insertContent(hash, content, now);
- internal.insertDocument("docs", `qmd://docs/${file}`, title, hash, now, now);
- }
- // Index notes collection
- for (const file of ["meeting-2025-01.md", "meeting-2025-02.md", "ideas.md"]) {
- const fullPath = join(notesDir, file);
- const content = fs.readFileSync(fullPath, "utf-8");
- const hash = require("crypto").createHash("sha256").update(content).digest("hex");
- const title = content.match(/^#\s+(.+)/m)?.[1] || file;
- internal.insertContent(hash, content, now);
- internal.insertDocument("notes", `qmd://notes/${file}`, title, hash, now, now);
- }
- });
- afterAll(() => {
- store.close();
- });
- test("search returns results for matching query", () => {
- const results = store.search("authentication");
- expect(results.length).toBeGreaterThan(0);
- });
- test("search results have expected shape", () => {
- const results = store.search("authentication");
- expect(results.length).toBeGreaterThan(0);
- const result = results[0]!;
- expect(result).toHaveProperty("filepath");
- expect(result).toHaveProperty("score");
- expect(result).toHaveProperty("title");
- expect(result).toHaveProperty("docid");
- expect(result).toHaveProperty("collectionName");
- expect(typeof result.score).toBe("number");
- expect(result.score).toBeGreaterThan(0);
- });
- test("search respects limit option", () => {
- const results = store.search("meeting", { limit: 1 });
- expect(results.length).toBeLessThanOrEqual(1);
- });
- test("search with collection filter", () => {
- const results = store.search("authentication", { collection: "notes" });
- for (const r of results) {
- expect(r.collectionName).toBe("notes");
- }
- });
- test("search returns empty for non-matching query", () => {
- const results = store.search("xyznonexistentterm123");
- expect(results).toHaveLength(0);
- });
- test("search finds documents across collections", () => {
- const results = store.search("authentication", { limit: 10 });
- const collections = new Set(results.map(r => r.collectionName));
- // Auth appears in both docs/auth.md and notes/meeting-2025-02.md
- expect(collections.size).toBeGreaterThanOrEqual(1);
- });
- });
- // =============================================================================
- // Document Retrieval Tests
- // =============================================================================
- describe("get and multiGet", () => {
- let store: QMDStore;
- beforeAll(() => {
- store = createStore({
- dbPath: join(testDir, "get-test.sqlite"),
- config: {
- collections: {
- docs: { path: docsDir, pattern: "**/*.md" },
- },
- },
- });
- // Index documents
- const now = new Date().toISOString();
- const { internal } = store;
- const fs = require("fs");
- for (const file of ["readme.md", "auth.md", "api.md"]) {
- const fullPath = join(docsDir, file);
- const content = fs.readFileSync(fullPath, "utf-8");
- const hash = require("crypto").createHash("sha256").update(content).digest("hex");
- const title = content.match(/^#\s+(.+)/m)?.[1] || file;
- internal.insertContent(hash, content, now);
- internal.insertDocument("docs", `qmd://docs/${file}`, title, hash, now, now);
- }
- });
- afterAll(() => {
- store.close();
- });
- test("get retrieves a document by path", () => {
- const result = store.get("qmd://docs/auth.md");
- expect("error" in result).toBe(false);
- if (!("error" in result)) {
- expect(result.title).toBe("Authentication");
- expect(result.collectionName).toBe("docs");
- }
- });
- test("get with includeBody returns body content", () => {
- const result = store.get("qmd://docs/auth.md", { includeBody: true });
- if (!("error" in result)) {
- expect(result.body).toBeDefined();
- expect(result.body).toContain("JWT tokens");
- }
- });
- test("get returns not_found for missing document", () => {
- const result = store.get("qmd://docs/nonexistent.md");
- expect("error" in result).toBe(true);
- if ("error" in result) {
- expect(result.error).toBe("not_found");
- }
- });
- test("get by docid", () => {
- // First get a document to find its docid
- const doc = store.get("qmd://docs/readme.md");
- if (!("error" in doc)) {
- const byDocid = store.get(`#${doc.docid}`);
- expect("error" in byDocid).toBe(false);
- if (!("error" in byDocid)) {
- expect(byDocid.docid).toBe(doc.docid);
- }
- }
- });
- test("multiGet retrieves multiple documents", () => {
- const { docs, errors } = store.multiGet("qmd://docs/*.md");
- expect(docs.length).toBeGreaterThan(0);
- });
- });
- // =============================================================================
- // Index Health Tests
- // =============================================================================
- describe("index health", () => {
- let store: QMDStore;
- beforeEach(() => {
- store = createStore({
- dbPath: freshDbPath(),
- config: {
- collections: {
- docs: { path: docsDir, pattern: "**/*.md" },
- },
- },
- });
- });
- afterEach(() => {
- store.close();
- });
- test("getStatus returns valid structure", () => {
- const status = store.getStatus();
- expect(status).toHaveProperty("totalDocuments");
- expect(status).toHaveProperty("needsEmbedding");
- expect(status).toHaveProperty("hasVectorIndex");
- expect(status).toHaveProperty("collections");
- expect(typeof status.totalDocuments).toBe("number");
- });
- test("getIndexHealth returns valid structure", () => {
- const health = store.getIndexHealth();
- expect(health).toHaveProperty("needsEmbedding");
- expect(health).toHaveProperty("totalDocs");
- expect(typeof health.needsEmbedding).toBe("number");
- expect(typeof health.totalDocs).toBe("number");
- });
- test("fresh store has zero documents", () => {
- const status = store.getStatus();
- expect(status.totalDocuments).toBe(0);
- });
- });
- // =============================================================================
- // Lifecycle Tests
- // =============================================================================
- describe("lifecycle", () => {
- test("close() makes subsequent operations throw", () => {
- const store = createStore({
- dbPath: freshDbPath(),
- config: { collections: {} },
- });
- store.close();
- // Database operations should fail after close
- expect(() => store.getStatus()).toThrow();
- });
- test("multiple stores can coexist with different databases", () => {
- const store1 = createStore({
- dbPath: freshDbPath(),
- config: {
- collections: {
- docs: { path: docsDir, pattern: "**/*.md" },
- },
- },
- });
- // Note: since config source is module-level, we close store1 first
- store1.close();
- const store2 = createStore({
- dbPath: freshDbPath(),
- config: {
- collections: {
- notes: { path: notesDir, pattern: "**/*.md" },
- },
- },
- });
- const names = store2.listCollections().map(c => c.name);
- expect(names).toContain("notes");
- expect(names).not.toContain("docs");
- store2.close();
- });
- });
- // =============================================================================
- // Config Initialization Tests
- // =============================================================================
- describe("config initialization", () => {
- test("inline config with global_context is preserved", () => {
- const store = createStore({
- dbPath: freshDbPath(),
- config: {
- global_context: "System knowledge base",
- collections: {
- docs: { path: docsDir, pattern: "**/*.md" },
- },
- },
- });
- const global = store.getGlobalContext();
- expect(global).toBe("System knowledge base");
- store.close();
- });
- test("inline config with pre-existing contexts is preserved", () => {
- const store = createStore({
- dbPath: freshDbPath(),
- config: {
- collections: {
- docs: {
- path: docsDir,
- pattern: "**/*.md",
- context: { "/auth": "Authentication docs" },
- },
- },
- },
- });
- const contexts = store.listContexts();
- expect(contexts).toContainEqual({
- collection: "docs",
- path: "/auth",
- context: "Authentication docs",
- });
- store.close();
- });
- test("inline config with empty collections object works", () => {
- const store = createStore({
- dbPath: freshDbPath(),
- config: { collections: {} },
- });
- expect(store.listCollections()).toEqual([]);
- expect(store.listContexts()).toEqual([]);
- store.close();
- });
- test("inline config with multiple collection options", () => {
- const store = createStore({
- dbPath: freshDbPath(),
- config: {
- collections: {
- docs: {
- path: docsDir,
- pattern: "**/*.md",
- ignore: ["drafts/**"],
- includeByDefault: true,
- },
- notes: {
- path: notesDir,
- pattern: "**/*.md",
- includeByDefault: false,
- },
- },
- },
- });
- const collections = store.listCollections();
- expect(collections).toHaveLength(2);
- store.close();
- });
- });
- // =============================================================================
- // Type Export Tests (compile-time checks, runtime verification)
- // =============================================================================
- describe("type exports", () => {
- test("StoreOptions type is usable", () => {
- const opts: StoreOptions = {
- dbPath: "/tmp/test.sqlite",
- config: { collections: {} },
- };
- expect(opts.dbPath).toBe("/tmp/test.sqlite");
- });
- test("CollectionConfig type is usable", () => {
- const config: CollectionConfig = {
- global_context: "test",
- collections: {
- test: { path: "/tmp", pattern: "**/*.md" },
- },
- };
- expect(config.collections).toHaveProperty("test");
- });
- test("QMDStore type exposes expected methods", () => {
- const store = createStore({
- dbPath: freshDbPath(),
- config: { collections: {} },
- });
- // Verify all methods exist
- expect(typeof store.query).toBe("function");
- expect(typeof store.search).toBe("function");
- expect(typeof store.structuredSearch).toBe("function");
- expect(typeof store.get).toBe("function");
- expect(typeof store.multiGet).toBe("function");
- expect(typeof store.addCollection).toBe("function");
- expect(typeof store.removeCollection).toBe("function");
- expect(typeof store.renameCollection).toBe("function");
- expect(typeof store.listCollections).toBe("function");
- expect(typeof store.addContext).toBe("function");
- expect(typeof store.removeContext).toBe("function");
- expect(typeof store.setGlobalContext).toBe("function");
- expect(typeof store.getGlobalContext).toBe("function");
- expect(typeof store.listContexts).toBe("function");
- expect(typeof store.getStatus).toBe("function");
- expect(typeof store.getIndexHealth).toBe("function");
- expect(typeof store.close).toBe("function");
- store.close();
- });
- });
|