FreeLLMAPI / server /src /__tests__ /routes /analytics.test.ts
Nryn215's picture
Upload folder using huggingface_hub
077865a verified
Raw
History Blame Contribute Delete
6.3 kB
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import type { Express } from 'express';
import { createApp } from '../../app.js';
import { getDb, initDb } from '../../db/index.js';
import { mintDashboardToken, isGatedApiPath } from '../helpers/auth.js';
let dashToken = '';
async function request(app: Express, path: string) {
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, {
headers: isGatedApiPath(path) ? { Authorization: `Bearer ${dashToken}` } : {},
});
const data = await res.json().catch(() => null);
server.close();
return { status: res.status, body: data };
}
function insertRequest(createdAt: string) {
const db = getDb();
db.prepare(`
INSERT INTO requests (platform, model_id, status, input_tokens, output_tokens, latency_ms, error, created_at)
VALUES ('test', 'test-model', 'success', 1, 2, 3, NULL, ?)
`).run(createdAt);
}
function insertTokensRequest(
platform: string,
modelId: string,
status: 'success' | 'error',
inputTokens: number,
outputTokens: number,
createdAt: string,
) {
const db = getDb();
db.prepare(`
INSERT INTO requests (platform, model_id, status, input_tokens, output_tokens, latency_ms, error, created_at)
VALUES (?, ?, ?, ?, ?, 3, NULL, ?)
`).run(platform, modelId, status, inputTokens, outputTokens, createdAt);
}
describe('Analytics API', () => {
let app: Express;
beforeAll(() => {
process.env.ENCRYPTION_KEY = '0'.repeat(64);
initDb(':memory:');
app = createApp();
dashToken = mintDashboardToken();
});
beforeEach(() => {
getDb().prepare('DELETE FROM requests').run();
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-05-29T12:00:00.000Z'));
});
afterEach(() => {
vi.useRealTimers();
});
it('uses a rolling 24-hour window for summary analytics', async () => {
insertRequest('2026-05-28 11:59:59');
insertRequest('2026-05-28 12:00:00');
insertRequest('2026-05-29 11:59:59');
const { status, body } = await request(app, '/api/analytics/summary?range=24h');
expect(status).toBe(200);
expect(body.totalRequests).toBe(2);
expect(body.totalInputTokens).toBe(2);
expect(body.totalOutputTokens).toBe(4);
});
it.each([
['7d', '2026-05-22 11:59:59', '2026-05-22 12:00:00'],
['30d', '2026-04-29 11:59:59', '2026-04-29 12:00:00'],
])('uses a rolling %s window for summary analytics', async (range, outside, boundary) => {
insertRequest(outside);
insertRequest(boundary);
insertRequest('2026-05-29 11:59:59');
const { status, body } = await request(app, `/api/analytics/summary?range=${range}`);
expect(status).toBe(200);
expect(body.totalRequests).toBe(2);
});
it('prices savings at the served model paid-equivalent rate', async () => {
// groq/llama-3.3-70b-versatile is mapped at $0.10/M in, $0.32/M out
// (db/model-pricing.ts): 10M in + 5M out → 1.00 + 1.60 = $2.60
insertTokensRequest('groq', 'llama-3.3-70b-versatile', 'success', 10_000_000, 5_000_000, '2026-05-29 11:00:00');
const { status, body } = await request(app, '/api/analytics/summary?range=24h');
expect(status).toBe(200);
expect(body.estimatedCostSavings).toBe(2.6);
// Drives the client's span-based 30-day projection
expect(body.firstRequestAt).toBe('2026-05-29 11:00:00');
});
it('falls back to modest default pricing for unmapped models', async () => {
// Unknown model → $0.20/M in, $0.80/M out: 10M in + 5M out → 2.00 + 4.00 = $6.00
insertTokensRequest('custom', 'mystery-model', 'success', 10_000_000, 5_000_000, '2026-05-29 11:00:00');
const { status, body } = await request(app, '/api/analytics/summary?range=24h');
expect(status).toBe(200);
expect(body.estimatedCostSavings).toBe(6);
});
it('excludes failed requests from savings', async () => {
insertTokensRequest('groq', 'llama-3.3-70b-versatile', 'error', 10_000_000, 0, '2026-05-29 11:00:00');
const { status, body } = await request(app, '/api/analytics/summary?range=24h');
expect(status).toBe(200);
expect(body.estimatedCostSavings).toBe(0);
});
it('returns per-model estimated cost in the by-model breakdown', async () => {
insertTokensRequest('groq', 'llama-3.3-70b-versatile', 'success', 10_000_000, 5_000_000, '2026-05-29 11:00:00');
const { status, body } = await request(app, '/api/analytics/by-model?range=24h');
expect(status).toBe(200);
expect(body[0].estimatedCost).toBe(2.6);
});
describe('pinned vs auto tracking', () => {
function insertPinnedRequest(modelId: string, requestedModel: string | null, createdAt: string) {
getDb().prepare(`
INSERT INTO requests (platform, model_id, requested_model, status, input_tokens, output_tokens, latency_ms, error, created_at)
VALUES ('test', ?, ?, 'success', 1, 2, 3, NULL, ?)
`).run(modelId, requestedModel, createdAt);
}
it('summary splits pinned, honored, and auto requests', async () => {
insertPinnedRequest('model-a', 'model-a', '2026-05-29 11:00:00'); // pin honored
insertPinnedRequest('model-b', 'model-a', '2026-05-29 11:01:00'); // pin overridden by failover
insertPinnedRequest('model-b', null, '2026-05-29 11:02:00'); // auto-routed
const { status, body } = await request(app, '/api/analytics/summary?range=24h');
expect(status).toBe(200);
expect(body.totalRequests).toBe(3);
expect(body.pinnedRequests).toBe(2);
expect(body.pinHonoredRequests).toBe(1);
});
it('by-model counts only requests the model served because it was pinned', async () => {
insertPinnedRequest('model-a', 'model-a', '2026-05-29 11:00:00'); // pinned + served
insertPinnedRequest('model-a', null, '2026-05-29 11:01:00'); // auto, same model
insertPinnedRequest('model-a', 'model-x', '2026-05-29 11:02:00'); // failover landed here
const { status, body } = await request(app, '/api/analytics/by-model?range=24h');
expect(status).toBe(200);
const row = body.find((r: any) => r.modelId === 'model-a');
expect(row.requests).toBe(3);
expect(row.pinnedRequests).toBe(1);
});
});
});