Files
citrus-cms/tests/Unit/WeldLogTriggerSystemTest.php
2026-04-28 21:15:09 +03:00

557 lines
18 KiB
PHP

<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\Services\WeldLogTriggers\WeldLogTriggerManager;
use App\Services\WeldLogTriggers\WeldLogTriggerRegistry;
use App\Services\WeldLogTriggers\Contracts\WeldLogTriggerInterface;
use App\Models\WeldLog;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
/**
* WeldLog Trigger System Integration Test Suite
*
* COMPREHENSIVE TESTING - Uses PRODUCTION Database for ALL tables
*
* This test suite validates:
* - Trigger registry and structure
* - Trigger execution with REAL production data
* - Database interactions with actual production tables
* - Field dependency logic
* - Execution order and results
*
* IMPORTANT: Uses PRODUCTION database (devstellar_viksa) for ALL tables
* - ALL tables use production database (real data)
* - Manual transaction management ensures rollback
* - 100% SAFE: All changes are rolled back after each test
*
* Note: Manual transaction management - NO data is persisted
*/
class WeldLogTriggerSystemTest extends TestCase
{
protected $registry;
protected $manager;
/**
* Setup for each test
*/
protected function setUp(): void
{
parent::setUp();
// Start transaction on PRODUCTION database (mysql connection)
// This ensures ALL changes will be rolled back
DB::beginTransaction();
Log::info("=== TEST STARTED ===", [
'database' => DB::getDatabaseName(),
'connection' => DB::getDefaultConnection(),
'transaction_level' => DB::transactionLevel(),
'protection' => 'ENABLED - All changes will be rolled back'
]);
// Initialize trigger system
$this->registry = new WeldLogTriggerRegistry();
$this->manager = new WeldLogTriggerManager($this->registry);
}
/**
* Teardown after each test
*/
protected function tearDown(): void
{
// IMPORTANT: Rollback ALL production database changes
// This ensures NO changes are persisted
if (DB::transactionLevel() > 0) {
DB::rollBack();
Log::info("=== TEST COMPLETED - ALL CHANGES ROLLED BACK ===", [
'database' => DB::getDatabaseName(),
'changes_persisted' => 'NO - Everything rolled back'
]);
}
parent::tearDown();
}
// ========================================
// REGISTRY AND STRUCTURE TESTS
// ========================================
/**
* Test: Registry registers all triggers correctly
*/
public function test_registry_registers_all_triggers()
{
$triggers = $this->registry->getTriggersInOrder();
$this->assertGreaterThanOrEqual(13, count($triggers), 'Registry should have at least 13 triggers');
foreach ($triggers as $trigger) {
$this->assertInstanceOf(
WeldLogTriggerInterface::class,
$trigger,
'All triggers must implement WeldLogTriggerInterface'
);
}
}
/**
* Test: Triggers are returned in correct order
*/
public function test_triggers_are_sorted_by_order()
{
$triggers = $this->registry->getTriggersInOrder();
$previousOrder = 0;
foreach ($triggers as $trigger) {
$currentOrder = $trigger->getOrder();
$this->assertGreaterThanOrEqual(
$previousOrder,
$currentOrder,
"Triggers must be sorted by order. {$trigger->getName()} has order {$currentOrder} but previous was {$previousOrder}"
);
$previousOrder = $currentOrder;
}
}
/**
* Test: Get specific trigger by name
*/
public function test_get_trigger_by_name()
{
$trigger = $this->registry->getTrigger('Spool Status Changer');
$this->assertNotNull($trigger, 'Should find Spool Status Changer trigger');
$this->assertEquals('Spool Status Changer', $trigger->getName());
$this->assertEquals(1, $trigger->getOrder());
}
/**
* Test: All expected triggers are registered
*/
public function test_all_expected_triggers_are_registered()
{
$expectedTriggers = [
'Spool Status Changer',
'Line Lists Update',
'NDE Matrix Update',
'Request Date Operations',
'Repair Logs Update',
'NDE Project Update',
'Test Package Operations',
'Test Pack Sync from WeldLog',
'Construction Paint Logs Sync',
'Test Pack Base Status Changer',
'Paint Follow Ups Sync',
'Handovers Sync',
'Test Pack Cleanup',
'NdtCalculationCacheTrigger',
'RegisterCreatorCacheTrigger'
];
$registeredNames = $this->registry->getTriggerNames();
foreach ($expectedTriggers as $expectedName) {
$this->assertContains(
$expectedName,
$registeredNames,
"Expected trigger '{$expectedName}' should be registered"
);
}
}
// ========================================
// FIELD DEPENDENCY TESTS
// ========================================
/**
* Test: shouldRun returns true for new records
*/
public function test_should_run_returns_true_for_new_records()
{
$trigger = $this->registry->getTrigger('Spool Status Changer');
$shouldRun = $trigger->shouldRun([], true);
$this->assertTrue($shouldRun, 'All triggers should run for new records');
}
/**
* Test: shouldRun checks dependent fields correctly
*/
public function test_should_run_checks_dependent_fields()
{
$trigger = $this->registry->getTrigger('Spool Status Changer');
// Should run when dependent field changes
$shouldRun1 = $trigger->shouldRun(['spool_number'], false);
$this->assertTrue($shouldRun1, 'Should run when spool_number changes');
$shouldRun2 = $trigger->shouldRun(['iso_number'], false);
$this->assertTrue($shouldRun2, 'Should run when iso_number changes');
// Should not run when non-dependent field changes
$shouldRun3 = $trigger->shouldRun(['drawing_revision'], false);
$this->assertFalse($shouldRun3, 'Should not run when non-dependent field changes');
}
/**
* Test: Spool Status Changer dependencies
*/
public function test_spool_status_changer_dependencies()
{
$trigger = $this->registry->getTrigger('Spool Status Changer');
$dependentFields = $trigger->getDependentFields();
$this->assertContains('spool_number', $dependentFields);
$this->assertContains('iso_number', $dependentFields);
$this->assertContains('type_of_joint', $dependentFields);
}
/**
* Test: NDE Matrix trigger dependencies
*/
public function test_nde_matrix_trigger_dependencies()
{
$trigger = $this->registry->getTrigger('NDE Matrix Update');
$dependentFields = $trigger->getDependentFields();
$this->assertContains('type_of_welds', $dependentFields);
$this->assertContains('fluid_code', $dependentFields);
$this->assertContains('type_of_joint', $dependentFields);
}
/**
* Test: Test Package trigger dependencies
*/
public function test_test_package_trigger_dependencies()
{
$trigger = $this->registry->getTrigger('Test Package Operations');
$dependentFields = $trigger->getDependentFields();
$this->assertContains('test_package_no', $dependentFields);
$this->assertContains('iso_number', $dependentFields);
}
// ========================================
// DATABASE INTERACTION TESTS
// ========================================
/**
* Test: Read real weld log record from production
*/
public function test_read_real_weld_log_record()
{
// Get first weld log from PRODUCTION database
$weldLog = WeldLog::first();
$this->assertInstanceOf(WeldLog::class, $weldLog);
$this->assertNotNull($weldLog->id);
// Verify we're reading from production
$this->assertEquals('mysql', $weldLog->getConnectionName());
Log::info("Testing with real production weld log", [
'id' => $weldLog->id,
'spool_number' => $weldLog->spool_number ?? 'N/A',
'iso_number' => $weldLog->iso_number ?? 'N/A'
]);
}
/**
* Test: Manager executes triggers with real production data
*/
public function test_manager_executes_triggers_with_real_data()
{
// Get real weld log from PRODUCTION
$weldLog = WeldLog::first();
$this->assertNotNull($weldLog, 'Production database must have at least one weld log');
// Convert to object format that triggers expect
$weldLogData = (object) $weldLog->toArray();
$results = $this->manager->executeTriggers(
$weldLogData,
null, // Simulate new record
['spool_number', 'iso_number', 'line_number'],
true
);
$this->assertIsArray($results);
$this->assertNotEmpty($results);
// Check that we have execution status for each trigger
foreach ($results as $triggerName => $result) {
$this->assertIsArray($result);
$this->assertTrue(
isset($result['executed']) || isset($result['skipped']) || isset($result['queued']),
"Trigger {$triggerName} must have execution status"
);
}
Log::info("Tested triggers with real production data", [
'weld_log_id' => $weldLog->id,
'triggers_executed' => count($results)
]);
}
/**
* Test: Only relevant triggers execute for specific field changes
*/
public function test_only_relevant_triggers_execute_for_field_changes()
{
// Get real weld log from PRODUCTION
$weldLog = WeldLog::first();
$this->assertNotNull($weldLog);
// Simulate field change (within transaction - will be rolled back)
$beforeData = (object) $weldLog->toArray();
$originalWelder = $weldLog->welder_1;
$weldLog->welder_1 = 'TEST-UPDATED-WELDER';
$weldLog->save();
$afterData = (object) $weldLog->fresh()->toArray();
$changedFields = ['welder_1'];
$results = $this->manager->executeTriggers(
$afterData,
$beforeData,
$changedFields,
false
);
// Count skipped vs executed
$skippedCount = 0;
$executedCount = 0;
foreach ($results as $result) {
if (isset($result['skipped']) && $result['skipped']) {
$skippedCount++;
} elseif (isset($result['executed']) && $result['executed']) {
$executedCount++;
}
}
// Most triggers should be skipped since welder_1 is not critical
$this->assertGreaterThan(
$executedCount,
$skippedCount,
'Most triggers should be skipped when only welder_1 changes'
);
Log::info("Tested field-specific triggers", [
'changed_field' => 'welder_1',
'skipped' => $skippedCount,
'executed' => $executedCount,
'note' => 'Changes rolled back - production data unchanged'
]);
}
/**
* Test: Critical field changes trigger multiple triggers
*/
public function test_critical_field_changes_trigger_multiple_triggers()
{
// Get real weld log from PRODUCTION
$weldLog = WeldLog::first();
$this->assertNotNull($weldLog);
$beforeData = (object) $weldLog->toArray();
// Change critical fields (within transaction - will be rolled back)
$originalSpool = $weldLog->spool_number;
$originalIso = $weldLog->iso_number;
$originalTP = $weldLog->test_package_no;
$weldLog->spool_number = 'TEST-CRITICAL-SPOOL';
$weldLog->iso_number = 'TEST-CRITICAL-ISO';
$weldLog->test_package_no = 'TEST-CRITICAL-TP';
$weldLog->save();
$afterData = (object) $weldLog->fresh()->toArray();
$changedFields = ['spool_number', 'iso_number', 'test_package_no'];
$results = $this->manager->executeTriggers(
$afterData,
$beforeData,
$changedFields,
false
);
// Multiple field changes should trigger multiple triggers
$executedCount = 0;
foreach ($results as $result) {
if (isset($result['executed']) && $result['executed']) {
$executedCount++;
}
}
$this->assertGreaterThan(
2,
$executedCount,
'Multiple critical field changes should trigger multiple triggers'
);
Log::info("Tested critical field changes", [
'changed_fields' => $changedFields,
'triggers_executed' => $executedCount,
'note' => 'Changes rolled back - production data unchanged'
]);
}
/**
* Test: All triggers execute for new record simulation
*/
public function test_all_triggers_execute_for_new_record()
{
// Get real weld log from PRODUCTION to simulate new record
$weldLog = WeldLog::first();
$this->assertNotNull($weldLog);
$weldLogData = (object) $weldLog->toArray();
$results = $this->manager->executeTriggers(
$weldLogData,
null, // Simulate new record
[],
true
);
$this->assertNotEmpty($results);
// For new records, all triggers should at least attempt to run
foreach ($results as $triggerName => $result) {
$this->assertTrue(
isset($result['executed']) || isset($result['skipped']) || isset($result['queued']),
"Trigger {$triggerName} must have execution status for new record"
);
}
Log::info("Tested new record trigger execution", [
'total_triggers' => count($results),
'note' => 'Simulated new record with real production data'
]);
}
// ========================================
// INTERFACE AND STRUCTURE TESTS
// ========================================
/**
* Test: Trigger interface methods exist
*/
public function test_trigger_interface_methods_exist()
{
$trigger = $this->registry->getTrigger('Spool Status Changer');
$this->assertTrue(method_exists($trigger, 'getName'));
$this->assertTrue(method_exists($trigger, 'getOrder'));
$this->assertTrue(method_exists($trigger, 'shouldRun'));
$this->assertTrue(method_exists($trigger, 'getDependentFields'));
$this->assertTrue(method_exists($trigger, 'execute'));
$this->assertTrue(method_exists($trigger, 'isAsync'));
}
/**
* Test: Dependent fields are properly defined
*/
public function test_dependent_fields_are_properly_defined()
{
$triggers = $this->registry->getTriggersInOrder();
foreach ($triggers as $trigger) {
$dependentFields = $trigger->getDependentFields();
$this->assertIsArray(
$dependentFields,
"{$trigger->getName()} must return array for getDependentFields()"
);
foreach ($dependentFields as $field) {
$this->assertIsString(
$field,
"Dependent field must be string in {$trigger->getName()}"
);
}
}
}
/**
* Test: Trigger async flag returns boolean
*/
public function test_trigger_async_flag()
{
$triggers = $this->registry->getTriggersInOrder();
foreach ($triggers as $trigger) {
$isAsync = $trigger->isAsync();
$this->assertIsBool(
$isAsync,
"{$trigger->getName()} isAsync() must return boolean"
);
}
}
/**
* Test: Manager returns results array
*/
public function test_manager_returns_results_array()
{
// Get real weld log from PRODUCTION
$weldLog = WeldLog::first();
$this->assertNotNull($weldLog);
$weldLogData = (object) $weldLog->toArray();
$results = $this->manager->executeTriggers($weldLogData, null, [], true);
$this->assertIsArray($results);
foreach ($results as $triggerName => $result) {
$this->assertIsString($triggerName);
$this->assertIsArray($result);
}
}
/**
* Test: Registry count method
*/
public function test_registry_count()
{
$count = $this->registry->count();
$this->assertIsInt($count);
$this->assertGreaterThanOrEqual(13, $count);
}
/**
* Test: Trigger names are unique
*/
public function test_trigger_names_are_unique()
{
$names = $this->registry->getTriggerNames();
$uniqueNames = array_unique($names);
$this->assertEquals(
count($names),
count($uniqueNames),
"All trigger names must be unique"
);
}
/**
* Test: Manager get registry method
*/
public function test_manager_get_registry()
{
$registry = $this->manager->getRegistry();
$this->assertInstanceOf(WeldLogTriggerRegistry::class, $registry);
$this->assertSame($this->registry, $registry);
}
}