Normalize formatting/line endings across configs, controllers, models, tests, and OpenAPI specs. Update rule expression/rule engine implementation and remove obsolete RuleAction controller/model. Add unit tests for rule expression syntax and multi-action behavior, and include docs updates.
272 lines
8.8 KiB
PHP
272 lines
8.8 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\Rule;
|
|
|
|
use App\Models\Rule\RuleDefModel;
|
|
use CodeIgniter\Test\CIUnitTestCase;
|
|
use CodeIgniter\Test\DatabaseTestTrait;
|
|
|
|
class RuleDefModelTest extends CIUnitTestCase
|
|
{
|
|
use DatabaseTestTrait;
|
|
|
|
protected $model;
|
|
protected $seed = \App\Database\Seeds\TestSeeder::class;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->model = new RuleDefModel();
|
|
}
|
|
|
|
/**
|
|
* Test that getActiveByEvent returns empty array when TestSiteID is null
|
|
* This ensures rules are standalone and must be explicitly included by test
|
|
*/
|
|
public function testGetActiveByEventReturnsEmptyWithoutTestSiteID(): void
|
|
{
|
|
$rules = $this->model->getActiveByEvent('test_created', null);
|
|
|
|
$this->assertIsArray($rules);
|
|
$this->assertEmpty($rules);
|
|
}
|
|
|
|
/**
|
|
* Test that a rule can be linked to multiple tests
|
|
*/
|
|
public function testRuleCanBeLinkedToMultipleTests(): void
|
|
{
|
|
$db = \Config\Database::connect();
|
|
|
|
// Get two existing tests
|
|
$tests = $db->table('testdefsite')
|
|
->where('EndDate', null)
|
|
->limit(2)
|
|
->get()
|
|
->getResultArray();
|
|
|
|
if (count($tests) < 2) {
|
|
$this->markTestSkipped('Need at least 2 tests available in testdefsite table');
|
|
}
|
|
|
|
$testSiteID1 = (int) $tests[0]['TestSiteID'];
|
|
$testSiteID2 = (int) $tests[1]['TestSiteID'];
|
|
|
|
// Create a rule
|
|
$ruleData = [
|
|
'RuleCode' => 'MULTI_TEST_RULE',
|
|
'RuleName' => 'Multi Test Rule',
|
|
'EventCode' => 'test_created',
|
|
'ConditionExpr' => 'order["InternalOID"] > 0',
|
|
'CreateDate' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
$ruleID = $this->model->insert($ruleData, true);
|
|
$this->assertNotFalse($ruleID);
|
|
|
|
// Link rule to both tests
|
|
$this->model->linkTest($ruleID, $testSiteID1);
|
|
$this->model->linkTest($ruleID, $testSiteID2);
|
|
|
|
// Verify rule is returned for both test sites
|
|
$rules1 = $this->model->getActiveByEvent('test_created', $testSiteID1);
|
|
$this->assertNotEmpty($rules1);
|
|
$this->assertCount(1, $rules1);
|
|
$this->assertEquals($ruleID, $rules1[0]['RuleID']);
|
|
|
|
$rules2 = $this->model->getActiveByEvent('test_created', $testSiteID2);
|
|
$this->assertNotEmpty($rules2);
|
|
$this->assertCount(1, $rules2);
|
|
$this->assertEquals($ruleID, $rules2[0]['RuleID']);
|
|
|
|
// Cleanup
|
|
$this->model->delete($ruleID);
|
|
}
|
|
|
|
/**
|
|
* Test that rules only work when explicitly linked to a test
|
|
*/
|
|
public function testRulesOnlyWorkWhenExplicitlyLinked(): void
|
|
{
|
|
$db = \Config\Database::connect();
|
|
|
|
// Get an existing test
|
|
$test = $db->table('testdefsite')
|
|
->where('EndDate', null)
|
|
->limit(1)
|
|
->get()
|
|
->getRowArray();
|
|
|
|
if (!$test) {
|
|
$this->markTestSkipped('No tests available in testdefsite table');
|
|
}
|
|
|
|
$testSiteID = (int) $test['TestSiteID'];
|
|
|
|
// Create a rule (not linked to any test yet)
|
|
$ruleData = [
|
|
'RuleCode' => 'UNLINKED_RULE',
|
|
'RuleName' => 'Unlinked Test Rule',
|
|
'EventCode' => 'test_created',
|
|
'ConditionExpr' => 'true',
|
|
'CreateDate' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
$ruleID = $this->model->insert($ruleData, true);
|
|
$this->assertNotFalse($ruleID);
|
|
|
|
// Verify rule is NOT returned when not linked
|
|
$rules = $this->model->getActiveByEvent('test_created', $testSiteID);
|
|
$this->assertEmpty($rules);
|
|
|
|
// Now link the rule
|
|
$this->model->linkTest($ruleID, $testSiteID);
|
|
|
|
// Verify rule is now returned
|
|
$rules = $this->model->getActiveByEvent('test_created', $testSiteID);
|
|
$this->assertNotEmpty($rules);
|
|
$this->assertCount(1, $rules);
|
|
|
|
// Cleanup
|
|
$this->model->delete($ruleID);
|
|
}
|
|
|
|
/**
|
|
* Test that unlinking a test removes the rule for that test
|
|
*/
|
|
public function testUnlinkingTestRemovesRule(): void
|
|
{
|
|
$db = \Config\Database::connect();
|
|
|
|
// Get two existing tests
|
|
$tests = $db->table('testdefsite')
|
|
->where('EndDate', null)
|
|
->limit(2)
|
|
->get()
|
|
->getResultArray();
|
|
|
|
if (count($tests) < 2) {
|
|
$this->markTestSkipped('Need at least 2 tests available in testdefsite table');
|
|
}
|
|
|
|
$testSiteID1 = (int) $tests[0]['TestSiteID'];
|
|
$testSiteID2 = (int) $tests[1]['TestSiteID'];
|
|
|
|
// Create a rule and link to both tests
|
|
$ruleData = [
|
|
'RuleCode' => 'UNLINK_TEST',
|
|
'RuleName' => 'Unlink Test Rule',
|
|
'EventCode' => 'test_created',
|
|
'ConditionExpr' => 'true',
|
|
'CreateDate' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
$ruleID = $this->model->insert($ruleData, true);
|
|
$this->model->linkTest($ruleID, $testSiteID1);
|
|
$this->model->linkTest($ruleID, $testSiteID2);
|
|
|
|
// Verify rule is returned for both
|
|
$this->assertNotEmpty($this->model->getActiveByEvent('test_created', $testSiteID1));
|
|
$this->assertNotEmpty($this->model->getActiveByEvent('test_created', $testSiteID2));
|
|
|
|
// Unlink from first test
|
|
$this->model->unlinkTest($ruleID, $testSiteID1);
|
|
|
|
// Verify rule is NOT returned for first test but still for second
|
|
$this->assertEmpty($this->model->getActiveByEvent('test_created', $testSiteID1));
|
|
$this->assertNotEmpty($this->model->getActiveByEvent('test_created', $testSiteID2));
|
|
|
|
// Cleanup
|
|
$this->model->delete($ruleID);
|
|
}
|
|
|
|
/**
|
|
* Test that deleted (soft deleted) rules are not returned
|
|
*/
|
|
public function testDeletedRulesAreNotReturned(): void
|
|
{
|
|
$db = \Config\Database::connect();
|
|
|
|
$test = $db->table('testdefsite')
|
|
->where('EndDate', null)
|
|
->limit(1)
|
|
->get()
|
|
->getRowArray();
|
|
|
|
if (!$test) {
|
|
$this->markTestSkipped('No tests available in testdefsite table');
|
|
}
|
|
|
|
$testSiteID = (int) $test['TestSiteID'];
|
|
|
|
// Create a rule and link it
|
|
$ruleData = [
|
|
'RuleCode' => 'DELETED_RULE',
|
|
'RuleName' => 'Deleted Test Rule',
|
|
'EventCode' => 'test_created',
|
|
'ConditionExpr' => 'true',
|
|
'CreateDate' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
$ruleID = $this->model->insert($ruleData, true);
|
|
$this->assertNotFalse($ruleID);
|
|
$this->model->linkTest($ruleID, $testSiteID);
|
|
|
|
// Verify rule is returned
|
|
$rules = $this->model->getActiveByEvent('test_created', $testSiteID);
|
|
$this->assertNotEmpty($rules);
|
|
|
|
// Soft delete the rule
|
|
$this->model->delete($ruleID);
|
|
|
|
// Verify deleted rule is NOT returned
|
|
$rules = $this->model->getActiveByEvent('test_created', $testSiteID);
|
|
$this->assertEmpty($rules);
|
|
}
|
|
|
|
/**
|
|
* Test getting linked tests for a rule
|
|
*/
|
|
public function testGetLinkedTests(): void
|
|
{
|
|
$db = \Config\Database::connect();
|
|
|
|
// Get two existing tests
|
|
$tests = $db->table('testdefsite')
|
|
->where('EndDate', null)
|
|
->limit(2)
|
|
->get()
|
|
->getResultArray();
|
|
|
|
if (count($tests) < 2) {
|
|
$this->markTestSkipped('Need at least 2 tests available');
|
|
}
|
|
|
|
$testSiteID1 = (int) $tests[0]['TestSiteID'];
|
|
$testSiteID2 = (int) $tests[1]['TestSiteID'];
|
|
|
|
// Create a rule
|
|
$ruleData = [
|
|
'RuleCode' => 'LINKED_TESTS',
|
|
'RuleName' => 'Linked Tests Rule',
|
|
'EventCode' => 'test_created',
|
|
'ConditionExpr' => 'true',
|
|
'CreateDate' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
$ruleID = $this->model->insert($ruleData, true);
|
|
$this->model->linkTest($ruleID, $testSiteID1);
|
|
$this->model->linkTest($ruleID, $testSiteID2);
|
|
|
|
// Get linked tests
|
|
$linkedTests = $this->model->getLinkedTests($ruleID);
|
|
|
|
$this->assertCount(2, $linkedTests);
|
|
$this->assertContains($testSiteID1, $linkedTests);
|
|
$this->assertContains($testSiteID2, $linkedTests);
|
|
|
|
// Cleanup
|
|
$this->model->delete($ruleID);
|
|
}
|
|
}
|