Spaces:
Runtime error
Runtime error
File size: 5,156 Bytes
077865a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | import { describe, it, expect, beforeAll } from 'vitest';
import type { Express } from 'express';
import { createApp } from '../../app.js';
import { initDb } from '../../db/index.js';
import { mintDashboardToken, isGatedApiPath } from '../helpers/auth.js';
let dashToken = '';
async function request(app: Express, method: string, path: string, body?: any) {
const server = app.listen(0);
const addr = server.address() as any;
const url = `http://127.0.0.1:${addr.port}${path}`;
const res = await fetch(url, {
method,
headers: {
...(body ? { 'Content-Type': 'application/json' } : {}),
...(isGatedApiPath(path) ? { Authorization: `Bearer ${dashToken}` } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json().catch(() => null);
server.close();
return { status: res.status, body: data };
}
describe('Fallback API', () => {
let app: Express;
beforeAll(() => {
process.env.ENCRYPTION_KEY = '0'.repeat(64);
initDb(':memory:');
app = createApp();
dashToken = mintDashboardToken();
});
it('GET /api/fallback returns fallback chain', async () => {
const { status, body } = await request(app, 'GET', '/api/fallback');
expect(status).toBe(200);
expect(Array.isArray(body)).toBe(true);
expect(body.length).toBeGreaterThan(0);
// Should be sorted by priority
for (let i = 1; i < body.length; i++) {
expect(body[i].priority).toBeGreaterThanOrEqual(body[i - 1].priority);
}
});
it('GET /api/fallback entries have expected fields', async () => {
const { body } = await request(app, 'GET', '/api/fallback');
const first = body[0];
expect(first).toHaveProperty('modelDbId');
expect(first).toHaveProperty('priority');
expect(first).toHaveProperty('enabled');
expect(first).toHaveProperty('platform');
expect(first).toHaveProperty('displayName');
expect(first).toHaveProperty('intelligenceRank');
});
it('PUT /api/fallback updates order', async () => {
const { body: original } = await request(app, 'GET', '/api/fallback');
// Reverse the order
const reversed = original.map((e: any, i: number) => ({
modelDbId: e.modelDbId,
priority: original.length - i,
enabled: e.enabled,
}));
const { status } = await request(app, 'PUT', '/api/fallback', reversed);
expect(status).toBe(200);
// Verify order changed
const { body: after } = await request(app, 'GET', '/api/fallback');
expect(after[0].modelDbId).toBe(original[original.length - 1].modelDbId);
// Restore original order
const restore = original.map((e: any, i: number) => ({
modelDbId: e.modelDbId,
priority: i + 1,
enabled: e.enabled,
}));
await request(app, 'PUT', '/api/fallback', restore);
});
it('POST /api/fallback/sort/intelligence sorts by cross-provider tier, then rank', async () => {
const { status } = await request(app, 'POST', '/api/fallback/sort/intelligence');
expect(status).toBe(200);
const { body } = await request(app, 'GET', '/api/fallback');
// intelligence_rank is per-provider, so the sort normalizes on the
// cross-provider capability tier (size_label) first (issue #135).
const tier: Record<string, number> = { Frontier: 1, Large: 2, Medium: 3, Small: 4 };
const tierOf = (label: string) => tier[label] ?? 5;
for (let i = 1; i < body.length; i++) {
const prevTier = tierOf(body[i - 1].sizeLabel);
const curTier = tierOf(body[i].sizeLabel);
// Capability tier never decreases...
expect(curTier).toBeGreaterThanOrEqual(prevTier);
// ...and within the same tier, per-provider rank breaks the tie.
if (curTier === prevTier) {
expect(body[i].intelligenceRank).toBeGreaterThanOrEqual(body[i - 1].intelligenceRank);
}
}
});
it('intelligence sort never places a weaker tier above a Frontier model (#135)', async () => {
await request(app, 'POST', '/api/fallback/sort/intelligence');
const { body } = await request(app, 'GET', '/api/fallback');
// The last Frontier model must come before the first non-Frontier model —
// i.e. no "Intel #1 from a weaker provider" leaks above the frontier tier.
const lastFrontier = body.map((m: any) => m.sizeLabel).lastIndexOf('Frontier');
const firstNonFrontier = body.findIndex((m: any) => m.sizeLabel !== 'Frontier');
if (lastFrontier !== -1 && firstNonFrontier !== -1) {
expect(lastFrontier).toBeLessThan(firstNonFrontier);
}
});
it('POST /api/fallback/sort/speed sorts by speed', async () => {
const { status } = await request(app, 'POST', '/api/fallback/sort/speed');
expect(status).toBe(200);
const { body } = await request(app, 'GET', '/api/fallback');
// Should be sorted ascending by speed rank
for (let i = 1; i < body.length; i++) {
expect(body[i].speedRank).toBeGreaterThanOrEqual(body[i - 1].speedRank);
}
});
it('POST /api/fallback/sort/invalid returns 400', async () => {
const { status } = await request(app, 'POST', '/api/fallback/sort/invalid');
expect(status).toBe(400);
});
});
|