/** * Descomplicar® Crescimento Digital * https://descomplicar.pt */ pdo = new PDO( "mysql:host={$testConfig['database']['hostname']};dbname={$testConfig['database']['database']}", $testConfig['database']['username'], $testConfig['database']['password'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ] ); } public function testTableExists(): void { $stmt = $this->pdo->query("SHOW TABLES LIKE '{$this->tableName}'"); $result = $stmt->fetch(); $this->assertNotFalse($result, "Table {$this->tableName} must exist"); } public function testTableStructureContract(): void { $stmt = $this->pdo->query("DESCRIBE {$this->tableName}"); $columns = $stmt->fetchAll(); $expectedColumns = [ 'id' => ['Type' => 'int', 'Null' => 'NO', 'Key' => 'PRI', 'Extra' => 'auto_increment'], 'setting_key' => ['Type' => 'varchar(255)', 'Null' => 'NO', 'Key' => 'UNI'], 'setting_value' => ['Type' => 'text', 'Null' => 'YES'], 'encrypted' => ['Type' => 'tinyint(1)', 'Null' => 'YES', 'Default' => '0'], 'created_at' => ['Type' => 'timestamp', 'Null' => 'NO', 'Default' => 'CURRENT_TIMESTAMP'], 'updated_at' => ['Type' => 'timestamp', 'Null' => 'NO', 'Default' => 'CURRENT_TIMESTAMP'] ]; $actualColumns = []; foreach ($columns as $column) { $actualColumns[$column['Field']] = [ 'Type' => $column['Type'], 'Null' => $column['Null'], 'Key' => $column['Key'], 'Default' => $column['Default'], 'Extra' => $column['Extra'] ]; } foreach ($expectedColumns as $columnName => $expectedSpec) { $this->assertArrayHasKey($columnName, $actualColumns, "Column {$columnName} must exist"); foreach ($expectedSpec as $property => $expectedValue) { $this->assertEquals( $expectedValue, $actualColumns[$columnName][$property] ?? null, "Column {$columnName} property {$property} must match contract" ); } } } public function testUniqueConstraintOnSettingKey(): void { // Insert first record $stmt = $this->pdo->prepare("INSERT INTO {$this->tableName} (setting_key, setting_value) VALUES (?, ?)"); $stmt->execute(['test_unique_key', 'test_value']); // Attempt to insert duplicate key should fail $this->expectException(\PDOException::class); $this->expectExceptionMessage('Duplicate entry'); $stmt->execute(['test_unique_key', 'another_value']); } public function testEncryptionFlagValidation(): void { $stmt = $this->pdo->prepare("INSERT INTO {$this->tableName} (setting_key, setting_value, encrypted) VALUES (?, ?, ?)"); // Valid encryption flag values $stmt->execute(['test_encrypted_1', 'encrypted_value', 1]); $stmt->execute(['test_encrypted_0', 'plain_value', 0]); // Verify encryption flag is stored correctly $stmt = $this->pdo->query("SELECT setting_key, encrypted FROM {$this->tableName} WHERE setting_key IN ('test_encrypted_1', 'test_encrypted_0')"); $results = $stmt->fetchAll(); $this->assertCount(2, $results); foreach ($results as $result) { if ($result['setting_key'] === 'test_encrypted_1') { $this->assertEquals(1, $result['encrypted']); } else { $this->assertEquals(0, $result['encrypted']); } } } public function testTimestampAutomaticUpdates(): void { // Insert record $stmt = $this->pdo->prepare("INSERT INTO {$this->tableName} (setting_key, setting_value) VALUES (?, ?)"); $stmt->execute(['test_timestamp', 'initial_value']); // Get initial timestamps $stmt = $this->pdo->query("SELECT created_at, updated_at FROM {$this->tableName} WHERE setting_key = 'test_timestamp'"); $initial = $stmt->fetch(); // Wait a moment and update sleep(1); $stmt = $this->pdo->prepare("UPDATE {$this->tableName} SET setting_value = ? WHERE setting_key = ?"); $stmt->execute(['updated_value', 'test_timestamp']); // Get updated timestamps $stmt = $this->pdo->query("SELECT created_at, updated_at FROM {$this->tableName} WHERE setting_key = 'test_timestamp'"); $updated = $stmt->fetch(); // created_at should remain the same $this->assertEquals($initial['created_at'], $updated['created_at']); // updated_at should be different $this->assertNotEquals($initial['updated_at'], $updated['updated_at']); $this->assertGreaterThan($initial['updated_at'], $updated['updated_at']); } public function testRequiredIndexesExist(): void { $stmt = $this->pdo->query("SHOW INDEX FROM {$this->tableName}"); $indexes = $stmt->fetchAll(); $indexNames = array_column($indexes, 'Key_name'); // Required indexes based on schema $expectedIndexes = ['PRIMARY', 'setting_key', 'idx_setting_key', 'idx_encrypted']; foreach ($expectedIndexes as $expectedIndex) { $this->assertContains( $expectedIndex, $indexNames, "Index {$expectedIndex} must exist for performance" ); } } public function testSettingValueCanStoreJson(): void { $jsonData = json_encode([ 'complex' => 'data', 'with' => ['nested', 'arrays'], 'and' => 123, 'numbers' => true ]); $stmt = $this->pdo->prepare("INSERT INTO {$this->tableName} (setting_key, setting_value) VALUES (?, ?)"); $stmt->execute(['test_json', $jsonData]); $stmt = $this->pdo->query("SELECT setting_value FROM {$this->tableName} WHERE setting_key = 'test_json'"); $result = $stmt->fetch(); $this->assertEquals($jsonData, $result['setting_value']); $this->assertIsArray(json_decode($result['setting_value'], true)); } public function testEncryptedSettingsHandling(): void { // Simulate encrypted data storage $plaintext = 'sensitive_api_key_value'; $encryptedData = base64_encode(openssl_encrypt( $plaintext, 'AES-256-GCM', 'test_encryption_key_32_characters', 0, 'test_iv_12bytes' )); $stmt = $this->pdo->prepare("INSERT INTO {$this->tableName} (setting_key, setting_value, encrypted) VALUES (?, ?, ?)"); $stmt->execute(['oauth_access_token', $encryptedData, 1]); // Verify encrypted flag is set and data is stored $stmt = $this->pdo->query("SELECT setting_value, encrypted FROM {$this->tableName} WHERE setting_key = 'oauth_access_token'"); $result = $stmt->fetch(); $this->assertEquals(1, $result['encrypted']); $this->assertEquals($encryptedData, $result['setting_value']); $this->assertNotEquals($plaintext, $result['setting_value']); } protected function tearDown(): void { // Clean up test data $this->pdo->exec("DELETE FROM {$this->tableName} WHERE setting_key LIKE 'test_%'"); } }