Files
kbdb-graph-plugin/tests/mock-client.ts
T
Claude 4cc7057cea feat(graph): node gloss→base embeddable entry 橋——Arcrun#7 A1
修「打標≠讀標」:node gloss+embed 標在 entity record、但 base embed 只掃 entries.metadata.embed
→ persistNodes 另落一筆 content=canonical:gloss、metadata.embed=true 的 base entry(走 API/零SQL)。
- gloss-entry.ts: upsertGlossEntry 冪等(確定性 page_name + list-then-write)
- backfill-gloss-entries.ts + route: 對既有 entity record 補 gloss entry
- triplet-ingest 帶 source.uri 進 metadata;kbdb-client 暴露 metadata_json(base 早支援)
- vitest 30/30(+7);[→arcrun] base 缺 upsert entry 端點,暫以 list-then-write 冪等
2026-07-05 09:16:26 +00:00

105 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 記憶體版 KbdbClient — 模擬基本盤 API,供插件單元測試(不打真網路、不碰 D1)。
// 只實作插件實際用到的方法,行為對齊 arcrun/kbdb 契約。
import type { KbdbClient, BaseEntry, BaseRecord } from '../src/lib/kbdb-client';
export class MockKbdbClient {
private entries = new Map<string, BaseEntry>();
private templates = new Map<string, string[]>();
private records = new Map<string, { template: string; values: Record<string, string>; owner_id?: string }>();
private seq = 0;
private id(prefix: string): string {
this.seq += 1;
return `${prefix}-${this.seq}`;
}
async createEntry(input: any): Promise<BaseEntry> {
const id = this.id('entry');
// 對齊 base entry-crud:保留 page_nameidempotency key)、metadata_jsonembed 打標)、parent_id。
const entry: BaseEntry = {
id,
content: input.content ?? null,
entry_type: input.entry_type,
owner_id: input.owner_id ?? null,
parent_id: input.parent_id ?? null,
page_name: input.page_name ?? null,
metadata_json: input.metadata_json ?? null,
};
this.entries.set(id, entry);
return entry;
}
async getEntry(id: string): Promise<BaseEntry | null> {
return this.entries.get(id) ?? null;
}
async listEntries(filters: any = {}): Promise<BaseEntry[]> {
// 對齊 base entry-crud listEntries 的 exact-match filters(含 page_name idempotency key、parent_id)。
return [...this.entries.values()].filter(
(e) =>
(!filters.entry_type || e.entry_type === filters.entry_type) &&
(!filters.owner_id || e.owner_id === filters.owner_id) &&
(!filters.parent_id || e.parent_id === filters.parent_id) &&
(!filters.page_name || e.page_name === filters.page_name),
);
}
async searchEntries(q: string, owner_id?: string): Promise<BaseEntry[]> {
const needle = q.toLowerCase();
return [...this.entries.values()].filter(
(e) => (e.content ?? '').toLowerCase().includes(needle) && (!owner_id || e.owner_id === owner_id),
);
}
async updateEntry(id: string, patch: any): Promise<BaseEntry | null> {
const e = this.entries.get(id);
if (!e) return null;
Object.assign(e, patch);
return e;
}
async deleteEntry(id: string): Promise<void> {
this.entries.delete(id);
}
async ensureTemplate(name: string, slots: string[]): Promise<void> {
// 對齊真 client 的 slot-diff 行為:既有 template 補缺 slot(不 early-return)。
const have = this.templates.get(name);
if (!have) {
this.templates.set(name, [...slots]);
return;
}
const set = new Set(have);
for (const s of slots) if (!set.has(s)) have.push(s);
}
async createRecord(template: string, values: Record<string, string>, owner_id?: string): Promise<string> {
const id = this.id('rec');
this.records.set(id, { template, values: { ...values }, owner_id });
return id;
}
async getRecord(recordId: string): Promise<BaseRecord | null> {
const r = this.records.get(recordId);
if (!r) return null;
return { record_id: recordId, template: r.template, values: r.values };
}
async updateRecord(recordId: string, values: Record<string, string>): Promise<void> {
const r = this.records.get(recordId);
if (r) Object.assign(r.values, values);
}
async listRecordsByTemplate(template: string, owner_id?: string): Promise<BaseRecord[]> {
return [...this.records.entries()]
.filter(([, r]) => r.template === template && (!owner_id || r.owner_id === owner_id))
.map(([record_id, r]) => ({ record_id, template: r.template, values: r.values }));
}
}
/** 轉成 KbdbClient 型別供 action 接受。 */
export function mockClient(): KbdbClient {
return new MockKbdbClient() as unknown as KbdbClient;
}