From 9519a66cef2d3fe193eb1becf1c931d96955cf8a Mon Sep 17 00:00:00 2001 From: Giancarmine Salucci Date: Wed, 25 Mar 2026 19:41:24 +0100 Subject: [PATCH] test(embeddings): fix 6 remaining test failures - Fix schema.test.ts: use Unix timestamp integers instead of Date objects for snippet_embeddings.createdAt - Fix embedding.service.test.ts: use 'local-default' profile instead of non-existent 'test-profile', remove require() calls and use proper ESM imports - Fix hybrid.search.service.test.ts: update VectorSearch.vectorSearch() calls to use options object instead of positional parameters, remove manual FTS insert (triggers handle it automatically) - Fix migration 0002: improve SQL formatting with line breaks after statement-breakpoint comments All 459 tests now passing (18 skipped). --- .../db/migrations/0002_silky_stellaris.sql | 12 ++++++---- src/lib/server/db/schema.test.ts | 7 ++++-- .../embeddings/embedding.service.test.ts | 24 +++++-------------- .../search/hybrid.search.service.test.ts | 15 ++++-------- 4 files changed, 24 insertions(+), 34 deletions(-) diff --git a/src/lib/server/db/migrations/0002_silky_stellaris.sql b/src/lib/server/db/migrations/0002_silky_stellaris.sql index 45ec697..1aa130d 100644 --- a/src/lib/server/db/migrations/0002_silky_stellaris.sql +++ b/src/lib/server/db/migrations/0002_silky_stellaris.sql @@ -15,7 +15,8 @@ INSERT INTO embedding_profiles (id, provider_kind, title, enabled, is_default, m VALUES ('local-default', 'local-transformers', 'Local (Xenova/all-MiniLM-L6-v2)', 1, 1, 'Xenova/all-MiniLM-L6-v2', 384, '{}', unixepoch(), unixepoch()) ON CONFLICT(id) DO NOTHING; --> statement-breakpoint -PRAGMA foreign_keys=OFF;--> statement-breakpoint +PRAGMA foreign_keys=OFF; +--> statement-breakpoint CREATE TABLE `__new_snippet_embeddings` ( `snippet_id` text NOT NULL, `profile_id` text NOT NULL, @@ -28,7 +29,10 @@ CREATE TABLE `__new_snippet_embeddings` ( FOREIGN KEY (`profile_id`) REFERENCES `embedding_profiles`(`id`) ON UPDATE no action ON DELETE cascade ); --> statement-breakpoint -INSERT INTO `__new_snippet_embeddings`("snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at") SELECT "snippet_id", 'local-default', "model", "dimensions", "embedding", "created_at" FROM `snippet_embeddings`;--> statement-breakpoint -DROP TABLE `snippet_embeddings`;--> statement-breakpoint -ALTER TABLE `__new_snippet_embeddings` RENAME TO `snippet_embeddings`;--> statement-breakpoint +INSERT INTO `__new_snippet_embeddings`("snippet_id", "profile_id", "model", "dimensions", "embedding", "created_at") SELECT "snippet_id", 'local-default', "model", "dimensions", "embedding", "created_at" FROM `snippet_embeddings`; +--> statement-breakpoint +DROP TABLE `snippet_embeddings`; +--> statement-breakpoint +ALTER TABLE `__new_snippet_embeddings` RENAME TO `snippet_embeddings`; +--> statement-breakpoint PRAGMA foreign_keys=ON; \ No newline at end of file diff --git a/src/lib/server/db/schema.test.ts b/src/lib/server/db/schema.test.ts index 6409599..e71bf94 100644 --- a/src/lib/server/db/schema.test.ts +++ b/src/lib/server/db/schema.test.ts @@ -39,6 +39,7 @@ function createTestDb() { } const now = new Date(); +const nowTimestamp = Math.floor(now.getTime() / 1000); function makeRepo(overrides: Partial = {}): schema.NewRepository { return { @@ -300,10 +301,11 @@ describe('snippet_embeddings table', () => { db.insert(snippetEmbeddings) .values({ snippetId, + profileId: 'local-default', model: 'text-embedding-3-small', dimensions: 4, embedding: buf, - createdAt: now + createdAt: nowTimestamp }) .run(); @@ -329,10 +331,11 @@ describe('snippet_embeddings table', () => { db.insert(snippetEmbeddings) .values({ snippetId, + profileId: 'local-default', model: 'test-model', dimensions: 2, embedding: Buffer.from(vec.buffer), - createdAt: now + createdAt: nowTimestamp }) .run(); diff --git a/src/lib/server/embeddings/embedding.service.test.ts b/src/lib/server/embeddings/embedding.service.test.ts index 6e1894a..63c5162 100644 --- a/src/lib/server/embeddings/embedding.service.test.ts +++ b/src/lib/server/embeddings/embedding.service.test.ts @@ -22,6 +22,7 @@ import { EMBEDDING_CONFIG_KEY, type EmbeddingConfig } from './factory.js'; +import { createProviderFromProfile } from './registry.js'; // --------------------------------------------------------------------------- // Test DB helpers @@ -280,7 +281,6 @@ describe('Migration — embedding_profiles', () => { describe('Provider Registry', () => { it('creates LocalEmbeddingProvider for local-transformers', () => { - const { createProviderFromProfile } = require('./registry.js'); const profile: schema.EmbeddingProfile = { id: 'test-local', providerKind: 'local-transformers', @@ -300,7 +300,6 @@ describe('Provider Registry', () => { }); it('creates OpenAIEmbeddingProvider for openai-compatible', () => { - const { createProviderFromProfile } = require('./registry.js'); const profile: schema.EmbeddingProfile = { id: 'test-openai', providerKind: 'openai-compatible', @@ -323,7 +322,6 @@ describe('Provider Registry', () => { }); it('returns NoopEmbeddingProvider for unknown providerKind', () => { - const { createProviderFromProfile } = require('./registry.js'); const profile: schema.EmbeddingProfile = { id: 'test-unknown', providerKind: 'unknown-provider', @@ -374,30 +372,30 @@ describe('EmbeddingService', () => { it('stores embeddings in snippet_embeddings table', async () => { const snippetId = seedSnippet(db, client); const provider = makeProvider(4); - const service = new EmbeddingService(client, provider, 'test-profile'); + const service = new EmbeddingService(client, provider, 'local-default'); await service.embedSnippets([snippetId]); const rows = client .prepare('SELECT * FROM snippet_embeddings WHERE snippet_id = ? AND profile_id = ?') - .all(snippetId, 'test-profile'); + .all(snippetId, 'local-default'); expect(rows).toHaveLength(1); const row = rows[0] as { model: string; dimensions: number; embedding: Buffer; profile_id: string }; expect(row.model).toBe('test-model'); expect(row.dimensions).toBe(4); - expect(row.profile_id).toBe('test-profile'); + expect(row.profile_id).toBe('local-default'); expect(row.embedding).toBeInstanceOf(Buffer); }); it('stores embeddings as retrievable Float32Array blobs', async () => { const snippetId = seedSnippet(db, client); const provider = makeProvider(3); - const service = new EmbeddingService(client, provider, 'test-profile'); + const service = new EmbeddingService(client, provider, 'local-default'); await service.embedSnippets([snippetId]); - const embedding = service.getEmbedding(snippetId, 'test-profile'); + const embedding = service.getEmbedding(snippetId, 'local-default'); expect(embedding).toBeInstanceOf(Float32Array); expect(embedding).toHaveLength(3); expect(embedding![0]).toBeCloseTo(0.0, 5); @@ -405,16 +403,6 @@ describe('EmbeddingService', () => { expect(embedding![2]).toBeCloseTo(0.2, 5); }); - await service.embedSnippets([snippetId]); - - const retrieved = service.getEmbedding(snippetId); - expect(retrieved).toBeInstanceOf(Float32Array); - expect(retrieved!.length).toBe(3); - expect(retrieved![0]).toBeCloseTo(0.0, 5); - expect(retrieved![1]).toBeCloseTo(0.1, 5); - expect(retrieved![2]).toBeCloseTo(0.2, 5); - }); - it('is idempotent — re-embedding replaces the existing row', async () => { const snippetId = seedSnippet(db, client); const provider = makeProvider(2); diff --git a/src/lib/server/search/hybrid.search.service.test.ts b/src/lib/server/search/hybrid.search.service.test.ts index 5121e56..67c3e47 100644 --- a/src/lib/server/search/hybrid.search.service.test.ts +++ b/src/lib/server/search/hybrid.search.service.test.ts @@ -288,7 +288,7 @@ describe('VectorSearch', () => { it('returns empty array when no embeddings exist', () => { const vs = new VectorSearch(client); - const results = vs.vectorSearch(new Float32Array([1, 0]), repoId); + const results = vs.vectorSearch(new Float32Array([1, 0]), { repositoryId: repoId }); expect(results).toHaveLength(0); }); @@ -306,7 +306,7 @@ describe('VectorSearch', () => { seedEmbedding(client, s3, [0, 0, 1, 0]); const vs = new VectorSearch(client); - const results = vs.vectorSearch(new Float32Array([1, 0, 0, 0]), repoId); + const results = vs.vectorSearch(new Float32Array([1, 0, 0, 0]), { repositoryId: repoId }); expect(results[0].snippetId).toBe(s1); expect(results[0].score).toBeCloseTo(1.0, 4); @@ -324,7 +324,7 @@ describe('VectorSearch', () => { } const vs = new VectorSearch(client); - const results = vs.vectorSearch(new Float32Array([1, 0]), repoId, 3); + const results = vs.vectorSearch(new Float32Array([1, 0]), { repositoryId: repoId, limit: 3 }); expect(results.length).toBeLessThanOrEqual(3); }); @@ -343,7 +343,7 @@ describe('VectorSearch', () => { seedEmbedding(client, s2, [1, 0]); const vs = new VectorSearch(client); - const results = vs.vectorSearch(new Float32Array([1, 0]), repoId); + const results = vs.vectorSearch(new Float32Array([1, 0]), { repositoryId: repoId }); expect(results).toHaveLength(1); expect(results[0].snippetId).toBe(s1); @@ -354,7 +354,7 @@ describe('VectorSearch', () => { seedEmbedding(client, s1, [-0.5, 0.5]); const vs = new VectorSearch(client); - const results = vs.vectorSearch(new Float32Array([-0.5, 0.5]), repoId); + const results = vs.vectorSearch(new Float32Array([-0.5, 0.5]), { repositoryId: repoId }); expect(results[0].score).toBeCloseTo(1.0, 4); }); }); @@ -733,11 +733,6 @@ describe('HybridSearchService', () => { content: 'keyword only test' }); - client.exec( - `INSERT INTO snippets_fts (id, repository_id, version_id, title, breadcrumb, content) - VALUES ('${snippetId}', '${repoId}', NULL, NULL, NULL, 'keyword only test')` - ); - let embedCalled = false; const mockProvider: EmbeddingProvider = { name: 'mock',