174 lines
5.2 KiB
TypeScript
174 lines
5.2 KiB
TypeScript
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('<!DOCTYPE html');
|
|
});
|
|
|
|
it('should serve health endpoint', async () => {
|
|
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<string, string>;
|
|
body: string;
|
|
}
|
|
|
|
function makeRequest(url: string): Promise<HttpResponse> {
|
|
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<string, string>,
|
|
body,
|
|
});
|
|
});
|
|
}).on('error', reject);
|
|
});
|
|
} |