- Bump DESK_MOLONI version to 3.0.1 across module - Normalize hooks to after_client_* and instantiate PerfexHooks safely - Fix OAuthController view path and API client class name - Add missing admin views for webhook config/logs; adjust view loading - Harden client portal routes and admin routes mapping - Make Dashboard/Logs/Queue tolerant to optional model methods - Align log details query with existing schema; avoid broken joins This makes the module operational in Perfex (admin + client), reduces 404s, and avoids fatal errors due to inconsistent tables/methods.
221 lines
8.0 KiB
PHP
221 lines
8.0 KiB
PHP
<?php
|
|
/**
|
|
* Contract Test for desk_moloni_config table
|
|
*
|
|
* This test MUST FAIL until the Config_model is properly implemented
|
|
* Following TDD RED-GREEN-REFACTOR cycle
|
|
*
|
|
* @package DeskMoloni\Tests\Contract
|
|
*/
|
|
|
|
namespace DeskMoloni\Tests\Contract;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class ConfigTableTest extends TestCase
|
|
{
|
|
private $CI;
|
|
private $db;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
// Initialize CodeIgniter instance
|
|
$this->CI = &get_instance();
|
|
$this->CI->load->database();
|
|
$this->db = $this->CI->db;
|
|
|
|
// Ensure we're in test environment
|
|
if (ENVIRONMENT !== 'testing') {
|
|
$this->markTestSkipped('Contract tests should only run in testing environment');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @test
|
|
* Contract: desk_moloni_config table must exist with correct structure
|
|
*/
|
|
public function config_table_exists_with_required_structure()
|
|
{
|
|
// ARRANGE: Test database table existence and structure
|
|
|
|
// ACT: Query table structure
|
|
$table_exists = $this->db->table_exists('desk_moloni_config');
|
|
|
|
// ASSERT: Table must exist
|
|
$this->assertTrue($table_exists, 'desk_moloni_config table must exist');
|
|
|
|
// ASSERT: Required columns exist with correct types
|
|
$fields = $this->db->list_fields('desk_moloni_config');
|
|
|
|
$required_fields = ['id', 'setting_key', 'setting_value', 'encrypted', 'created_at', 'updated_at'];
|
|
foreach ($required_fields as $field) {
|
|
$this->assertContains($field, $fields, "Required field '{$field}' must exist in desk_moloni_config table");
|
|
}
|
|
|
|
// ASSERT: Check field types and constraints
|
|
$field_data = $this->db->field_data('desk_moloni_config');
|
|
$field_info = [];
|
|
foreach ($field_data as $field) {
|
|
$field_info[$field->name] = $field;
|
|
}
|
|
|
|
// Verify setting_key is unique
|
|
$this->assertEquals('varchar', strtolower($field_info['setting_key']->type), 'setting_key must be varchar type');
|
|
$this->assertEquals(255, $field_info['setting_key']->max_length, 'setting_key must have max_length of 255');
|
|
|
|
// Verify encrypted is boolean (tinyint in MySQL)
|
|
$this->assertEquals('tinyint', strtolower($field_info['encrypted']->type), 'encrypted must be tinyint type');
|
|
$this->assertEquals(1, $field_info['encrypted']->default_value, 'encrypted must have default value of 0');
|
|
}
|
|
|
|
/**
|
|
* @test
|
|
* Contract: Config table must enforce unique constraint on setting_key
|
|
*/
|
|
public function config_table_enforces_unique_setting_key()
|
|
{
|
|
// ARRANGE: Clean table and insert test data
|
|
$this->db->truncate('desk_moloni_config');
|
|
|
|
$test_data = [
|
|
'setting_key' => 'test_unique_key',
|
|
'setting_value' => 'test_value',
|
|
'encrypted' => 0
|
|
];
|
|
|
|
// ACT & ASSERT: First insert should succeed
|
|
$first_insert = $this->db->insert('desk_moloni_config', $test_data);
|
|
$this->assertTrue($first_insert, 'First insert with unique key should succeed');
|
|
|
|
// ACT & ASSERT: Second insert with same key should fail
|
|
$this->expectException(\Exception::class);
|
|
$this->db->insert('desk_moloni_config', $test_data);
|
|
}
|
|
|
|
/**
|
|
* @test
|
|
* Contract: Config table must have proper indexes for performance
|
|
*/
|
|
public function config_table_has_required_indexes()
|
|
{
|
|
// ACT: Get table indexes
|
|
$indexes = $this->db->query("SHOW INDEX FROM desk_moloni_config")->result_array();
|
|
|
|
// ASSERT: Primary key exists
|
|
$has_primary = false;
|
|
$has_setting_key_index = false;
|
|
|
|
foreach ($indexes as $index) {
|
|
if ($index['Key_name'] === 'PRIMARY') {
|
|
$has_primary = true;
|
|
}
|
|
if ($index['Key_name'] === 'idx_setting_key') {
|
|
$has_setting_key_index = true;
|
|
}
|
|
}
|
|
|
|
$this->assertTrue($has_primary, 'Table must have PRIMARY KEY');
|
|
$this->assertTrue($has_setting_key_index, 'Table must have idx_setting_key index for performance');
|
|
}
|
|
|
|
/**
|
|
* @test
|
|
* Contract: Config table must support encrypted and non-encrypted values
|
|
*/
|
|
public function config_table_supports_encryption_flag()
|
|
{
|
|
// ARRANGE: Clean table
|
|
$this->db->truncate('desk_moloni_config');
|
|
|
|
// ACT: Insert encrypted and non-encrypted test data
|
|
$encrypted_data = [
|
|
'setting_key' => 'oauth_access_token',
|
|
'setting_value' => 'encrypted_token_value',
|
|
'encrypted' => 1
|
|
];
|
|
|
|
$plain_data = [
|
|
'setting_key' => 'api_base_url',
|
|
'setting_value' => 'https://api.moloni.pt/v1',
|
|
'encrypted' => 0
|
|
];
|
|
|
|
$this->db->insert('desk_moloni_config', $encrypted_data);
|
|
$this->db->insert('desk_moloni_config', $plain_data);
|
|
|
|
// ASSERT: Data inserted correctly with proper encryption flags
|
|
$encrypted_row = $this->db->get_where('desk_moloni_config', ['setting_key' => 'oauth_access_token'])->row();
|
|
$plain_row = $this->db->get_where('desk_moloni_config', ['setting_key' => 'api_base_url'])->row();
|
|
|
|
$this->assertEquals(1, $encrypted_row->encrypted, 'Encrypted flag must be set for sensitive data');
|
|
$this->assertEquals(0, $plain_row->encrypted, 'Encrypted flag must be false for plain data');
|
|
}
|
|
|
|
/**
|
|
* @test
|
|
* Contract: Config table must have automatic timestamps
|
|
*/
|
|
public function config_table_has_automatic_timestamps()
|
|
{
|
|
// ARRANGE: Clean table
|
|
$this->db->truncate('desk_moloni_config');
|
|
|
|
// ACT: Insert test record
|
|
$test_data = [
|
|
'setting_key' => 'timestamp_test',
|
|
'setting_value' => 'test_value',
|
|
'encrypted' => 0
|
|
];
|
|
|
|
$this->db->insert('desk_moloni_config', $test_data);
|
|
|
|
// ASSERT: Timestamps are automatically set
|
|
$row = $this->db->get_where('desk_moloni_config', ['setting_key' => 'timestamp_test'])->row();
|
|
|
|
$this->assertNotNull($row->created_at, 'created_at must be automatically set');
|
|
$this->assertNotNull($row->updated_at, 'updated_at must be automatically set');
|
|
|
|
// ASSERT: Timestamps are recent (within last 5 seconds)
|
|
$created_time = strtotime($row->created_at);
|
|
$current_time = time();
|
|
$this->assertLessThan(5, abs($current_time - $created_time), 'created_at must be recent');
|
|
}
|
|
|
|
/**
|
|
* @test
|
|
* Contract: Config table must support TEXT values for large configurations
|
|
*/
|
|
public function config_table_supports_large_text_values()
|
|
{
|
|
// ARRANGE: Clean table
|
|
$this->db->truncate('desk_moloni_config');
|
|
|
|
// ACT: Insert large value (simulate large JSON configuration)
|
|
$large_value = str_repeat('{"large_config":' . str_repeat('"test"', 1000) . '}', 10);
|
|
|
|
$test_data = [
|
|
'setting_key' => 'large_config_test',
|
|
'setting_value' => $large_value,
|
|
'encrypted' => 0
|
|
];
|
|
|
|
$insert_success = $this->db->insert('desk_moloni_config', $test_data);
|
|
|
|
// ASSERT: Large values can be stored
|
|
$this->assertTrue($insert_success, 'Table must support large TEXT values');
|
|
|
|
// ASSERT: Large value is retrieved correctly
|
|
$row = $this->db->get_where('desk_moloni_config', ['setting_key' => 'large_config_test'])->row();
|
|
$this->assertEquals($large_value, $row->setting_value, 'Large values must be stored and retrieved correctly');
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
// Clean up test data
|
|
if ($this->db) {
|
|
$this->db->where('setting_key LIKE', 'test_%');
|
|
$this->db->or_where('setting_key LIKE', '%_test');
|
|
$this->db->delete('desk_moloni_config');
|
|
}
|
|
}
|
|
} |