322 lines
11 KiB
TypeScript
322 lines
11 KiB
TypeScript
/**
|
|
* 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);
|
|
});
|
|
});
|
|
});
|