// Integration: /api/internal/prefetch/tank01 (Session 18). // // Verifies (a) the endpoint is gated by the shared internal key, // (b) the request body is translated into the prefetch module's // argv shape correctly, and (c) the module's summary is returned // verbatim. The prefetch module itself is mocked — it has its own // unit coverage in tests/unit/tank01Prefetch.test.js. const express = require('express'); const request = require('supertest'); jest.mock('../../scripts/tank01-prefetch', () => ({ main: jest.fn(), })); const tank01Prefetch = require('../../scripts/tank01-prefetch'); beforeEach(() => { jest.resetAllMocks(); process.env.VYNDR_INTERNAL_KEY = 'test-internal-key-9999'; }); function mountApp() { // Reset the require cache so the route picks up the mocked // tank01-prefetch module fresh between tests. delete require.cache[require.resolve('../../src/routes/internal')]; const internalRoutes = require('../../src/routes/internal'); const app = express(); app.use(express.json()); app.use('/api/internal', internalRoutes); return app; } describe('POST /api/internal/prefetch/tank01', () => { test('rejects requests without the internal key', async () => { const app = mountApp(); const res = await request(app) .post('/api/internal/prefetch/tank01') .send({ dryRun: true }); expect(res.status).toBe(401); expect(tank01Prefetch.main).not.toHaveBeenCalled(); }); test('translates body into argv and returns the summary', async () => { const fakeSummary = { nba: { games: 3, boxscores: 8, odds: true, skipped: null }, mlb: { games: 5, boxscores: 12, bvp: 0, skipped: null, bvp_skipped_reason: null }, requestsSpent: 4, dryRun: false, }; tank01Prefetch.main.mockResolvedValueOnce(fakeSummary); const app = mountApp(); const res = await request(app) .post('/api/internal/prefetch/tank01') .set('x-internal-key', 'test-internal-key-9999') .send({ max: 25, sports: ['nba', 'mlb'], dryRun: false }); expect(res.status).toBe(200); expect(res.body).toEqual({ ok: true, summary: fakeSummary }); expect(tank01Prefetch.main).toHaveBeenCalledTimes(1); const argv = tank01Prefetch.main.mock.calls[0][0]; expect(argv).toContain('--max=25'); expect(argv).toContain('--sports=nba,mlb'); expect(argv).not.toContain('--dry-run'); }); test('forwards --dry-run when requested', async () => { tank01Prefetch.main.mockResolvedValueOnce({ dryRun: true }); const app = mountApp(); const res = await request(app) .post('/api/internal/prefetch/tank01') .set('x-internal-key', 'test-internal-key-9999') .send({ dryRun: true }); expect(res.status).toBe(200); const argv = tank01Prefetch.main.mock.calls[0][0]; expect(argv).toContain('--dry-run'); }); test('returns 500 with the underlying error when the module rejects', async () => { tank01Prefetch.main.mockRejectedValueOnce(new Error('budget exhausted')); const app = mountApp(); const res = await request(app) .post('/api/internal/prefetch/tank01') .set('x-internal-key', 'test-internal-key-9999') .send({}); expect(res.status).toBe(500); expect(res.body).toEqual({ ok: false, error: 'budget exhausted' }); }); test('accepts string sports (single sport) and forwards it', async () => { tank01Prefetch.main.mockResolvedValueOnce({ ok: true }); const app = mountApp(); await request(app) .post('/api/internal/prefetch/tank01') .set('x-internal-key', 'test-internal-key-9999') .send({ sports: 'mlb' }); const argv = tank01Prefetch.main.mock.calls[0][0]; expect(argv).toContain('--sports=mlb'); }); });