/** * Test Helpers * Utility functions for setting up test data */ const db = require('../../database/connection'); const jwt = require('jsonwebtoken'); const bcrypt = require('bcrypt'); /** * Create a test user with authentication token * @param {string} email - User email * @param {string} username - Username * @param {string} password - Password (optional, defaults to 'testpassword') * @returns {Promise} User and token */ async function createTestUser(email, username, password = 'testpassword') { try { // Hash password const hashedPassword = await bcrypt.hash(password, 10); // Create user const [user] = await db('players').insert({ email, username, password_hash: hashedPassword, email_verified: true, user_group: Math.floor(Math.random() * 10), is_active: true, created_at: new Date(), updated_at: new Date(), }).returning('*'); // Generate JWT token const token = jwt.sign( { id: user.id, username: user.username }, process.env.JWT_SECRET || 'test-secret', { expiresIn: '24h' }, ); // Initialize player resources const resourceTypes = await db('resource_types').where('is_active', true); for (const resourceType of resourceTypes) { await db('player_resources').insert({ player_id: user.id, resource_type_id: resourceType.id, amount: 10000, // Generous amount for testing storage_capacity: 50000, last_updated: new Date(), }); } // Initialize combat statistics await db('combat_statistics').insert({ player_id: user.id, battles_initiated: 0, battles_won: 0, battles_lost: 0, ships_lost: 0, ships_destroyed: 0, total_damage_dealt: 0, total_damage_received: 0, total_experience_gained: 0, resources_looted: JSON.stringify({}), created_at: new Date(), updated_at: new Date(), }); return { user, token }; } catch (error) { console.error('Failed to create test user:', error); throw error; } } /** * Create a test fleet with ships * @param {number} playerId - Owner player ID * @param {string} name - Fleet name * @param {string} location - Fleet location * @returns {Promise} Created fleet */ async function createTestFleet(playerId, name, location) { try { // Get or create a basic ship design let shipDesign = await db('ship_designs') .where('name', 'Test Fighter') .where('is_public', true) .first(); if (!shipDesign) { [shipDesign] = await db('ship_designs').insert({ name: 'Test Fighter', ship_class: 'fighter', hull_type: 'light', components: JSON.stringify({ weapons: ['basic_laser'], shields: ['basic_shield'], engines: ['basic_engine'], }), stats: JSON.stringify({ hp: 100, attack: 15, defense: 10, speed: 5, }), cost: JSON.stringify({ scrap: 100, energy: 50, }), build_time: 30, is_public: true, is_active: true, hull_points: 100, shield_points: 25, armor_points: 10, attack_power: 15, attack_speed: 1.0, movement_speed: 5, cargo_capacity: 0, special_abilities: JSON.stringify([]), damage_resistances: JSON.stringify({}), created_at: new Date(), updated_at: new Date(), }).returning('*'); } // Create fleet const [fleet] = await db('fleets').insert({ player_id: playerId, name, current_location: location, destination: null, fleet_status: 'idle', combat_rating: 150, total_ship_count: 10, fleet_composition: JSON.stringify({ 'Test Fighter': 10, }), combat_victories: 0, combat_defeats: 0, movement_started: null, arrival_time: null, last_updated: new Date(), created_at: new Date(), }).returning('*'); // Add ships to fleet await db('fleet_ships').insert({ fleet_id: fleet.id, ship_design_id: shipDesign.id, quantity: 10, health_percentage: 100, experience: 0, created_at: new Date(), }); // Create initial combat experience record await db('ship_combat_experience').insert({ fleet_id: fleet.id, ship_design_id: shipDesign.id, battles_survived: 0, enemies_destroyed: 0, damage_dealt: 0, experience_points: 0, veterancy_level: 1, combat_bonuses: JSON.stringify({}), created_at: new Date(), updated_at: new Date(), }); return fleet; } catch (error) { console.error('Failed to create test fleet:', error); throw error; } } /** * Create a test colony with basic buildings * @param {number} playerId - Owner player ID * @param {string} name - Colony name * @param {string} coordinates - Colony coordinates * @param {number} planetTypeId - Planet type ID (optional) * @returns {Promise} Created colony */ async function createTestColony(playerId, name, coordinates, planetTypeId = null) { try { // Get planet type if (!planetTypeId) { const planetType = await db('planet_types') .where('is_active', true) .first(); planetTypeId = planetType?.id || 1; } // Get sector const sectorCoordinates = coordinates.split('-').slice(0, 2).join('-'); let sector = await db('galaxy_sectors') .where('coordinates', sectorCoordinates) .first(); if (!sector) { [sector] = await db('galaxy_sectors').insert({ name: `Test Sector ${sectorCoordinates}`, coordinates: sectorCoordinates, description: 'Test sector for integration tests', danger_level: 3, special_rules: JSON.stringify({}), created_at: new Date(), }).returning('*'); } // Create colony const [colony] = await db('colonies').insert({ player_id: playerId, name, coordinates, sector_id: sector.id, planet_type_id: planetTypeId, population: 1000, max_population: 10000, morale: 100, loyalty: 100, defense_rating: 50, shield_strength: 25, under_siege: false, successful_defenses: 0, times_captured: 0, founded_at: new Date(), last_updated: new Date(), }).returning('*'); // Add basic buildings const commandCenter = await db('building_types') .where('name', 'Command Center') .first(); if (commandCenter) { await db('colony_buildings').insert({ colony_id: colony.id, building_type_id: commandCenter.id, level: 1, health_percentage: 100, is_under_construction: false, created_at: new Date(), updated_at: new Date(), }); } // Add defense grid for combat testing const defenseGrid = await db('building_types') .where('name', 'Defense Grid') .first(); if (defenseGrid) { await db('colony_buildings').insert({ colony_id: colony.id, building_type_id: defenseGrid.id, level: 2, health_percentage: 100, is_under_construction: false, created_at: new Date(), updated_at: new Date(), }); } // Initialize colony resource production const resourceTypes = await db('resource_types').where('is_active', true); for (const resourceType of resourceTypes) { await db('colony_resource_production').insert({ colony_id: colony.id, resource_type_id: resourceType.id, production_rate: 10, consumption_rate: 5, current_stored: 1000, storage_capacity: 10000, last_calculated: new Date(), }); } return colony; } catch (error) { console.error('Failed to create test colony:', error); throw error; } } /** * Create test combat configuration * @param {string} name - Configuration name * @param {string} type - Combat type * @param {Object} config - Configuration data * @returns {Promise} Created configuration */ async function createTestCombatConfig(name, type, config = {}) { try { const [combatConfig] = await db('combat_configurations').insert({ config_name: name, combat_type: type, config_data: JSON.stringify({ auto_resolve: true, preparation_time: 1, max_rounds: 5, round_duration: 2, damage_variance: 0.1, experience_gain: 1.0, casualty_rate_min: 0.1, casualty_rate_max: 0.7, loot_multiplier: 1.0, spectator_limit: 100, priority: 100, ...config, }), description: `Test ${type} combat configuration`, is_active: true, created_at: new Date(), updated_at: new Date(), }).returning('*'); return combatConfig; } catch (error) { console.error('Failed to create test combat config:', error); throw error; } } /** * Create a completed combat encounter for testing history * @param {number} attackerFleetId - Attacker fleet ID * @param {number} defenderFleetId - Defender fleet ID (optional) * @param {number} defenderColonyId - Defender colony ID (optional) * @param {string} outcome - Combat outcome * @returns {Promise} Created encounter */ async function createTestCombatEncounter(attackerFleetId, defenderFleetId = null, defenderColonyId = null, outcome = 'attacker_victory') { try { // Create battle const [battle] = await db('battles').insert({ battle_type: defenderColonyId ? 'fleet_vs_colony' : 'fleet_vs_fleet', location: 'A3-91-X', combat_type_id: 1, participants: JSON.stringify({ attacker_fleet_id: attackerFleetId, defender_fleet_id: defenderFleetId, defender_colony_id: defenderColonyId, }), status: 'completed', battle_data: JSON.stringify({}), result: JSON.stringify({ outcome }), started_at: new Date(Date.now() - 300000), // 5 minutes ago completed_at: new Date(), created_at: new Date(), }).returning('*'); // Create encounter const [encounter] = await db('combat_encounters').insert({ battle_id: battle.id, attacker_fleet_id: attackerFleetId, defender_fleet_id: defenderFleetId, defender_colony_id: defenderColonyId, encounter_type: battle.battle_type, location: battle.location, initial_forces: JSON.stringify({ attacker: { ships: 10 }, defender: { ships: 8 }, }), final_forces: JSON.stringify({ attacker: { ships: outcome === 'attacker_victory' ? 7 : 2 }, defender: { ships: outcome === 'defender_victory' ? 6 : 0 }, }), casualties: JSON.stringify({ attacker: { ships: {}, total_ships: outcome === 'attacker_victory' ? 3 : 8 }, defender: { ships: {}, total_ships: outcome === 'defender_victory' ? 2 : 8 }, }), combat_log: JSON.stringify([ { round: 1, event: 'combat_start', description: 'Combat initiated' }, { round: 1, event: 'combat_resolution', description: `${outcome.replace('_', ' ')}` }, ]), experience_gained: 100, loot_awarded: JSON.stringify(outcome === 'attacker_victory' ? { scrap: 500, energy: 250 } : {}), outcome, duration_seconds: 90, started_at: battle.started_at, completed_at: battle.completed_at, created_at: new Date(), }).returning('*'); return { battle, encounter }; } catch (error) { console.error('Failed to create test combat encounter:', error); throw error; } } /** * Wait for a condition to be met * @param {Function} condition - Function that returns true when condition is met * @param {number} timeout - Timeout in milliseconds * @param {number} interval - Check interval in milliseconds * @returns {Promise} */ async function waitForCondition(condition, timeout = 5000, interval = 100) { const start = Date.now(); while (Date.now() - start < timeout) { if (await condition()) { return; } await new Promise(resolve => setTimeout(resolve, interval)); } throw new Error(`Condition not met within ${timeout}ms`); } /** * Clean up all test data */ async function cleanupTestData() { try { // Delete in order to respect foreign key constraints await db('combat_logs').del(); await db('combat_encounters').del(); await db('combat_queue').del(); await db('battles').del(); await db('ship_combat_experience').del(); await db('fleet_ships').del(); await db('fleet_positions').del(); await db('fleets').del(); await db('colony_resource_production').del(); await db('colony_buildings').del(); await db('colonies').del(); await db('player_resources').del(); await db('combat_statistics').del(); await db('players').where('email', 'like', '%@test.com').del(); await db('combat_configurations').where('config_name', 'like', 'test_%').del(); await db('ship_designs').where('name', 'like', 'Test %').del(); await db('galaxy_sectors').where('name', 'like', 'Test Sector%').del(); } catch (error) { console.error('Failed to cleanup test data:', error); } } /** * Reset combat-related data between tests */ async function resetCombatData() { try { await db('combat_logs').del(); await db('combat_encounters').del(); await db('combat_queue').del(); await db('battles').del(); // Reset fleet statuses await db('fleets').update({ fleet_status: 'idle', last_combat: null, }); // Reset colony siege status await db('colonies').update({ under_siege: false, last_attacked: null, }); } catch (error) { console.error('Failed to reset combat data:', error); } } module.exports = { createTestUser, createTestFleet, createTestColony, createTestCombatConfig, createTestCombatEncounter, waitForCondition, cleanupTestData, resetCombatData, };