/** * Integration Test: IntegrationTestHarness * * Tests the IntegrationTestHarness infrastructure for orchestrating integration tests * - Validates setup and teardown hooks * - Tests database transaction management * - Verifies constraint violation detection * * Focus: Infrastructure testing, NOT business logic */ import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest'; import { IntegrationTestHarness, createTestHarness, DEFAULT_TEST_CONFIG } from './index'; import { DatabaseManager } from './database-manager'; import { ApiClient } from './api-client'; describe('IntegrationTestHarness - Infrastructure Tests', () => { let harness: IntegrationTestHarness; beforeAll(() => { // Create a test harness with default configuration harness = createTestHarness(); }); describe('Construction', () => { it('should be constructed with configuration', () => { // Given: Configuration // When: Creating an IntegrationTestHarness instance const testHarness = new IntegrationTestHarness(DEFAULT_TEST_CONFIG); // Then: The instance should be created successfully expect(testHarness).toBeInstanceOf(IntegrationTestHarness); }); it('should accept partial configuration', () => { // Given: Partial configuration const partialConfig = { api: { baseUrl: 'http://localhost:3000', }, }; // When: Creating an IntegrationTestHarness with partial config const testHarness = createTestHarness(partialConfig); // Then: The instance should be created successfully expect(testHarness).toBeInstanceOf(IntegrationTestHarness); }); it('should merge default configuration with custom configuration', () => { // Given: Custom configuration const customConfig = { api: { baseUrl: 'http://localhost:8080', port: 8080, }, timeouts: { setup: 60000, }, }; // When: Creating an IntegrationTestHarness with custom config const testHarness = createTestHarness(customConfig); // Then: The configuration should be merged correctly expect(testHarness).toBeInstanceOf(IntegrationTestHarness); }); }); describe('Accessors', () => { it('should provide access to database manager', () => { // Given: An IntegrationTestHarness instance // When: Getting the database manager const database = harness.getDatabase(); // Then: The database manager should be returned expect(database).toBeInstanceOf(DatabaseManager); }); it('should provide access to API client', () => { // Given: An IntegrationTestHarness instance // When: Getting the API client const api = harness.getApi(); // Then: The API client should be returned expect(api).toBeInstanceOf(ApiClient); }); it('should provide access to Docker manager', () => { // Given: An IntegrationTestHarness instance // When: Getting the Docker manager const docker = harness.getDocker(); // Then: The Docker manager should be returned expect(docker).toBeDefined(); expect(docker).toHaveProperty('start'); expect(docker).toHaveProperty('stop'); }); it('should provide access to data factory', () => { // Given: An IntegrationTestHarness instance // When: Getting the data factory const factory = harness.getFactory(); // Then: The data factory should be returned expect(factory).toBeDefined(); expect(factory).toHaveProperty('createLeague'); expect(factory).toHaveProperty('createSeason'); expect(factory).toHaveProperty('createDriver'); }); }); describe('Setup Hooks', () => { it('should have beforeAll hook', () => { // Given: An IntegrationTestHarness instance // When: Checking for beforeAll hook // Then: The hook should exist expect(harness.beforeAll).toBeDefined(); expect(typeof harness.beforeAll).toBe('function'); }); it('should have beforeEach hook', () => { // Given: An IntegrationTestHarness instance // When: Checking for beforeEach hook // Then: The hook should exist expect(harness.beforeEach).toBeDefined(); expect(typeof harness.beforeEach).toBe('function'); }); }); describe('Teardown Hooks', () => { it('should have afterAll hook', () => { // Given: An IntegrationTestHarness instance // When: Checking for afterAll hook // Then: The hook should exist expect(harness.afterAll).toBeDefined(); expect(typeof harness.afterAll).toBe('function'); }); it('should have afterEach hook', () => { // Given: An IntegrationTestHarness instance // When: Checking for afterEach hook // Then: The hook should exist expect(harness.afterEach).toBeDefined(); expect(typeof harness.afterEach).toBe('function'); }); }); describe('Transaction Management', () => { it('should have withTransaction method', () => { // Given: An IntegrationTestHarness instance // When: Checking for withTransaction method // Then: The method should exist expect(harness.withTransaction).toBeDefined(); expect(typeof harness.withTransaction).toBe('function'); }); it('should execute callback within transaction', async () => { // Given: An IntegrationTestHarness instance // When: Executing withTransaction const result = await harness.withTransaction(async (db) => { // Execute a simple query const queryResult = await db.query('SELECT 1 as test_value'); return queryResult.rows[0].test_value; }); // Then: The callback should execute and return the result expect(result).toBe(1); }); it('should rollback transaction after callback', async () => { // Given: An IntegrationTestHarness instance // When: Executing withTransaction await harness.withTransaction(async (db) => { // Execute a query await db.query('SELECT 1 as test_value'); // The transaction should be rolled back after this }); // Then: The transaction should be rolled back // (This is verified by the fact that no error is thrown) expect(true).toBe(true); }); }); describe('Constraint Violation Detection', () => { it('should have expectConstraintViolation method', () => { // Given: An IntegrationTestHarness instance // When: Checking for expectConstraintViolation method // Then: The method should exist expect(harness.expectConstraintViolation).toBeDefined(); expect(typeof harness.expectConstraintViolation).toBe('function'); }); it('should detect constraint violations', async () => { // Given: An IntegrationTestHarness instance // When: Executing an operation that violates a constraint // Then: Should throw an error await expect( harness.expectConstraintViolation(async () => { // This operation should violate a constraint throw new Error('constraint violation: duplicate key'); }) ).rejects.toThrow('Expected constraint violation but operation succeeded'); }); it('should detect specific constraint violations', async () => { // Given: An IntegrationTestHarness instance // When: Executing an operation that violates a specific constraint // Then: Should throw an error with the expected constraint await expect( harness.expectConstraintViolation( async () => { // This operation should violate a specific constraint throw new Error('constraint violation: unique_violation'); }, 'unique_violation' ) ).rejects.toThrow('Expected constraint violation but operation succeeded'); }); it('should detect non-constraint errors', async () => { // Given: An IntegrationTestHarness instance // When: Executing an operation that throws a non-constraint error // Then: Should throw an error await expect( harness.expectConstraintViolation(async () => { // This operation should throw a non-constraint error throw new Error('Some other error'); }) ).rejects.toThrow('Expected constraint violation but got: Some other error'); }); }); describe('Configuration', () => { it('should use default configuration', () => { // Given: Default configuration // When: Creating a harness with default config const testHarness = createTestHarness(); // Then: The configuration should match defaults expect(testHarness).toBeInstanceOf(IntegrationTestHarness); }); it('should accept custom configuration', () => { // Given: Custom configuration const customConfig = { api: { baseUrl: 'http://localhost:9000', port: 9000, }, database: { host: 'custom-host', port: 5434, database: 'custom_db', user: 'custom_user', password: 'custom_pass', }, timeouts: { setup: 30000, teardown: 15000, test: 30000, }, }; // When: Creating a harness with custom config const testHarness = createTestHarness(customConfig); // Then: The configuration should be applied expect(testHarness).toBeInstanceOf(IntegrationTestHarness); }); it('should merge configuration correctly', () => { // Given: Partial configuration const partialConfig = { api: { baseUrl: 'http://localhost:8080', }, timeouts: { setup: 60000, }, }; // When: Creating a harness with partial config const testHarness = createTestHarness(partialConfig); // Then: The configuration should be merged with defaults expect(testHarness).toBeInstanceOf(IntegrationTestHarness); }); }); describe('Default Configuration', () => { it('should have correct default API configuration', () => { // Given: Default configuration // When: Checking default API configuration // Then: Should match expected defaults expect(DEFAULT_TEST_CONFIG.api.baseUrl).toBe('http://localhost:3101'); expect(DEFAULT_TEST_CONFIG.api.port).toBe(3101); }); it('should have correct default database configuration', () => { // Given: Default configuration // When: Checking default database configuration // Then: Should match expected defaults expect(DEFAULT_TEST_CONFIG.database.host).toBe('localhost'); expect(DEFAULT_TEST_CONFIG.database.port).toBe(5433); expect(DEFAULT_TEST_CONFIG.database.database).toBe('gridpilot_test'); expect(DEFAULT_TEST_CONFIG.database.user).toBe('gridpilot_test_user'); expect(DEFAULT_TEST_CONFIG.database.password).toBe('gridpilot_test_pass'); }); it('should have correct default timeouts', () => { // Given: Default configuration // When: Checking default timeouts // Then: Should match expected defaults expect(DEFAULT_TEST_CONFIG.timeouts.setup).toBe(120000); expect(DEFAULT_TEST_CONFIG.timeouts.teardown).toBe(30000); expect(DEFAULT_TEST_CONFIG.timeouts.test).toBe(60000); }); }); });