Files
desk-moloni/modules/desk_moloni/tests/database/ConfigTableTest.php
Emanuel Almeida 8c4f68576f chore: add spec-kit and standardize signatures
- Added GitHub spec-kit for development workflow
- Standardized file signatures to Descomplicar® format
- Updated development configuration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-12 01:27:37 +01:00

217 lines
8.1 KiB
PHP

/**
* Descomplicar® Crescimento Digital
* https://descomplicar.pt
*/
<?php
declare(strict_types=1);
namespace DeskMoloni\Tests\Database;
use PHPUnit\Framework\TestCase;
use PDO;
/**
* Contract Test: desk_moloni_config table structure and constraints
*
* This test MUST FAIL initially as part of TDD methodology.
* Tests validate database schema contracts before implementation.
*/
class ConfigTableTest extends TestCase
{
private PDO $pdo;
private string $tableName = 'tbl_desk_moloni_config';
protected function setUp(): void
{
global $testConfig;
$this->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_%'");
}
}