- Rename directory keep-notes -> memento-note with all code references - Prisma: SQLite -> PostgreSQL (both app and MCP server schemas) - Sync MCP schema with main app (add missing fields, relations, indexes) - Delete 17 SQLite migrations (clean slate for PostgreSQL) - Remove SQLite dependencies (@libsql/client, better-sqlite3, etc.) - Fix MCP server: hardcoded Windows DB paths -> DATABASE_URL env var - Fix MCP server: .dockerignore excluded index-sse.js (SSE mode broken) - MCP Dockerfile: node:20 -> node:22 - Docker Compose: add postgres service, remove SQLite volume - Generate favicon.ico, icon-192.png, icon-512.png, apple-icon.png - Update layout.tsx icons and manifest.json for PNG icons - Update all .env files for PostgreSQL - Rewrite README.md with updated sections - Remove mcp-server/node_modules and prisma/client-generated from git tracking Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
137 lines
4.5 KiB
TypeScript
137 lines
4.5 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { validateEmbedding, calculateL2Norm, normalizeEmbedding } from '../../lib/utils'
|
|
|
|
test.describe('Embedding Validation', () => {
|
|
test.describe('validateEmbedding()', () => {
|
|
test('should validate a normal embedding', () => {
|
|
const embedding = [0.1, 0.2, 0.3, 0.4, 0.5]
|
|
const result = validateEmbedding(embedding)
|
|
|
|
expect(result.valid).toBe(true)
|
|
expect(result.issues).toHaveLength(0)
|
|
})
|
|
|
|
test('should reject empty embedding', () => {
|
|
const result = validateEmbedding([])
|
|
|
|
expect(result.valid).toBe(false)
|
|
expect(result.issues).toContain('Embedding is empty or has zero dimensionality')
|
|
})
|
|
|
|
test('should reject null embedding', () => {
|
|
const result = validateEmbedding(null as any)
|
|
|
|
expect(result.valid).toBe(false)
|
|
expect(result.issues).toContain('Embedding is empty or has zero dimensionality')
|
|
})
|
|
|
|
test('should reject embedding with NaN values', () => {
|
|
const embedding = [0.1, NaN, 0.3, 0.4, 0.5]
|
|
const result = validateEmbedding(embedding)
|
|
|
|
expect(result.valid).toBe(false)
|
|
expect(result.issues).toContain('Embedding contains NaN values')
|
|
})
|
|
|
|
test('should reject embedding with Infinity values', () => {
|
|
const embedding = [0.1, 0.2, Infinity, 0.4, 0.5]
|
|
const result = validateEmbedding(embedding)
|
|
|
|
expect(result.valid).toBe(false)
|
|
expect(result.issues).toContain('Embedding contains Infinity values')
|
|
})
|
|
|
|
test('should reject zero vector', () => {
|
|
const embedding = [0, 0, 0, 0, 0]
|
|
const result = validateEmbedding(embedding)
|
|
|
|
expect(result.valid).toBe(false)
|
|
expect(result.issues).toContain('Embedding is a zero vector (all values are 0)')
|
|
})
|
|
|
|
test('should warn about L2 norm outside normal range', () => {
|
|
// Very small norm
|
|
const smallEmbedding = [0.01, 0.01, 0.01]
|
|
const result1 = validateEmbedding(smallEmbedding)
|
|
|
|
expect(result1.valid).toBe(false)
|
|
expect(result1.issues.some(issue => issue.includes('L2 norm'))).toBe(true)
|
|
|
|
// Very large norm
|
|
const largeEmbedding = [2, 2, 2]
|
|
const result2 = validateEmbedding(largeEmbedding)
|
|
|
|
expect(result2.valid).toBe(false)
|
|
expect(result2.issues.some(issue => issue.includes('L2 norm'))).toBe(true)
|
|
})
|
|
|
|
test('should detect multiple issues', () => {
|
|
const embedding = [NaN, Infinity, 0]
|
|
const result = validateEmbedding(embedding)
|
|
|
|
expect(result.valid).toBe(false)
|
|
expect(result.issues.length).toBeGreaterThan(1)
|
|
expect(result.issues).toContain('Embedding contains NaN values')
|
|
expect(result.issues).toContain('Embedding contains Infinity values')
|
|
// Note: NaN and Infinity are not zero, so it won't detect zero vector
|
|
})
|
|
})
|
|
|
|
test.describe('calculateL2Norm()', () => {
|
|
test('should calculate correct L2 norm', () => {
|
|
const vector = [3, 4]
|
|
const norm = calculateL2Norm(vector)
|
|
|
|
expect(norm).toBe(5) // sqrt(3^2 + 4^2) = 5
|
|
})
|
|
|
|
test('should return 0 for zero vector', () => {
|
|
const vector = [0, 0, 0]
|
|
const norm = calculateL2Norm(vector)
|
|
|
|
expect(norm).toBe(0)
|
|
})
|
|
|
|
test('should handle negative values', () => {
|
|
const vector = [-3, -4]
|
|
const norm = calculateL2Norm(vector)
|
|
|
|
expect(norm).toBe(5) // sqrt((-3)^2 + (-4)^2) = 5
|
|
})
|
|
})
|
|
|
|
test.describe('normalizeEmbedding()', () => {
|
|
test('should normalize a vector to unit L2 norm', () => {
|
|
const embedding = [3, 4]
|
|
const normalized = normalizeEmbedding(embedding)
|
|
const norm = calculateL2Norm(normalized)
|
|
|
|
expect(norm).toBeCloseTo(1.0, 5)
|
|
})
|
|
|
|
test('should preserve direction of vector', () => {
|
|
const embedding = [1, 2, 3]
|
|
const normalized = normalizeEmbedding(embedding)
|
|
|
|
// Check that ratios are preserved
|
|
expect(normalized[1] / normalized[0]).toBeCloseTo(embedding[1] / embedding[0], 5)
|
|
expect(normalized[2] / normalized[1]).toBeCloseTo(embedding[2] / embedding[1], 5)
|
|
})
|
|
|
|
test('should return zero vector unchanged', () => {
|
|
const embedding = [0, 0, 0]
|
|
const normalized = normalizeEmbedding(embedding)
|
|
|
|
expect(normalized).toEqual(embedding)
|
|
})
|
|
|
|
test('should handle already normalized vectors', () => {
|
|
const embedding = [0.707, 0.707] // Already approximately unit norm
|
|
const normalized = normalizeEmbedding(embedding)
|
|
const norm = calculateL2Norm(normalized)
|
|
|
|
expect(norm).toBeCloseTo(1.0, 5)
|
|
})
|
|
})
|
|
})
|