initializeCodeIgniter(); // Set up database connection $this->setupDatabase(); } public function tearDown(): void { // Clean up any resources parent::tearDown(); } /** * Initialize CodeIgniter for testing */ protected function initializeCodeIgniter() { // Mock CodeIgniter instance for testing $this->ci = new stdClass(); // Mock database object $this->ci->db = $this->createDatabaseMock(); // Set global CI instance if (!function_exists('get_instance')) { function get_instance() { return $GLOBALS['CI_INSTANCE']; } } $GLOBALS['CI_INSTANCE'] = $this->ci; } /** * Create database mock for testing */ protected function createDatabaseMock() { return new DatabaseMock(); } /** * Set up database connection and tables */ protected function setupDatabase() { $this->db = $this->ci->db; // Ensure test tables exist $this->createTestTables(); } /** * Create test tables if they don't exist */ protected function createTestTables() { // This would normally create actual test tables // For now, we'll mock this functionality } /** * Execute a raw SQL query for testing */ protected function executeRawSQL($sql) { return $this->db->query($sql); } /** * Get table structure information */ protected function getTableStructure($tableName) { return $this->db->field_data($tableName); } /** * Clean up test data */ protected function cleanupTestData($tableName, $conditions = []) { $this->db->delete($tableName, $conditions); } } /** * Mock Database class for testing when real database is not available */ class DatabaseMock { private $lastQuery = ''; private $lastError = ['code' => 0, 'message' => '']; private $insertSuccess = true; private $mockData = []; public function table_exists($tableName) { // Mock table existence based on expected Desk-Moloni tables $expectedTables = [ 'desk_moloni_config', 'desk_moloni_mapping', 'desk_moloni_sync_queue', 'desk_moloni_sync_log' ]; return in_array($tableName, $expectedTables); } public function field_exists($fieldName, $tableName) { // Mock field existence based on table structure $tableFields = [ 'desk_moloni_config' => [ 'id', 'setting_key', 'setting_value', 'encrypted', 'created_at', 'updated_at' ], 'desk_moloni_mapping' => [ 'id', 'entity_type', 'perfex_id', 'moloni_id', 'sync_direction', 'last_sync_at', 'created_at', 'updated_at' ], 'desk_moloni_sync_queue' => [ 'id', 'task_type', 'entity_type', 'entity_id', 'priority', 'payload', 'status', 'attempts', 'max_attempts', 'scheduled_at', 'started_at', 'completed_at', 'error_message', 'created_at', 'updated_at' ], 'desk_moloni_sync_log' => [ 'id', 'operation_type', 'entity_type', 'perfex_id', 'moloni_id', 'direction', 'status', 'request_data', 'response_data', 'error_message', 'execution_time_ms', 'created_at' ] ]; return isset($tableFields[$tableName]) && in_array($fieldName, $tableFields[$tableName]); } public function field_data($tableName) { // Mock field data structure $mockField = new stdClass(); $mockField->name = 'id'; $mockField->type = 'int'; $mockField->primary_key = 1; return [$mockField]; } public function insert($tableName, $data) { $this->lastQuery = "INSERT INTO {$tableName}"; // Mock insert validation if (isset($data['setting_key']) && $data['setting_key'] === 'test_unique_key') { static $inserted = false; if ($inserted) { $this->lastError = ['code' => 1062, 'message' => 'Duplicate entry']; return false; } $inserted = true; } // Store mock data for retrieval $data['id'] = rand(1, 1000); $data['created_at'] = date('Y-m-d H:i:s'); $data['updated_at'] = date('Y-m-d H:i:s'); $this->mockData[] = (object) $data; return $this->insertSuccess; } public function update($tableName, $data, $where = null) { $this->lastQuery = "UPDATE {$tableName}"; return true; } public function delete($tableName, $where = null) { $this->lastQuery = "DELETE FROM {$tableName}"; return true; } public function where($field, $value = null) { return $this; } public function order_by($field, $direction = 'ASC') { return $this; } public function limit($limit, $offset = null) { return $this; } public function get($tableName = null) { $result = new stdClass(); $result->row_array = []; $result->result_array = $this->mockData; $result->row = function() { return !empty($this->mockData) ? $this->mockData[0] : null; }; $result->result = function() { return $this->mockData; }; return $result; } public function count_all_results($tableName) { return count($this->mockData); } public function query($sql) { $this->lastQuery = $sql; // Mock specific queries if (strpos($sql, 'SHOW INDEX') !== false) { $mockIndexes = [ ['Key_name' => 'PRIMARY'], ['Key_name' => 'idx_setting_key'], ['Key_name' => 'idx_encrypted'], ['Key_name' => 'idx_created_at'] ]; $result = new stdClass(); $result->result_array = function() use ($mockIndexes) { return $mockIndexes; }; return $result; } if (strpos($sql, 'TABLE_COLLATION') !== false) { $result = new stdClass(); $result->row = function() { $row = new stdClass(); $row->TABLE_COLLATION = 'utf8mb4_unicode_ci'; return $row; }; return $result; } if (strpos($sql, 'ENGINE') !== false) { $result = new stdClass(); $result->row = function() { $row = new stdClass(); $row->ENGINE = 'InnoDB'; return $row; }; return $result; } return $this->get(); } public function error() { return $this->lastError; } public function insert_id() { return rand(1, 1000); } // Reset mock state public function reset() { $this->mockData = []; $this->lastError = ['code' => 0, 'message' => '']; $this->insertSuccess = true; } }