Files
wifi-densepose/npm/packages/agentic-synth/tests/unit/routing/model-router.test.js
ruv d803bfe2b1 Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
2026-02-28 14:39:40 -05:00

258 lines
7.6 KiB
JavaScript

/**
* Unit tests for ModelRouter
*/
import { describe, it, expect, beforeEach } from 'vitest';
import { ModelRouter } from '../../../src/routing/model-router.js';
describe('ModelRouter', () => {
let router;
let models;
beforeEach(() => {
models = [
{ id: 'model-1', endpoint: 'http://api1.com', capabilities: ['general', 'code'] },
{ id: 'model-2', endpoint: 'http://api2.com', capabilities: ['general'] },
{ id: 'model-3', endpoint: 'http://api3.com', capabilities: ['math', 'reasoning'] }
];
router = new ModelRouter({
models,
strategy: 'round-robin'
});
});
describe('constructor', () => {
it('should create router with default options', () => {
const defaultRouter = new ModelRouter();
expect(defaultRouter.models).toEqual([]);
expect(defaultRouter.strategy).toBe('round-robin');
});
it('should accept custom options', () => {
expect(router.models).toEqual(models);
expect(router.strategy).toBe('round-robin');
});
it('should initialize model stats', () => {
models.forEach(model => {
const stats = router.getStats(model.id);
expect(stats).toBeDefined();
expect(stats.requests).toBe(0);
expect(stats.errors).toBe(0);
});
});
});
describe('registerModel', () => {
it('should register new model', () => {
const newModel = { id: 'model-4', endpoint: 'http://api4.com' };
router.registerModel(newModel);
expect(router.models).toContain(newModel);
expect(router.getStats('model-4')).toBeDefined();
});
it('should throw error for invalid model', () => {
expect(() => router.registerModel({})).toThrow('Model must have id and endpoint');
expect(() => router.registerModel({ id: 'test' })).toThrow('Model must have id and endpoint');
});
it('should initialize stats for new model', () => {
const newModel = { id: 'model-4', endpoint: 'http://api4.com' };
router.registerModel(newModel);
const stats = router.getStats('model-4');
expect(stats.requests).toBe(0);
expect(stats.errors).toBe(0);
expect(stats.avgLatency).toBe(0);
});
});
describe('route - round-robin', () => {
it('should distribute requests evenly', () => {
const results = [];
for (let i = 0; i < 6; i++) {
results.push(router.route({}));
}
expect(results[0]).toBe('model-1');
expect(results[1]).toBe('model-2');
expect(results[2]).toBe('model-3');
expect(results[3]).toBe('model-1');
expect(results[4]).toBe('model-2');
expect(results[5]).toBe('model-3');
});
it('should wrap around after reaching end', () => {
for (let i = 0; i < 3; i++) {
router.route({});
}
expect(router.route({})).toBe('model-1');
});
});
describe('route - least-latency', () => {
beforeEach(() => {
router.strategy = 'least-latency';
// Record some metrics
router.recordMetrics('model-1', 100);
router.recordMetrics('model-2', 50);
router.recordMetrics('model-3', 150);
});
it('should route to model with lowest latency', () => {
const modelId = router.route({});
expect(modelId).toBe('model-2');
});
it('should update as latencies change', () => {
router.recordMetrics('model-1', 20);
router.recordMetrics('model-1', 20);
const modelId = router.route({});
expect(modelId).toBe('model-1');
});
});
describe('route - cost-optimized', () => {
beforeEach(() => {
router.strategy = 'cost-optimized';
});
it('should route small requests to first model', () => {
const smallRequest = { data: 'test' };
const modelId = router.route(smallRequest);
expect(modelId).toBe('model-1');
});
it('should route large requests to last model', () => {
const largeRequest = { data: 'x'.repeat(2000) };
const modelId = router.route(largeRequest);
expect(modelId).toBe('model-3');
});
});
describe('route - capability-based', () => {
beforeEach(() => {
router.strategy = 'capability-based';
});
it('should route to model with required capability', () => {
const request = { capability: 'code' };
const modelId = router.route(request);
expect(modelId).toBe('model-1');
});
it('should route math requests to capable model', () => {
const request = { capability: 'math' };
const modelId = router.route(request);
expect(modelId).toBe('model-3');
});
it('should fallback to first model if no match', () => {
const request = { capability: 'unsupported' };
const modelId = router.route(request);
expect(modelId).toBe('model-1');
});
});
describe('route - error handling', () => {
it('should throw error when no models available', () => {
const emptyRouter = new ModelRouter();
expect(() => emptyRouter.route({})).toThrow('No models available for routing');
});
});
describe('recordMetrics', () => {
it('should record successful requests', () => {
router.recordMetrics('model-1', 100, true);
const stats = router.getStats('model-1');
expect(stats.requests).toBe(1);
expect(stats.errors).toBe(0);
expect(stats.avgLatency).toBe(100);
});
it('should record failed requests', () => {
router.recordMetrics('model-1', 100, false);
const stats = router.getStats('model-1');
expect(stats.requests).toBe(1);
expect(stats.errors).toBe(1);
});
it('should calculate average latency', () => {
router.recordMetrics('model-1', 100);
router.recordMetrics('model-1', 200);
router.recordMetrics('model-1', 300);
const stats = router.getStats('model-1');
expect(stats.avgLatency).toBe(200);
});
it('should handle non-existent model gracefully', () => {
router.recordMetrics('nonexistent', 100);
expect(router.getStats('nonexistent')).toBeUndefined();
});
});
describe('getStats', () => {
it('should return stats for specific model', () => {
router.recordMetrics('model-1', 100);
const stats = router.getStats('model-1');
expect(stats).toHaveProperty('requests');
expect(stats).toHaveProperty('errors');
expect(stats).toHaveProperty('avgLatency');
});
it('should return all stats when no model specified', () => {
const allStats = router.getStats();
expect(allStats).toHaveProperty('model-1');
expect(allStats).toHaveProperty('model-2');
expect(allStats).toHaveProperty('model-3');
});
it('should track multiple models independently', () => {
router.recordMetrics('model-1', 100);
router.recordMetrics('model-2', 200);
expect(router.getStats('model-1').avgLatency).toBe(100);
expect(router.getStats('model-2').avgLatency).toBe(200);
});
});
describe('performance', () => {
it('should handle 1000 routing decisions quickly', () => {
const start = Date.now();
for (let i = 0; i < 1000; i++) {
router.route({});
}
const duration = Date.now() - start;
expect(duration).toBeLessThan(100); // Less than 100ms
});
it('should efficiently handle many models', () => {
const manyModels = Array.from({ length: 100 }, (_, i) => ({
id: `model-${i}`,
endpoint: `http://api${i}.com`
}));
const largeRouter = new ModelRouter({ models: manyModels });
const start = Date.now();
for (let i = 0; i < 1000; i++) {
largeRouter.route({});
}
const duration = Date.now() - start;
expect(duration).toBeLessThan(200);
});
});
});