test(platform-service): add usage route-level tests — 386 tests, 37.9% stmts

This commit is contained in:
saravanakumardb1 2026-02-16 12:15:40 -08:00
parent 149d97dd9f
commit 1cb9af3ae4

View File

@ -0,0 +1,155 @@
/**
* Route-level tests for usage module Fastify inject.
*/
import Fastify from 'fastify';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
const repoMock = {
list: vi.fn(),
upsert: vi.fn(),
getMonthlyUsage: vi.fn(),
getByDate: vi.fn(),
};
vi.mock('./repository.js', () => repoMock);
vi.mock('../../lib/request-context.js', () => ({
getRequestProductId: () => 'lysnrai',
}));
const baseUsage = {
id: 'usg_2026-02-16_user_1',
productId: 'lysnrai',
userId: 'user_1',
date: '2026-02-16',
dictations: 5,
words: 250,
durationMs: 30000,
tokensUsed: 1200,
costUsd: 0.01,
model: 'gpt-4o-mini',
source: 'desktop',
createdAt: '2026-02-16T00:00:00Z',
};
describe('usageRoutes', () => {
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
vi.restoreAllMocks();
});
it('GET /usage returns usage records', async () => {
repoMock.list.mockResolvedValue([baseUsage]);
const { usageRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(usageRoutes, { prefix: '/api' });
const res = await app.inject({ method: 'GET', url: '/api/usage' });
expect(res.statusCode).toBe(200);
const data = JSON.parse(res.body);
expect(data.records).toHaveLength(1);
expect(data.count).toBe(1);
});
it('GET /usage/summary returns aggregated summary', async () => {
repoMock.list.mockResolvedValue([baseUsage]);
const { usageRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(usageRoutes, { prefix: '/api' });
const res = await app.inject({ method: 'GET', url: '/api/usage/summary' });
expect(res.statusCode).toBe(200);
const data = JSON.parse(res.body);
expect(data.totalWords).toBe(250);
expect(data.totalDictations).toBe(5);
expect(data.totalTokens).toBe(1200);
expect(data.modelBreakdown).toHaveLength(1);
expect(data.sourceBreakdown).toHaveLength(1);
expect(data.productBreakdown).toHaveLength(1);
});
it('POST /usage upserts a usage record', async () => {
repoMock.upsert.mockResolvedValue(baseUsage);
const { usageRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(usageRoutes, { prefix: '/api' });
const res = await app.inject({
method: 'POST',
url: '/api/usage',
payload: {
userId: 'user_1',
date: '2026-02-16',
dictations: 5,
words: 250,
tokensUsed: 1200,
},
});
expect(res.statusCode).toBe(200);
expect(repoMock.upsert).toHaveBeenCalled();
});
it('POST /usage rejects invalid input', async () => {
const { usageRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(usageRoutes, { prefix: '/api' });
const res = await app.inject({
method: 'POST',
url: '/api/usage',
payload: { date: 'invalid' },
});
expect(res.statusCode).toBe(400);
});
it('POST /usage/check-limits returns allowed status', async () => {
repoMock.getMonthlyUsage.mockResolvedValue({ tokens: 500, words: 200, dictations: 5 });
const { usageRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(usageRoutes, { prefix: '/api' });
const res = await app.inject({
method: 'POST',
url: '/api/usage/check-limits',
payload: { userId: 'user_1', plan: 'free' },
});
expect(res.statusCode).toBe(200);
const data = JSON.parse(res.body);
expect(data.allowed).toBe(true);
expect(data.exceeded).toEqual([]);
});
it('POST /usage/check-limits detects exceeded limits', async () => {
repoMock.getMonthlyUsage.mockResolvedValue({ tokens: 20000, words: 10000, dictations: 200 });
const { usageRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(usageRoutes, { prefix: '/api' });
const res = await app.inject({
method: 'POST',
url: '/api/usage/check-limits',
payload: { userId: 'user_1', plan: 'free' },
});
expect(res.statusCode).toBe(200);
const data = JSON.parse(res.body);
expect(data.allowed).toBe(false);
expect(data.exceeded.length).toBeGreaterThan(0);
});
it('POST /usage/check-limits rejects invalid plan', async () => {
const { usageRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(usageRoutes, { prefix: '/api' });
const res = await app.inject({
method: 'POST',
url: '/api/usage/check-limits',
payload: { userId: 'user_1', plan: 'invalid' },
});
expect(res.statusCode).toBe(400);
});
});