Skip to main content
Glama
assertions.ts8.43 kB
/** * Custom Assertions for ThoughtMCP Tests * * Provides domain-specific assertions for better test readability: * - Memory validation * - Embedding validation * - Performance validation * - Confidence validation */ import { expect } from "vitest"; import type { TestMemory, MemorySector } from "./test-fixtures"; /** * Assert that a value is a valid memory object */ export function assertValidMemory(memory: unknown): asserts memory is TestMemory { expect(memory).toBeDefined(); expect(memory).toBeTypeOf("object"); const mem = memory as TestMemory; expect(mem.id).toBeDefined(); expect(mem.id).toBeTypeOf("string"); expect(mem.content).toBeDefined(); expect(mem.content).toBeTypeOf("string"); expect(mem.createdAt).toBeInstanceOf(Date); expect(mem.lastAccessed).toBeInstanceOf(Date); expect(mem.accessCount).toBeTypeOf("number"); expect(mem.accessCount).toBeGreaterThanOrEqual(0); expect(mem.salience).toBeTypeOf("number"); expect(mem.salience).toBeGreaterThanOrEqual(0); expect(mem.salience).toBeLessThanOrEqual(1); expect(mem.strength).toBeTypeOf("number"); expect(mem.strength).toBeGreaterThanOrEqual(0); expect(mem.strength).toBeLessThanOrEqual(1); expect(mem.userId).toBeDefined(); expect(mem.primarySector).toBeDefined(); expect(["episodic", "semantic", "procedural", "emotional", "reflective"]).toContain( mem.primarySector ); } /** * Assert that a value is a valid embedding vector */ export function assertValidEmbedding( embedding: unknown, expectedDimension: number = 1536 ): asserts embedding is number[] { expect(embedding).toBeDefined(); expect(Array.isArray(embedding)).toBe(true); const emb = embedding as number[]; expect(emb.length).toBe(expectedDimension); for (const value of emb) { expect(value).toBeTypeOf("number"); expect(Number.isFinite(value)).toBe(true); } } /** * Assert that embeddings are normalized (unit length) */ export function assertNormalizedEmbedding(embedding: number[]): void { let magnitude = 0; for (const value of embedding) { magnitude += value * value; } magnitude = Math.sqrt(magnitude); // Allow small floating point error expect(magnitude).toBeCloseTo(1.0, 5); } /** * Assert that two embeddings have expected similarity */ export function assertEmbeddingSimilarity( embedding1: number[], embedding2: number[], expectedSimilarity: number, tolerance: number = 0.1 ): void { expect(embedding1.length).toBe(embedding2.length); let dotProduct = 0; let mag1 = 0; let mag2 = 0; for (let i = 0; i < embedding1.length; i++) { dotProduct += embedding1[i] * embedding2[i]; mag1 += embedding1[i] * embedding1[i]; mag2 += embedding2[i] * embedding2[i]; } const similarity = dotProduct / (Math.sqrt(mag1) * Math.sqrt(mag2)); expect(similarity).toBeGreaterThanOrEqual(expectedSimilarity - tolerance); expect(similarity).toBeLessThanOrEqual(expectedSimilarity + tolerance); } /** * Assert that execution time meets target */ export function assertLatencyTarget( actualMs: number, targetMs: number, operation: string = "Operation" ): void { if (actualMs > targetMs) { throw new Error(`${operation} latency ${actualMs}ms exceeds target ${targetMs}ms`); } expect(actualMs).toBeLessThanOrEqual(targetMs); } /** * Assert that percentile latencies meet targets */ export function assertPercentileTargets( latencies: number[], targets: { p50?: number; p95?: number; p99?: number } ): void { const sorted = [...latencies].sort((a, b) => a - b); if (targets.p50) { const p50 = sorted[Math.floor(sorted.length * 0.5)]; assertLatencyTarget(p50, targets.p50, "p50"); } if (targets.p95) { const p95 = sorted[Math.floor(sorted.length * 0.95)]; assertLatencyTarget(p95, targets.p95, "p95"); } if (targets.p99) { const p99 = sorted[Math.floor(sorted.length * 0.99)]; assertLatencyTarget(p99, targets.p99, "p99"); } } /** * Assert that confidence is calibrated */ export function assertCalibratedConfidence( predicted: number, actual: number, tolerance: number = 0.1 ): void { expect(predicted).toBeTypeOf("number"); expect(predicted).toBeGreaterThanOrEqual(0); expect(predicted).toBeLessThanOrEqual(1); expect(actual).toBeTypeOf("number"); expect(actual).toBeGreaterThanOrEqual(0); expect(actual).toBeLessThanOrEqual(1); const error = Math.abs(predicted - actual); if (error > tolerance) { throw new Error( `Confidence calibration error ${error.toFixed(3)} exceeds tolerance ${tolerance}` ); } expect(error).toBeLessThanOrEqual(tolerance); } /** * Assert that accuracy meets target */ export function assertAccuracyTarget( correct: number, total: number, targetAccuracy: number, metric: string = "Accuracy" ): void { expect(total).toBeGreaterThan(0); const accuracy = correct / total; if (accuracy < targetAccuracy) { throw new Error( `${metric} ${(accuracy * 100).toFixed(1)}% below target ${(targetAccuracy * 100).toFixed(1)}%` ); } expect(accuracy).toBeGreaterThanOrEqual(targetAccuracy); } /** * Assert that value is within range */ export function assertInRange( value: number, min: number, max: number, label: string = "Value" ): void { if (value < min || value > max) { throw new Error(`${label} ${value} outside range [${min}, ${max}]`); } expect(value).toBeGreaterThanOrEqual(min); expect(value).toBeLessThanOrEqual(max); } /** * Assert that overhead is within acceptable limit */ export function assertOverheadLimit( baselineMs: number, actualMs: number, maxOverheadPercent: number, operation: string = "Operation" ): void { const overhead = ((actualMs - baselineMs) / baselineMs) * 100; if (overhead > maxOverheadPercent) { throw new Error( `${operation} overhead ${overhead.toFixed(1)}% exceeds limit ${maxOverheadPercent}%` ); } expect(overhead).toBeLessThanOrEqual(maxOverheadPercent); } /** * Assert that array contains unique values */ export function assertUniqueValues<T>(array: T[], label: string = "Array"): void { const uniqueSet = new Set(array); if (uniqueSet.size !== array.length) { throw new Error( `${label} contains duplicate values (${array.length} items, ${uniqueSet.size} unique)` ); } expect(uniqueSet.size).toBe(array.length); } /** * Assert that value is a valid sector */ export function assertValidSector(sector: unknown): asserts sector is MemorySector { expect(sector).toBeDefined(); expect(["episodic", "semantic", "procedural", "emotional", "reflective"]).toContain(sector); } /** * Assert that emotion state is valid */ export function assertValidEmotionState(emotion: unknown): void { expect(emotion).toBeDefined(); expect(emotion).toBeTypeOf("object"); const em = emotion as { valence: number; arousal: number; dominance: number; confidence: number; }; assertInRange(em.valence, -1, 1, "Valence"); assertInRange(em.arousal, 0, 1, "Arousal"); assertInRange(em.dominance, -1, 1, "Dominance"); assertInRange(em.confidence, 0, 1, "Confidence"); } /** * Measure execution time of async function */ export async function measureExecutionTime<T>( fn: () => Promise<T> ): Promise<{ result: T; durationMs: number }> { const startTime = performance.now(); const result = await fn(); const endTime = performance.now(); const durationMs = endTime - startTime; return { result, durationMs }; } /** * Measure multiple executions and return statistics */ export async function measureMultipleExecutions<T>( fn: () => Promise<T>, iterations: number = 100 ): Promise<{ results: T[]; latencies: number[]; p50: number; p95: number; p99: number; mean: number; min: number; max: number; }> { const results: T[] = []; const latencies: number[] = []; for (let i = 0; i < iterations; i++) { const { result, durationMs } = await measureExecutionTime(fn); results.push(result); latencies.push(durationMs); } const sorted = [...latencies].sort((a, b) => a - b); return { results, latencies, p50: sorted[Math.floor(sorted.length * 0.5)], p95: sorted[Math.floor(sorted.length * 0.95)], p99: sorted[Math.floor(sorted.length * 0.99)], mean: latencies.reduce((a, b) => a + b, 0) / latencies.length, min: Math.min(...latencies), max: Math.max(...latencies), }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keyurgolani/ThoughtMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server