Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 1m17s
170 lines
6.3 KiB
TypeScript
170 lines
6.3 KiB
TypeScript
// src/routes/price.routes.test.ts
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import supertest from 'supertest';
|
|
import type { Request, Response, NextFunction } from 'express';
|
|
import { createTestApp } from '../tests/utils/createTestApp';
|
|
import { mockLogger } from '../tests/utils/mockLogger';
|
|
import { createMockUserProfile } from '../tests/utils/mockFactories';
|
|
|
|
// Mock the price repository
|
|
vi.mock('../services/db/price.db', () => ({
|
|
priceRepo: {
|
|
getPriceHistory: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
// Mock the logger to keep test output clean
|
|
vi.mock('../services/logger.server', async () => ({
|
|
// Use async import to avoid hoisting issues with mockLogger
|
|
logger: (await import('../tests/utils/mockLogger')).mockLogger,
|
|
}));
|
|
|
|
// Mock the passport middleware
|
|
vi.mock('../config/passport', () => ({
|
|
default: {
|
|
authenticate: vi.fn(() => (req: Request, res: Response, next: NextFunction) => {
|
|
// If req.user is not set by the test setup, simulate unauthenticated access.
|
|
if (!req.user) {
|
|
return res.status(401).json({ message: 'Unauthorized' });
|
|
}
|
|
// If req.user is set, proceed as an authenticated user.
|
|
next();
|
|
}),
|
|
},
|
|
}));
|
|
|
|
// Import the router AFTER other setup.
|
|
import priceRouter from './price.routes';
|
|
import { priceRepo } from '../services/db/price.db';
|
|
|
|
describe('Price Routes (/api/price-history)', () => {
|
|
const mockUser = createMockUserProfile({ user: { user_id: 'price-user-123' } });
|
|
const app = createTestApp({
|
|
router: priceRouter,
|
|
basePath: '/api/price-history',
|
|
authenticatedUser: mockUser,
|
|
});
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('POST /', () => {
|
|
it('should return 200 OK with price history data for a valid request', async () => {
|
|
const mockHistory = [
|
|
{ master_item_id: 1, price_in_cents: 199, date: '2024-01-01T00:00:00.000Z' },
|
|
{ master_item_id: 2, price_in_cents: 299, date: '2024-01-08T00:00:00.000Z' },
|
|
];
|
|
vi.mocked(priceRepo.getPriceHistory).mockResolvedValue(mockHistory);
|
|
|
|
const response = await supertest(app)
|
|
.post('/api/price-history')
|
|
.send({ masterItemIds: [1, 2] });
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.data).toEqual(mockHistory);
|
|
expect(priceRepo.getPriceHistory).toHaveBeenCalledWith([1, 2], expect.any(Object), 1000, 0);
|
|
});
|
|
|
|
it('should pass limit and offset from the body to the repository', async () => {
|
|
vi.mocked(priceRepo.getPriceHistory).mockResolvedValue([]);
|
|
await supertest(app)
|
|
.post('/api/price-history')
|
|
.send({ masterItemIds: [1, 2, 3], limit: 50, offset: 10 });
|
|
|
|
expect(priceRepo.getPriceHistory).toHaveBeenCalledWith([1, 2, 3], expect.any(Object), 50, 10);
|
|
});
|
|
|
|
it('should log the request info', async () => {
|
|
vi.mocked(priceRepo.getPriceHistory).mockResolvedValue([]);
|
|
await supertest(app)
|
|
.post('/api/price-history')
|
|
.send({ masterItemIds: [1, 2, 3], limit: 25, offset: 5 });
|
|
|
|
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
{ itemCount: 3, limit: 25, offset: 5 },
|
|
'[API /price-history] Received request for historical price data.',
|
|
);
|
|
});
|
|
|
|
it('should return 500 if the database call fails', async () => {
|
|
const dbError = new Error('Database connection failed');
|
|
vi.mocked(priceRepo.getPriceHistory).mockRejectedValue(dbError);
|
|
|
|
const response = await supertest(app)
|
|
.post('/api/price-history')
|
|
.send({ masterItemIds: [1, 2, 3] });
|
|
|
|
expect(response.status).toBe(500);
|
|
expect(response.body.error.message).toBe('Database connection failed');
|
|
});
|
|
|
|
it('should return 400 if masterItemIds is an empty array', async () => {
|
|
const response = await supertest(app).post('/api/price-history').send({ masterItemIds: [] });
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error.details[0].message).toBe(
|
|
'masterItemIds must be a non-empty array of positive integers.',
|
|
);
|
|
});
|
|
|
|
it('should return 400 if masterItemIds is not an array', async () => {
|
|
const response = await supertest(app)
|
|
.post('/api/price-history')
|
|
.send({ masterItemIds: 'not-an-array' });
|
|
|
|
expect(response.status).toBe(400);
|
|
// The actual message is "Invalid input: expected array, received string"
|
|
expect(response.body.error.details[0].message).toBe(
|
|
'Invalid input: expected array, received string',
|
|
);
|
|
});
|
|
|
|
it('should return 400 if masterItemIds contains non-positive integers', async () => {
|
|
const response = await supertest(app)
|
|
.post('/api/price-history')
|
|
.send({ masterItemIds: [1, -2, 3] });
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error.details[0].message).toBe('Number must be greater than 0');
|
|
});
|
|
|
|
it('should return 400 if masterItemIds is missing', async () => {
|
|
const response = await supertest(app).post('/api/price-history').send({});
|
|
|
|
expect(response.status).toBe(400);
|
|
// The actual message is "Invalid input: expected array, received undefined"
|
|
expect(response.body.error.details[0].message).toBe(
|
|
'Invalid input: expected array, received undefined',
|
|
);
|
|
});
|
|
|
|
it('should return 400 for invalid limit and offset', async () => {
|
|
const response = await supertest(app)
|
|
.post('/api/price-history')
|
|
.send({ masterItemIds: [1], limit: -1, offset: 'abc' });
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error.details).toHaveLength(2);
|
|
// The actual message is "Too small: expected number to be >0"
|
|
expect(response.body.error.details[0].message).toBe('Too small: expected number to be >0');
|
|
expect(response.body.error.details[1].message).toBe(
|
|
'Invalid input: expected number, received NaN',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('Rate Limiting', () => {
|
|
it('should apply priceHistoryLimiter to POST /', async () => {
|
|
vi.mocked(priceRepo.getPriceHistory).mockResolvedValue([]);
|
|
const response = await supertest(app)
|
|
.post('/api/price-history')
|
|
.set('X-Test-Rate-Limit-Enable', 'true')
|
|
.send({ masterItemIds: [1, 2] });
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(50);
|
|
});
|
|
});
|
|
});
|