test(platform-service): add subscriptions route-level tests — 395 tests, 39.6% stmts

This commit is contained in:
saravanakumardb1 2026-02-16 12:17:00 -08:00
parent 1cb9af3ae4
commit c7934af227

View File

@ -0,0 +1,190 @@
/**
* Route-level tests for subscriptions module Fastify inject.
*/
import Fastify from 'fastify';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
const repoMock = {
getByUserId: vi.fn(),
getByStripeCustomerId: vi.fn(),
getByStripeCustomerIdAnyProduct: vi.fn(),
createSubscription: vi.fn(),
updateSubscription: vi.fn(),
getPaymentsByUser: vi.fn(),
createPayment: vi.fn(),
};
vi.mock('./repository.js', () => repoMock);
vi.mock('../../lib/request-context.js', () => ({
getRequestProductId: () => 'lysnrai',
}));
const baseSub = {
id: 'sub_user_1_123',
productId: 'lysnrai',
userId: 'user_1',
plan: 'pro',
status: 'active',
currentPeriodStart: '2026-02-01T00:00:00Z',
currentPeriodEnd: '2026-03-01T00:00:00Z',
cancelAtPeriodEnd: false,
monthlyPrice: 9.99,
tokensIncluded: 100000,
tokensUsed: 5000,
createdAt: '2026-02-01T00:00:00Z',
updatedAt: '2026-02-01T00:00:00Z',
};
describe('subscriptionRoutes', () => {
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
vi.restoreAllMocks();
});
it('GET /subscriptions/:userId returns subscription', async () => {
repoMock.getByUserId.mockResolvedValue(baseSub);
const { subscriptionRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(subscriptionRoutes, { prefix: '/api' });
const res = await app.inject({ method: 'GET', url: '/api/subscriptions/user_1' });
expect(res.statusCode).toBe(200);
const data = JSON.parse(res.body);
expect(data.plan).toBe('pro');
});
it('GET /subscriptions/:userId returns 404 when not found', async () => {
repoMock.getByUserId.mockResolvedValue(null);
const { subscriptionRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(subscriptionRoutes, { prefix: '/api' });
const res = await app.inject({ method: 'GET', url: '/api/subscriptions/user_1' });
expect(res.statusCode).toBe(404);
});
it('POST /subscriptions creates a subscription', async () => {
repoMock.createSubscription.mockResolvedValue(baseSub);
const { subscriptionRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(subscriptionRoutes, { prefix: '/api' });
const res = await app.inject({
method: 'POST',
url: '/api/subscriptions',
payload: {
userId: 'user_1',
plan: 'pro',
status: 'active',
monthlyPrice: 9.99,
tokensIncluded: 100000,
},
});
expect(res.statusCode).toBe(201);
});
it('POST /subscriptions with trial days', async () => {
repoMock.createSubscription.mockResolvedValue(baseSub);
const { subscriptionRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(subscriptionRoutes, { prefix: '/api' });
const res = await app.inject({
method: 'POST',
url: '/api/subscriptions',
payload: {
userId: 'user_1',
plan: 'pro',
status: 'trialing',
monthlyPrice: 9.99,
tokensIncluded: 100000,
trialDays: 14,
},
});
expect(res.statusCode).toBe(201);
});
it('POST /subscriptions rejects invalid input', async () => {
const { subscriptionRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(subscriptionRoutes, { prefix: '/api' });
const res = await app.inject({
method: 'POST',
url: '/api/subscriptions',
payload: { plan: 'invalid' },
});
expect(res.statusCode).toBe(400);
});
it('PUT /subscriptions/:userId updates subscription', async () => {
repoMock.getByUserId.mockResolvedValue(baseSub);
repoMock.updateSubscription.mockResolvedValue({ ...baseSub, plan: 'enterprise' });
const { subscriptionRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(subscriptionRoutes, { prefix: '/api' });
const res = await app.inject({
method: 'PUT',
url: '/api/subscriptions/user_1',
payload: { plan: 'enterprise' },
});
expect(res.statusCode).toBe(200);
const data = JSON.parse(res.body);
expect(data.plan).toBe('enterprise');
});
it('PUT /subscriptions/:userId returns 404 when not found', async () => {
repoMock.getByUserId.mockResolvedValue(null);
const { subscriptionRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(subscriptionRoutes, { prefix: '/api' });
const res = await app.inject({
method: 'PUT',
url: '/api/subscriptions/user_1',
payload: { plan: 'enterprise' },
});
expect(res.statusCode).toBe(404);
});
it('GET /payments/:userId lists payments', async () => {
const payment = { id: 'pay_1', amount: 999, currency: 'usd', status: 'succeeded' };
repoMock.getPaymentsByUser.mockResolvedValue([payment]);
const { subscriptionRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(subscriptionRoutes, { prefix: '/api' });
const res = await app.inject({ method: 'GET', url: '/api/payments/user_1' });
expect(res.statusCode).toBe(200);
const data = JSON.parse(res.body);
expect(data.payments).toHaveLength(1);
});
it('POST /payments records a payment', async () => {
const payment = { id: 'pay_1', amount: 999, currency: 'usd', status: 'succeeded' };
repoMock.createPayment.mockResolvedValue(payment);
const { subscriptionRoutes } = await import('./routes.js');
const app = Fastify({ logger: false });
await app.register(subscriptionRoutes, { prefix: '/api' });
const res = await app.inject({
method: 'POST',
url: '/api/payments',
payload: {
userId: 'user_1',
amount: 999,
currency: 'usd',
status: 'succeeded',
description: 'Pro plan',
method: 'card',
},
});
expect(res.statusCode).toBe(201);
});
});