This commit is contained in:
90
packages/server/tests/local/vector-backfill.test.ts
Normal file
90
packages/server/tests/local/vector-backfill.test.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { MindDB, FrameStore, SessionStore } from '@waggle/core';
|
||||
import { runVectorBackfill } from '../../src/local/vector-backfill.js';
|
||||
import { MockEmbedder } from '../../../hive-mind-core/tests/mind/helpers/mock-embedder.js';
|
||||
import type { EmbeddingProviderInstance } from '@waggle/core';
|
||||
|
||||
/**
|
||||
* D1 follow-up — one-time vector repair + chunk backfill per mind.
|
||||
* Mock-fingerprint repair (re-embed noise vectors) + rechunkAllFrames,
|
||||
* idempotent via meta flag, skip-and-retry while the embedder is mock.
|
||||
*/
|
||||
|
||||
/** Wrap MockEmbedder as a provider instance reporting a REAL active provider. */
|
||||
function realProvider(): EmbeddingProviderInstance {
|
||||
const m = new MockEmbedder();
|
||||
return Object.assign(m, {
|
||||
getActiveProvider: () => 'ollama',
|
||||
getStatus: () => ({ activeProvider: 'ollama', modelName: 'test-model' }),
|
||||
}) as unknown as EmbeddingProviderInstance;
|
||||
}
|
||||
|
||||
function mockProvider(): EmbeddingProviderInstance {
|
||||
const m = new MockEmbedder();
|
||||
return Object.assign(m, {
|
||||
getActiveProvider: () => 'mock',
|
||||
getStatus: () => ({ activeProvider: 'mock', modelName: 'deterministic-mock' }),
|
||||
}) as unknown as EmbeddingProviderInstance;
|
||||
}
|
||||
|
||||
describe('runVectorBackfill', () => {
|
||||
let db: MindDB;
|
||||
let gopId: string;
|
||||
|
||||
beforeEach(() => {
|
||||
db = new MindDB(':memory:');
|
||||
const frames = new FrameStore(db);
|
||||
gopId = new SessionStore(db).create().gop_id;
|
||||
frames.createIFrame(gopId, 'a frame about quarterly planning details', 'normal', 'system');
|
||||
frames.createIFrame(gopId, 'another frame with sailing trip notes and logistics', 'normal', 'system');
|
||||
});
|
||||
|
||||
afterEach(() => db.close());
|
||||
|
||||
it('skips (without setting the flag) while the embedder is mock — retries later', async () => {
|
||||
const r1 = await runVectorBackfill(db, mockProvider());
|
||||
expect(r1.skipped).toBe('no_real_embedder');
|
||||
// a later run with a real provider DOES the work
|
||||
const r2 = await runVectorBackfill(db, realProvider());
|
||||
expect(r2.skipped).toBeNull();
|
||||
expect(r2.chunksCreated).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('backfills chunks once and is a flagged no-op afterwards', async () => {
|
||||
const r1 = await runVectorBackfill(db, realProvider());
|
||||
expect(r1.skipped).toBeNull();
|
||||
expect(r1.chunksCreated).toBe(2);
|
||||
expect(r1.vectorsRepaired).toBe(false); // no mock fingerprint on a fresh mind
|
||||
|
||||
const r2 = await runVectorBackfill(db, realProvider());
|
||||
expect(r2.skipped).toBe('already_done');
|
||||
expect(r2.chunksCreated).toBe(0);
|
||||
});
|
||||
|
||||
it('repairs mock-fingerprinted vectors: recreates vec tables and re-embeds all frames', async () => {
|
||||
const raw = db.getDatabase();
|
||||
// simulate the D1 probe finding: vectors written under the mock fingerprint
|
||||
raw.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES ('embedding_provider', 'mock')").run();
|
||||
raw.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES ('embedding_dim', '1024')").run();
|
||||
|
||||
const r = await runVectorBackfill(db, realProvider());
|
||||
expect(r.skipped).toBeNull();
|
||||
expect(r.vectorsRepaired).toBe(true);
|
||||
expect(r.framesReembedded).toBe(2);
|
||||
expect(r.chunksCreated).toBe(2);
|
||||
const vec = raw.prepare('SELECT COUNT(*) AS n FROM memory_frames_vec').get() as { n: number };
|
||||
expect(vec.n).toBe(2);
|
||||
});
|
||||
|
||||
it('marks an empty mind done without doing work', async () => {
|
||||
const empty = new MindDB(':memory:');
|
||||
try {
|
||||
const r = await runVectorBackfill(empty, realProvider());
|
||||
expect(r.skipped).toBe('empty_mind');
|
||||
const again = await runVectorBackfill(empty, realProvider());
|
||||
expect(again.skipped).toBe('already_done');
|
||||
} finally {
|
||||
empty.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user