Files
vyndr/tests/integration/session28Routes.test.js
T

107 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Integration: parlay / lines / books routes (Session 28).
const express = require('express');
const request = require('supertest');
// Redis-backed services are mocked at the redis layer.
const mockStore = {};
const mockScan = jest.fn(async () => ['0', []]);
jest.mock('../../src/utils/redis', () => ({
cacheGet: jest.fn(async (k) => (k in mockStore ? mockStore[k] : null)),
getRedisClient: () => ({ scan: mockScan, lrange: async () => [], rpush: async () => 1, ltrim: async () => 'OK', expire: async () => 1 }),
isDegraded: () => false,
}));
function mount(routePath, file) {
delete require.cache[require.resolve(file)];
const app = express();
app.use(express.json());
app.use(routePath, require(file));
return app;
}
beforeEach(() => {
for (const k of Object.keys(mockStore)) delete mockStore[k];
jest.clearAllMocks();
});
describe('POST /api/parlay/calculate', () => {
const app = () => mount('/api/parlay', '../../src/routes/parlay');
test('returns combined odds + grade for valid legs', async () => {
const res = await request(app()).post('/api/parlay/calculate').send({
legs: [
{ player: 'A', stat: 'points', odds: 100, grade: 'A', gameId: 'g1' },
{ player: 'B', stat: 'hits', odds: 100, grade: 'A', gameId: 'g2' },
],
});
expect(res.status).toBe(200);
expect(res.body.combinedOdds).toBe(300); // 2×2 = 4.0 → +300
expect(res.body.combinedGrade).toBeDefined();
});
test('empty legs → 400', async () => {
const res = await request(app()).post('/api/parlay/calculate').send({ legs: [] });
expect(res.status).toBe(400);
});
test('suggestions endpoint returns combos', async () => {
const props = [
{ player: 'A', stat: 'points', odds: -110, grade: 'A', gameId: 'g1' },
{ player: 'B', stat: 'hits', odds: -110, grade: 'A', gameId: 'g2' },
{ player: 'C', stat: 'goals', odds: -110, grade: 'B', gameId: 'g3' },
];
const res = await request(app()).post('/api/parlay/suggestions').send({ props, legs: 3, max: 1 });
expect(res.status).toBe(200);
expect(res.body.suggestions).toHaveLength(1);
});
});
describe('GET /api/lines/:sport/movers', () => {
const app = () => mount('/api/lines', '../../src/routes/lineMovement');
test('empty when no snapshots cached', async () => {
const res = await request(app()).get('/api/lines/mlb/movers');
expect(res.status).toBe(200);
expect(res.body.movers).toEqual([]);
});
test('unsupported sport → 404', async () => {
const res = await request(app()).get('/api/lines/cricket/movers');
expect(res.status).toBe(404);
});
});
describe('GET /api/books/:sport', () => {
const app = () => mount('/api/books', '../../src/routes/bookComparison');
test('returns best lines from cached props', async () => {
mockStore[`odds:nba:${new Date().toISOString().split('T')[0]}`] = {
props: [
{
player: 'Wemby', stat_type: 'points',
lines: [
{ book: 'dk', over_odds: -110 },
{ book: 'fd', over_odds: -105 },
],
},
],
};
const res = await request(app()).get('/api/books/nba');
expect(res.status).toBe(200);
expect(res.body.bestLines).toHaveLength(1);
expect(res.body.bestLines[0].bestBook).toBe('fd');
});
test('empty when no cached props', async () => {
const res = await request(app()).get('/api/books/nba');
expect(res.status).toBe(200);
expect(res.body.bestLines).toEqual([]);
});
test('unsupported sport → 404', async () => {
const res = await request(app()).get('/api/books/cricket');
expect(res.status).toBe(404);
});
});