import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import * as http from 'http'; import * as path from 'path'; import { FixtureServerService } from '@/packages/infrastructure/adapters/automation/FixtureServerService'; describe('FixtureServerService', () => { let service: FixtureServerService; let testPort: number; const fixturesPath = './resources/iracing-hosted-sessions'; beforeEach(() => { service = new FixtureServerService(); testPort = 13400 + Math.floor(Math.random() * 100); }); afterEach(async () => { if (service.isRunning()) { await service.stop(); } await new Promise((resolve) => setTimeout(resolve, 50)); }); describe('start', () => { it('should start the server on specified port', async () => { await service.start(testPort, fixturesPath); expect(service.isRunning()).toBe(true); expect(service.getBaseUrl()).toBe(`http://localhost:${testPort}`); }); it('should throw error if server is already running', async () => { await service.start(testPort, fixturesPath); await expect(service.start(testPort, fixturesPath)).rejects.toThrow( 'Fixture server is already running' ); }); it('should throw error if fixtures path does not exist', async () => { await expect(service.start(testPort, './non-existent-path')).rejects.toThrow( /Fixtures path does not exist/ ); }); }); describe('stop', () => { it('should stop a running server', async () => { await service.start(testPort, fixturesPath); expect(service.isRunning()).toBe(true); await service.stop(); expect(service.isRunning()).toBe(false); }); it('should resolve when server is not running', async () => { expect(service.isRunning()).toBe(false); await expect(service.stop()).resolves.toBeUndefined(); }); }); describe('waitForReady', () => { it('should return true when server is ready', async () => { await service.start(testPort, fixturesPath); const isReady = await service.waitForReady(5000); expect(isReady).toBe(true); }); it('should return false when server is not running', async () => { const isReady = await service.waitForReady(500); expect(isReady).toBe(false); }); }); describe('getBaseUrl', () => { it('should return correct base URL with default port', () => { expect(service.getBaseUrl()).toBe('http://localhost:3456'); }); it('should return correct base URL after starting on custom port', async () => { await service.start(testPort, fixturesPath); expect(service.getBaseUrl()).toBe(`http://localhost:${testPort}`); }); }); describe('isRunning', () => { it('should return false when server is not started', () => { expect(service.isRunning()).toBe(false); }); it('should return true when server is running', async () => { await service.start(testPort, fixturesPath); expect(service.isRunning()).toBe(true); }); }); describe('HTTP serving', () => { it('should serve HTML files from fixtures path', async () => { await service.start(testPort, fixturesPath); const response = await makeRequest(`http://localhost:${testPort}/01-hosted-racing.html`); expect(response.statusCode).toBe(200); expect(response.headers['content-type']).toBe('text/html'); expect(response.body).toContain(' { await service.start(testPort, fixturesPath); const response = await makeRequest(`http://localhost:${testPort}/health`); expect(response.statusCode).toBe(200); expect(response.headers['content-type']).toBe('application/json'); expect(JSON.parse(response.body)).toEqual({ status: 'ok' }); }); it('should return 404 for non-existent files', async () => { await service.start(testPort, fixturesPath); const response = await makeRequest(`http://localhost:${testPort}/non-existent.html`); expect(response.statusCode).toBe(404); }); it('should include CORS headers', async () => { await service.start(testPort, fixturesPath); const response = await makeRequest(`http://localhost:${testPort}/health`); expect(response.headers['access-control-allow-origin']).toBe('*'); }); it('should return 404 for path traversal attempts', async () => { await service.start(testPort, fixturesPath); const response = await makeRequest(`http://localhost:${testPort}/../package.json`); expect([403, 404]).toContain(response.statusCode); }); }); }); interface HttpResponse { statusCode: number; headers: Record; body: string; } function makeRequest(url: string): Promise { return new Promise((resolve, reject) => { http.get(url, (res) => { let body = ''; res.on('data', (chunk) => { body += chunk; }); res.on('end', () => { resolve({ statusCode: res.statusCode || 0, headers: res.headers as Record, body, }); }); }).on('error', reject); }); }