mirror of
https://github.com/doum1004/llmwiki-cli.git
synced 2026-04-28 23:16:09 +02:00
feat: implement tests for SupabaseProvider and GitProvider, and refactor StorageProvider tests
This commit is contained in:
@@ -6,7 +6,7 @@ export class SupabaseProvider implements StorageProvider {
|
||||
private client: any;
|
||||
private wikiId: string;
|
||||
|
||||
private constructor(client: any, wikiId: string) {
|
||||
constructor(client: any, wikiId: string) {
|
||||
this.client = client;
|
||||
this.wikiId = wikiId;
|
||||
}
|
||||
|
||||
69
test/filesystem-provider.test.ts
Normal file
69
test/filesystem-provider.test.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
||||
import { mkdtemp, rm } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import { tmpdir } from "os";
|
||||
import { WikiManager } from "../src/lib/wiki.ts";
|
||||
import type { StorageProvider } from "../src/types.ts";
|
||||
|
||||
let testDir: string;
|
||||
let provider: StorageProvider;
|
||||
|
||||
beforeEach(async () => {
|
||||
testDir = await mkdtemp(join(tmpdir(), "llmwiki-fs-"));
|
||||
provider = new WikiManager(testDir);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await rm(testDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe("FilesystemProvider", () => {
|
||||
it("writePage + readPage round-trips content", async () => {
|
||||
await provider.writePage("test.md", "hello world");
|
||||
const content = await provider.readPage("test.md");
|
||||
expect(content).toBe("hello world");
|
||||
});
|
||||
|
||||
it("readPage returns null for missing page", async () => {
|
||||
const content = await provider.readPage("nonexistent.md");
|
||||
expect(content).toBeNull();
|
||||
});
|
||||
|
||||
it("pageExists returns false for missing page", async () => {
|
||||
expect(await provider.pageExists("nope.md")).toBe(false);
|
||||
});
|
||||
|
||||
it("pageExists returns true after write", async () => {
|
||||
await provider.writePage("exists.md", "content");
|
||||
expect(await provider.pageExists("exists.md")).toBe(true);
|
||||
});
|
||||
|
||||
it("appendPage returns false for missing page", async () => {
|
||||
const ok = await provider.appendPage("missing.md", "more");
|
||||
expect(ok).toBe(false);
|
||||
});
|
||||
|
||||
it("appendPage appends to existing page", async () => {
|
||||
await provider.writePage("page.md", "first\n");
|
||||
const ok = await provider.appendPage("page.md", "second");
|
||||
expect(ok).toBe(true);
|
||||
const content = await provider.readPage("page.md");
|
||||
expect(content).toBe("first\nsecond");
|
||||
});
|
||||
|
||||
it("listPages returns written markdown files", async () => {
|
||||
await provider.writePage("a.md", "a");
|
||||
await provider.writePage("sub/b.md", "b");
|
||||
const pages = await provider.listPages();
|
||||
expect(pages).toContain("a.md");
|
||||
expect(pages).toContain("sub/b.md");
|
||||
});
|
||||
|
||||
it("listPages with dir scopes to subdirectory", async () => {
|
||||
await provider.writePage("root.md", "r");
|
||||
await provider.writePage("sub/child.md", "c");
|
||||
const pages = await provider.listPages("sub");
|
||||
expect(pages).toContain("sub/child.md");
|
||||
expect(pages).not.toContain("root.md");
|
||||
});
|
||||
});
|
||||
66
test/git-provider.test.ts
Normal file
66
test/git-provider.test.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
||||
import { mkdtemp, rm } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import { tmpdir } from "os";
|
||||
import { execFile } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { GitProvider } from "../src/lib/git-provider.ts";
|
||||
import * as git from "../src/lib/git.ts";
|
||||
import type { StorageProvider } from "../src/types.ts";
|
||||
|
||||
const exec = promisify(execFile);
|
||||
|
||||
let gitDir: string;
|
||||
let gitProvider: StorageProvider;
|
||||
|
||||
beforeEach(async () => {
|
||||
gitDir = await mkdtemp(join(tmpdir(), "llmwiki-git-"));
|
||||
await git.init(gitDir);
|
||||
// Configure git user for CI environments
|
||||
await exec("git", ["config", "user.name", "Test"], { cwd: gitDir });
|
||||
await exec("git", ["config", "user.email", "test@test.com"], { cwd: gitDir });
|
||||
gitProvider = new GitProvider(gitDir);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await rm(gitDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe("GitProvider", () => {
|
||||
it("writePage stores content and auto-commits", async () => {
|
||||
await gitProvider.writePage("test.md", "hello");
|
||||
const content = await gitProvider.readPage("test.md");
|
||||
expect(content).toBe("hello");
|
||||
const log = await git.log(gitDir, 1);
|
||||
expect(log.ok).toBe(true);
|
||||
expect(log.output).toContain("update test.md");
|
||||
});
|
||||
|
||||
it("appendPage auto-commits on success", async () => {
|
||||
await gitProvider.writePage("page.md", "first\n");
|
||||
await gitProvider.appendPage("page.md", "second");
|
||||
const log = await git.log(gitDir, 2);
|
||||
expect(log.ok).toBe(true);
|
||||
expect(log.output).toContain("append to page.md");
|
||||
});
|
||||
|
||||
it("appendPage does not commit on missing page", async () => {
|
||||
const ok = await gitProvider.appendPage("missing.md", "nope");
|
||||
expect(ok).toBe(false);
|
||||
const log = await git.log(gitDir, 1);
|
||||
expect(log.output).not.toContain("append to missing.md");
|
||||
});
|
||||
|
||||
it("readPage returns null for missing page", async () => {
|
||||
const content = await gitProvider.readPage("nope.md");
|
||||
expect(content).toBeNull();
|
||||
});
|
||||
|
||||
it("listPages works like filesystem", async () => {
|
||||
await gitProvider.writePage("a.md", "a");
|
||||
await gitProvider.writePage("sub/b.md", "b");
|
||||
const pages = await gitProvider.listPages();
|
||||
expect(pages).toContain("a.md");
|
||||
expect(pages).toContain("sub/b.md");
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,6 @@ import { join } from "path";
|
||||
import { tmpdir } from "os";
|
||||
import { createProvider } from "../src/lib/storage.ts";
|
||||
import { GitProvider } from "../src/lib/git-provider.ts";
|
||||
import * as git from "../src/lib/git.ts";
|
||||
import type { StorageProvider, WikiConfig } from "../src/types.ts";
|
||||
|
||||
function makeConfig(backend: WikiConfig["backend"] = "filesystem"): WikiConfig {
|
||||
@@ -18,11 +17,9 @@ function makeConfig(backend: WikiConfig["backend"] = "filesystem"): WikiConfig {
|
||||
}
|
||||
|
||||
let testDir: string;
|
||||
let provider: StorageProvider;
|
||||
|
||||
beforeEach(async () => {
|
||||
testDir = await mkdtemp(join(tmpdir(), "llmwiki-storage-"));
|
||||
provider = await createProvider(makeConfig("filesystem"), testDir);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -30,7 +27,8 @@ afterEach(async () => {
|
||||
});
|
||||
|
||||
describe("createProvider", () => {
|
||||
it("creates a filesystem provider", () => {
|
||||
it("creates a filesystem provider", async () => {
|
||||
const provider = await createProvider(makeConfig("filesystem"), testDir);
|
||||
expect(provider).toBeDefined();
|
||||
expect(provider.readPage).toBeInstanceOf(Function);
|
||||
expect(provider.writePage).toBeInstanceOf(Function);
|
||||
@@ -56,106 +54,3 @@ describe("createProvider", () => {
|
||||
).rejects.toThrow('Unknown storage backend: "unknown"');
|
||||
});
|
||||
});
|
||||
|
||||
describe("StorageProvider contract (filesystem)", () => {
|
||||
it("writePage + readPage round-trips content", async () => {
|
||||
await provider.writePage("test.md", "hello world");
|
||||
const content = await provider.readPage("test.md");
|
||||
expect(content).toBe("hello world");
|
||||
});
|
||||
|
||||
it("readPage returns null for missing page", async () => {
|
||||
const content = await provider.readPage("nonexistent.md");
|
||||
expect(content).toBeNull();
|
||||
});
|
||||
|
||||
it("pageExists returns false for missing page", async () => {
|
||||
expect(await provider.pageExists("nope.md")).toBe(false);
|
||||
});
|
||||
|
||||
it("pageExists returns true after write", async () => {
|
||||
await provider.writePage("exists.md", "content");
|
||||
expect(await provider.pageExists("exists.md")).toBe(true);
|
||||
});
|
||||
|
||||
it("appendPage returns false for missing page", async () => {
|
||||
const ok = await provider.appendPage("missing.md", "more");
|
||||
expect(ok).toBe(false);
|
||||
});
|
||||
|
||||
it("appendPage appends to existing page", async () => {
|
||||
await provider.writePage("page.md", "first\n");
|
||||
const ok = await provider.appendPage("page.md", "second");
|
||||
expect(ok).toBe(true);
|
||||
const content = await provider.readPage("page.md");
|
||||
expect(content).toBe("first\nsecond");
|
||||
});
|
||||
|
||||
it("listPages returns written markdown files", async () => {
|
||||
await provider.writePage("a.md", "a");
|
||||
await provider.writePage("sub/b.md", "b");
|
||||
const pages = await provider.listPages();
|
||||
expect(pages).toContain("a.md");
|
||||
expect(pages).toContain("sub/b.md");
|
||||
});
|
||||
|
||||
it("listPages with dir scopes to subdirectory", async () => {
|
||||
await provider.writePage("root.md", "r");
|
||||
await provider.writePage("sub/child.md", "c");
|
||||
const pages = await provider.listPages("sub");
|
||||
expect(pages).toContain("sub/child.md");
|
||||
expect(pages).not.toContain("root.md");
|
||||
});
|
||||
});
|
||||
|
||||
describe("GitProvider", () => {
|
||||
let gitDir: string;
|
||||
let gitProvider: StorageProvider;
|
||||
|
||||
beforeEach(async () => {
|
||||
gitDir = await mkdtemp(join(tmpdir(), "llmwiki-git-"));
|
||||
await git.init(gitDir);
|
||||
gitProvider = await createProvider(makeConfig("git"), gitDir);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await rm(gitDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("writePage stores content and auto-commits", async () => {
|
||||
await gitProvider.writePage("test.md", "hello");
|
||||
const content = await gitProvider.readPage("test.md");
|
||||
expect(content).toBe("hello");
|
||||
const log = await git.log(gitDir, 1);
|
||||
expect(log.ok).toBe(true);
|
||||
expect(log.output).toContain("update test.md");
|
||||
});
|
||||
|
||||
it("appendPage auto-commits on success", async () => {
|
||||
await gitProvider.writePage("page.md", "first\n");
|
||||
await gitProvider.appendPage("page.md", "second");
|
||||
const log = await git.log(gitDir, 2);
|
||||
expect(log.ok).toBe(true);
|
||||
expect(log.output).toContain("append to page.md");
|
||||
});
|
||||
|
||||
it("appendPage does not commit on missing page", async () => {
|
||||
const ok = await gitProvider.appendPage("missing.md", "nope");
|
||||
expect(ok).toBe(false);
|
||||
const log = await git.log(gitDir, 1);
|
||||
expect(log.output).not.toContain("append to missing.md");
|
||||
});
|
||||
|
||||
it("readPage returns null for missing page", async () => {
|
||||
const content = await gitProvider.readPage("nope.md");
|
||||
expect(content).toBeNull();
|
||||
});
|
||||
|
||||
it("listPages works like filesystem", async () => {
|
||||
await gitProvider.writePage("a.md", "a");
|
||||
await gitProvider.writePage("sub/b.md", "b");
|
||||
const pages = await gitProvider.listPages();
|
||||
expect(pages).toContain("a.md");
|
||||
expect(pages).toContain("sub/b.md");
|
||||
});
|
||||
});
|
||||
|
||||
126
test/supabase-provider.test.ts
Normal file
126
test/supabase-provider.test.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import { describe, it, expect, beforeEach } from "bun:test";
|
||||
import { SupabaseProvider } from "../src/lib/supabase-provider.ts";
|
||||
|
||||
// In-memory store simulating Supabase table
|
||||
let store: Map<string, { wiki_id: string; path: string; content: string }>;
|
||||
|
||||
function mockClient() {
|
||||
function makeEqChain(wikiId: string, isCount: boolean) {
|
||||
return {
|
||||
eq(col2: string, val2: string) {
|
||||
const key = `${wikiId}:${val2}`;
|
||||
if (isCount) {
|
||||
const exists = store.has(key);
|
||||
return { count: exists ? 1 : 0, error: null };
|
||||
}
|
||||
return {
|
||||
maybeSingle() {
|
||||
const row = store.get(key);
|
||||
return { data: row ? { content: row.content } : null, error: null };
|
||||
},
|
||||
};
|
||||
},
|
||||
like(_col: string, _pattern: string) {
|
||||
return {
|
||||
order() {
|
||||
return { data: [], error: null };
|
||||
},
|
||||
};
|
||||
},
|
||||
order(_col: string) {
|
||||
const results: { path: string }[] = [];
|
||||
for (const row of store.values()) {
|
||||
if (row.wiki_id === wikiId) {
|
||||
results.push({ path: row.path });
|
||||
}
|
||||
}
|
||||
return { data: results, error: null };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
from(_table: string) {
|
||||
return {
|
||||
select(fields: string, opts?: { count?: string; head?: boolean }) {
|
||||
const isCount = opts?.count === "exact";
|
||||
return {
|
||||
eq(col: string, val: string) {
|
||||
return makeEqChain(val, isCount);
|
||||
},
|
||||
};
|
||||
},
|
||||
upsert(row: any, _opts?: any) {
|
||||
const key = `${row.wiki_id}:${row.path}`;
|
||||
store.set(key, { wiki_id: row.wiki_id, path: row.path, content: row.content });
|
||||
return { error: null };
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let provider: SupabaseProvider;
|
||||
|
||||
beforeEach(() => {
|
||||
store = new Map();
|
||||
provider = new SupabaseProvider(mockClient(), "test-wiki");
|
||||
});
|
||||
|
||||
describe("SupabaseProvider", () => {
|
||||
it("writePage + readPage round-trips content", async () => {
|
||||
await provider.writePage("wiki/test.md", "hello world");
|
||||
const content = await provider.readPage("wiki/test.md");
|
||||
expect(content).toBe("hello world");
|
||||
});
|
||||
|
||||
it("readPage returns null for missing page", async () => {
|
||||
const content = await provider.readPage("nonexistent.md");
|
||||
expect(content).toBeNull();
|
||||
});
|
||||
|
||||
it("writePage overwrites existing content", async () => {
|
||||
await provider.writePage("wiki/page.md", "v1");
|
||||
await provider.writePage("wiki/page.md", "v2");
|
||||
const content = await provider.readPage("wiki/page.md");
|
||||
expect(content).toBe("v2");
|
||||
});
|
||||
|
||||
it("appendPage appends to existing page", async () => {
|
||||
await provider.writePage("wiki/page.md", "first\n");
|
||||
const ok = await provider.appendPage("wiki/page.md", "second");
|
||||
expect(ok).toBe(true);
|
||||
const content = await provider.readPage("wiki/page.md");
|
||||
expect(content).toBe("first\nsecond");
|
||||
});
|
||||
|
||||
it("appendPage returns false for missing page", async () => {
|
||||
const ok = await provider.appendPage("missing.md", "nope");
|
||||
expect(ok).toBe(false);
|
||||
});
|
||||
|
||||
it("pageExists returns false for missing page", async () => {
|
||||
expect(await provider.pageExists("nope.md")).toBe(false);
|
||||
});
|
||||
|
||||
it("pageExists returns true after write", async () => {
|
||||
await provider.writePage("wiki/exists.md", "content");
|
||||
expect(await provider.pageExists("wiki/exists.md")).toBe(true);
|
||||
});
|
||||
|
||||
it("listPages returns stored pages", async () => {
|
||||
await provider.writePage("wiki/a.md", "a");
|
||||
await provider.writePage("wiki/b.md", "b");
|
||||
const pages = await provider.listPages();
|
||||
expect(pages).toContain("wiki/a.md");
|
||||
expect(pages).toContain("wiki/b.md");
|
||||
});
|
||||
|
||||
it("listPages only returns .md files", async () => {
|
||||
await provider.writePage("wiki/page.md", "content");
|
||||
await provider.writePage("wiki/image.png", "binary");
|
||||
const pages = await provider.listPages();
|
||||
expect(pages).toContain("wiki/page.md");
|
||||
expect(pages).not.toContain("wiki/image.png");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user