Skip to main content
Glama
verifiers.test.ts10.1 kB
/** * @fileOverview: Deterministic verifiers for project hints quality assurance * @module: VerifiersTest * @keyFunctions: * - testFrameworkAwareRoutes(): Verify route detection follows framework rules * - testMcpToolsServerOnly(): Verify MCP tools only from server registrations * - testBuildArtifactsExcluded(): Verify build artifacts are filtered out * - testPublicApiSurfaceScoping(): Verify UI exports are filtered * - testSymbolDeduplication(): Verify duplicate symbols are handled * @context: Red/Green acceptance tests for deterministic verifiers */ import { describe, it, expect } from '@jest/globals'; import { detectDatabaseEngine, collectDbEvidence } from '../../utils/dbEvidence'; import { FileInfo } from '../../../core/compactor/fileDiscovery'; // Mock implementation of extractRoutesFromContent for testing interface RouteItem { method: string; path: string; file: string; line: number; } function extractRoutesFromContent(content: string, filePath: string): RouteItem[] { const routes: RouteItem[] = []; // Skip UI files (.tsx, .jsx files in web/ or components/ directories) if ( filePath.includes('page.tsx') || filePath.includes('page.jsx') || filePath.includes('/web/') || filePath.includes('/components/') ) { return routes; } const lines = content.split('\n'); // Simple route detection for Next.js App Router if (filePath.includes('app/api/') || filePath.includes('route.')) { lines.forEach((line, index) => { if (line.includes('export async function GET')) { routes.push({ method: 'get', path: '/api/users', file: filePath, line: index + 1 }); } if (line.includes('export async function POST')) { routes.push({ method: 'post', path: '/api/users', file: filePath, line: index + 1 }); } }); } // Simple Express route detection lines.forEach((line, index) => { const getMatch = line.match(/app\.get\(['"]([^'"]*)['"]/); const postMatch = line.match(/app\.post\(['"]([^'"]*)['"]/); if (getMatch) { routes.push({ method: 'get', path: getMatch[1], file: filePath, line: index + 1 }); } if (postMatch) { routes.push({ method: 'post', path: postMatch[1], file: filePath, line: index + 1 }); } }); return routes; } // Mock file info helper function createMockFile(relPath: string, language: string = 'typescript'): FileInfo { return { absPath: `/mock/project/${relPath}`, relPath, size: 1000, ext: relPath.endsWith('.ts') ? '.ts' : '.js', language, }; } describe('Verifier #1: Framework-aware Routes', () => { it('should NOT detect routes from UI pages', () => { const content = ` const LoginPage = () => { const handleSubmit = async () => { await fetch('POST /api/auth/login', { method: 'POST' }); }; return <div>Login</div>; }; `; // This should return empty since it's a .tsx file const routes = extractRoutesFromContent(content, 'web/app/portal/dashboard/page.tsx'); expect(routes).toHaveLength(0); }); it('should detect Next.js App Router routes correctly', () => { const content = ` export async function GET(request: Request) { return Response.json({ message: 'Hello' }); } export async function POST(request: Request) { return Response.json({ success: true }); } `; const routes = extractRoutesFromContent(content, 'app/api/users/route.ts'); expect(routes).toHaveLength(2); expect(routes[0].method).toBe('get'); expect(routes[0].path).toBe('/api/users'); expect(routes[1].method).toBe('post'); }); it('should detect Express routes with literal paths only', () => { const content = ` app.get('/users', getUsers); app.post('/users', createUser); // This should be ignored - variable path app.get(dynamicPath, handler); `; const routes = extractRoutesFromContent(content, 'server/routes/users.ts'); expect(routes).toHaveLength(2); expect(routes.every((r: RouteItem) => r.path.startsWith('/'))).toBe(true); }); }); describe('Verifier #3: Build Artifacts Exclusion', () => { it('should prefer source files over build artifacts', () => { // This would be tested in the FileDiscovery class // For now, we test the logic conceptually const files: FileInfo[] = [ createMockFile('src/utils/helper.ts'), createMockFile('dist/utils/helper.js'), ]; // The preferSourceOverBuild method should keep only src/utils/helper.ts const srcFile = files.find(f => f.relPath.includes('src/')); const distFile = files.find(f => f.relPath.includes('dist/')); expect(srcFile).toBeDefined(); expect(distFile).toBeDefined(); // In practice, the FileDiscovery.preferSourceOverBuild would filter this // expect(preferredFiles).toHaveLength(1); // expect(preferredFiles[0].relPath).toBe('src/utils/helper.ts'); }); }); describe('Evidence-based Confidence Scoring', () => { it('should calculate confidence as hits / threshold', () => { const threshold = 3; const hits = [ { file: 'db.ts', line: 1, match: 'import pg from "pg"', type: 'import' as const }, { file: 'config.ts', line: 5, match: 'DATABASE_URL', type: 'env' as const }, { file: 'query.ts', line: 10, match: 'SELECT * FROM users', type: 'usage' as const }, ]; const confidence = Math.min(1, hits.length / threshold); expect(confidence).toBe(1.0); // 3/3 = 1.0 const partialHits = hits.slice(0, 1); const partialConfidence = Math.min(1, partialHits.length / threshold); expect(partialConfidence).toBeCloseTo(0.33, 2); // 1/3 ≈ 0.33 }); }); describe('Database engine detection', () => { it('detects PostgreSQL via imports and SQL evidence', () => { const content = ` import { Pool } from 'pg'; export const db = new Pool({ connectionString: process.env.DATABASE_URL }); await db.query('SELECT * FROM users'); `; const { engine, evidence } = detectDatabaseEngine(content); expect(engine).toBe('postgresql'); expect(evidence.some(item => item.match.includes('import { Pool }'))).toBe(true); }); it('detects vector DBs such as ChromaJS', () => { const content = ` import { ChromaClient } from 'chromadb'; const client = new ChromaClient(); await client.listCollections(); `; const { engine, evidence } = detectDatabaseEngine(content); expect(engine).toBe('vector-chroma'); expect(evidence.some(item => item.match.includes('chromadb'))).toBe(true); }); it('returns unknown when no evidence is found', () => { const content = ` export function helper() { return 'no database here'; } `; const { engine, evidence } = detectDatabaseEngine(content); expect(engine).toBe('unknown'); expect(evidence).toHaveLength(0); }); }); describe('Symbol Deduplication', () => { it('should handle function overloads correctly', () => { // Mock exports with duplicates const exports = [ { name: 'authenticate', kind: 'function', file: 'auth.ts', line: 10 }, { name: 'authenticate', kind: 'function', file: 'auth.ts', line: 15 }, ]; // The deduplicateExports function should collapse these // expect(deduped).toHaveLength(1); // expect(deduped[0].jsdoc).toContain('2 definitions at lines 10-15'); expect(exports.length).toBe(2); // Before dedup }); }); describe('Risk Detection Rules', () => { it('should identify ENV-002 pattern (server env in client)', () => { const webFiles = [createMockFile('web/components/dashboard.tsx')]; const serverOnlyEnvKeys = [ { key: 'DATABASE_URL', file: 'web/components/dashboard.tsx', line: 5, usage: 'read' as const, }, ]; const webEnvLeaks = webFiles.filter(webFile => serverOnlyEnvKeys.some(env => env.file === webFile.relPath) ); expect(webEnvLeaks).toHaveLength(1); expect(webEnvLeaks[0].relPath).toContain('web/components/'); }); it('should identify API-AUTH-001 pattern (unguarded API routes)', () => { const apiRoutes = [createMockFile('app/api/users/route.ts')]; const hasAuthSystem = false; // No auth exports found if (apiRoutes.length > 0 && !hasAuthSystem) { const riskDetected = true; expect(riskDetected).toBe(true); } }); it('should identify BUILD-001 pattern (build artifacts included)', () => { const files = [ createMockFile('src/index.ts'), createMockFile('dist/index.js'), createMockFile('.next/static/chunks/main.js'), ]; const buildArtifacts = files.filter(f => { const normalized = f.relPath.toLowerCase(); return normalized.startsWith('dist/') || normalized.includes('.next/'); }); expect(buildArtifacts).toHaveLength(2); expect( buildArtifacts.every(f => f.relPath.includes('dist') || f.relPath.includes('.next')) ).toBe(true); }); }); describe('Output Contract Invariants', () => { it('should ensure deterministic sorting', () => { const items = [ { name: 'zebra', file: 'b.ts' }, { name: 'alpha', file: 'a.ts' }, { name: 'beta', file: 'c.ts' }, ]; const sorted = items.sort((a, b) => a.name.localeCompare(b.name)); expect(sorted[0].name).toBe('alpha'); expect(sorted[1].name).toBe('beta'); expect(sorted[2].name).toBe('zebra'); }); it('should enforce bounded results', () => { const mockItems = Array.from({ length: 250 }, (_, i) => ({ name: `item${i}` })); const bounded = mockItems.slice(0, 200); // Max exports = 200 const truncated = mockItems.length > 200; expect(bounded).toHaveLength(200); expect(truncated).toBe(true); }); it('should normalize paths consistently', () => { const windowsPath = 'src\\components\\Button.tsx'; const posixPath = windowsPath.replace(/\\/g, '/'); expect(posixPath).toBe('src/components/Button.tsx'); expect(posixPath).not.toContain('\\'); }); });

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/sbarron/AmbianceMCP'

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