557 lines
18 KiB
PHP
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);
|
|
}
|
|
}
|