IntegrityCheckTest.php

Namespace

Drupal\Tests\rules\Unit\Integration\Engine

File

tests/src/Unit/Integration/Engine/IntegrityCheckTest.php

View source
<?php

namespace Drupal\Tests\rules\Unit\Integration\Engine;

// cspell:ignore näme
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\rules\Context\ContextConfig;
use Drupal\rules\Context\ContextDefinition;
use Drupal\rules\Engine\RulesComponent;
use Drupal\Tests\rules\Unit\Integration\RulesEntityIntegrationTestBase;

/**
 * Test the integrity check functionality during configuration time.
 *
 * @group Rules
 */
class IntegrityCheckTest extends RulesEntityIntegrationTestBase {
    
    /**
     * Tests that the integrity check can be invoked.
     */
    public function testIntegrityCheck() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        $rule->addAction('rules_entity_save', ContextConfig::create()->map('entity', 'entity'));
        $violation_list = RulesComponent::create($rule)->addContextDefinition('entity', ContextDefinition::create('entity'))
            ->checkIntegrity();
        $this->assertCount(0, $violation_list);
    }
    
    /**
     * Tests that a wrongly configured variable name triggers a violation.
     */
    public function testUnknownVariable() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        $action = $this->rulesExpressionManager
            ->createAction('rules_entity_save', ContextConfig::create()->map('entity', 'unknown_variable'));
        $rule->addExpressionObject($action);
        $violation_list = RulesComponent::create($rule)->checkIntegrity();
        $this->assertCount(1, $violation_list);
        $violation = $violation_list[0];
        // The Exception message part of the output should be HTML-escaped.
        $this->assertEquals("Data selector <em class=\"placeholder\">unknown_variable</em> for context <em class=\"placeholder\">Entity</em> is invalid. Unable to get variable &#039;unknown_variable&#039;; it is not defined.", (string) $violation->getMessage());
        $this->assertEquals($action->getUuid(), $violation->getUuid());
    }
    
    /**
     * Tests that the integrity check with UUID works.
     */
    public function testCheckUuid() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        // Just use a rule with 2 dummy actions.
        $rule->addAction('rules_entity_save', ContextConfig::create()->map('entity', 'unknown_variable_1'));
        $second_action = $this->rulesExpressionManager
            ->createAction('rules_entity_save', ContextConfig::create()->map('entity', 'unknown_variable_2'));
        $rule->addExpressionObject($second_action);
        $all_violations = RulesComponent::create($rule)->addContextDefinition('entity', ContextDefinition::create('entity'))
            ->checkIntegrity();
        $this->assertCount(2, $all_violations);
        $uuid_violations = $all_violations->getFor($second_action->getUuid());
        $this->assertCount(1, $uuid_violations);
        $violation = $uuid_violations[0];
        // The Exception message part of the output should be HTML-escaped.
        $this->assertEquals("Data selector <em class=\"placeholder\">unknown_variable_2</em> for context <em class=\"placeholder\">Entity</em> is invalid. Unable to get variable &#039;unknown_variable_2&#039;; it is not defined.", (string) $violation->getMessage());
        $this->assertEquals($second_action->getUuid(), $violation->getUuid());
    }
    
    /**
     * Tests that an invalid condition plugin ID results in a violation.
     */
    public function testInvalidCondition() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        $condition = $this->rulesExpressionManager
            ->createCondition('invalid_condition_id');
        $rule->addExpressionObject($condition);
        $violations = RulesComponent::create($rule)->checkIntegrity();
        $this->assertCount(1, $violations);
        $this->assertEquals('Condition plugin <em class="placeholder">invalid_condition_id</em> does not exist', (string) $violations[0]->getMessage());
        $this->assertEquals($condition->getUuid(), $violations[0]->getUuid());
    }
    
    /**
     * Tests that a missing condition plugin ID results in a violation.
     */
    public function testMissingCondition() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        $condition = $this->rulesExpressionManager
            ->createCondition('');
        $rule->addExpressionObject($condition);
        $violations = RulesComponent::create($rule)->checkIntegrity();
        $this->assertCount(1, $violations);
        $this->assertEquals('Condition plugin ID is missing', (string) $violations[0]->getMessage());
        $this->assertEquals($condition->getUuid(), $violations[0]->getUuid());
    }
    
    /**
     * Tests that an invalid action plugin ID results in a violation.
     */
    public function testInvalidAction() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        $action = $this->rulesExpressionManager
            ->createAction('invalid_action_id');
        $rule->addExpressionObject($action);
        $violations = RulesComponent::create($rule)->checkIntegrity();
        $this->assertCount(1, $violations);
        $this->assertEquals('Action plugin <em class="placeholder">invalid_action_id</em> does not exist', (string) $violations[0]->getMessage());
        $this->assertEquals($action->getUuid(), $violations[0]->getUuid());
    }
    
    /**
     * Tests that a missing action plugin ID results in a violation.
     */
    public function testMissingAction() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        $action = $this->rulesExpressionManager
            ->createAction('');
        $rule->addExpressionObject($action);
        $violations = RulesComponent::create($rule)->checkIntegrity();
        $this->assertCount(1, $violations);
        $this->assertEquals('Action plugin ID is missing', (string) $violations[0]->getMessage());
        $this->assertEquals($action->getUuid(), $violations[0]->getUuid());
    }
    
    /**
     * Tests invalid characters in provided variables.
     */
    public function testInvalidProvidedName() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        // The condition provides a "provided_text" variable.
        $condition = $this->rulesExpressionManager
            ->createCondition('rules_test_provider', ContextConfig::create()->provideAs('provided_text', 'invalid_näme'));
        $rule->addExpressionObject($condition);
        $violation_list = RulesComponent::create($rule)->checkIntegrity();
        $this->assertCount(1, $violation_list);
        $this->assertEquals('Provided variable name <em class="placeholder">invalid_näme</em> contains not allowed characters.', (string) $violation_list[0]->getMessage());
        $this->assertEquals($condition->getUuid(), $violation_list[0]->getUuid());
    }
    
    /**
     * Tests the input restriction on contexts.
     */
    public function testInputRestriction() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        $action = $this->rulesExpressionManager
            ->createAction('rules_entity_fetch_by_id', ContextConfig::create()->map('type', 'variable_1')
            ->setValue('entity_id', 1));
        $rule->addExpressionObject($action);
        $violation_list = RulesComponent::create($rule)->addContextDefinition('variable_1', ContextDefinition::create('string'))
            ->checkIntegrity();
        $this->assertCount(1, $violation_list);
        $this->assertEquals('The context <em class="placeholder">Entity type</em> may not be configured using a selector.', (string) $violation_list[0]->getMessage());
        $this->assertEquals($action->getUuid(), $violation_list[0]->getUuid());
    }
    
    /**
     * Tests the data selector restriction on contexts.
     */
    public function testSelectorRestriction() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        $action = $this->rulesExpressionManager
            ->createAction('rules_data_set', ContextConfig::create()->setValue('data', 'some value')
            ->setValue('value', 'some new value'));
        $rule->addExpressionObject($action);
        $violation_list = RulesComponent::create($rule)->checkIntegrity();
        $this->assertCount(1, $violation_list);
        $this->assertEquals('The context <em class="placeholder">Data</em> may only be configured using a selector.', (string) $violation_list[0]->getMessage());
        $this->assertEquals($action->getUuid(), $violation_list[0]->getUuid());
    }
    
    /**
     * Tests that a primitive context is assigned something that matches.
     */
    public function testPrimitiveTypeViolation() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        // The condition expects a string but we pass a list, which will trigger the
        // violation.
        $condition = $this->rulesExpressionManager
            ->createCondition('rules_test_string_condition', ContextConfig::create()->map('text', 'list_variable'));
        $rule->addExpressionObject($condition);
        $violation_list = RulesComponent::create($rule)->addContextDefinition('list_variable', ContextDefinition::create('list'))
            ->checkIntegrity();
        $this->assertCount(1, $violation_list);
        $this->assertEquals('Expected a string data type for context <em class="placeholder">Text to compare</em> but got a list data type instead.', (string) $violation_list[0]->getMessage());
        $this->assertEquals($condition->getUuid(), $violation_list[0]->getUuid());
    }
    
    /**
     * Tests that a list context is assigned something that matches.
     */
    public function testListTypeViolation() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        // The condition expects a list for the type context but we pass a node
        // which will trigger the violation.
        $condition = $this->rulesExpressionManager
            ->createCondition('rules_node_is_of_type', ContextConfig::create()->map('node', 'node')
            ->map('types', 'node'));
        $rule->addExpressionObject($condition);
        $violation_list = RulesComponent::create($rule)->addContextDefinition('node', ContextDefinition::create('entity:node'))
            ->checkIntegrity();
        $this->assertCount(1, $violation_list);
        $this->assertEquals('Expected a list data type for context <em class="placeholder">Content types</em> but got a entity:node data type instead.', (string) $violation_list[0]->getMessage());
        $this->assertEquals($condition->getUuid(), $violation_list[0]->getUuid());
    }
    
    /**
     * Tests that a complex data context is assigned something that matches.
     */
    public function testComplexTypeViolation() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        // The condition expects a node context but gets a list instead which cause
        // the violation.
        $condition = $this->rulesExpressionManager
            ->createCondition('rules_node_is_of_type', ContextConfig::create()->map('node', 'list_variable')
            ->map('types', 'list_variable'));
        $rule->addExpressionObject($condition);
        $violation_list = RulesComponent::create($rule)->addContextDefinition('list_variable', ContextDefinition::create('list'))
            ->checkIntegrity();
        $this->assertCount(1, $violation_list);
        $this->assertEquals('Expected a entity:node data type for context <em class="placeholder">Node</em> but got a list data type instead.', (string) $violation_list[0]->getMessage());
        $this->assertEquals($condition->getUuid(), $violation_list[0]->getUuid());
    }
    
    /**
     * Tests that an absent required context triggers a violation.
     */
    public function testMissingRequiredContext() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        // The condition is completely un-configured, missing 2 required contexts.
        $condition = $this->rulesExpressionManager
            ->createCondition('rules_node_is_of_type');
        $rule->addExpressionObject($condition);
        $violation_list = RulesComponent::create($rule)->checkIntegrity();
        $this->assertCount(2, $violation_list);
        $this->assertEquals('The required context <em class="placeholder">Node</em> is missing.', (string) $violation_list[0]->getMessage());
        $this->assertEquals('The required context <em class="placeholder">Content types</em> is missing.', (string) $violation_list[1]->getMessage());
        $this->assertEquals($condition->getUuid(), $violation_list[0]->getUuid());
        $this->assertEquals($condition->getUuid(), $violation_list[1]->getUuid());
    }
    
    /**
     * Make sure that nested expression violations have the correct UUID.
     */
    public function testNestedExpressionUuids() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        $action_set = $this->rulesExpressionManager
            ->createInstance('rules_action_set');
        // The most inner action will trigger a violation for an unknown variable.
        $action = $this->rulesExpressionManager
            ->createAction('rules_entity_save', ContextConfig::create()->map('entity', 'unknown_variable'));
        $action_set->addExpressionObject($action);
        $rule->addExpressionObject($action_set);
        $violation_list = RulesComponent::create($rule)->checkIntegrity();
        $this->assertCount(1, $violation_list);
        // UUID must be that of the most inner action.
        $this->assertEquals($action->getUuid(), $violation_list[0]->getUuid());
    }
    
    /**
     * Tests using provided variables in sub-sequent actions passes checks.
     */
    public function testUsingProvidedVariables() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        $rule->addAction('rules_variable_add', ContextConfig::create()->setValue('type', 'any')
            ->setValue('value', 'foo'));
        $rule->addAction('rules_variable_add', ContextConfig::create()->setValue('type', 'any')
            ->map('value', 'variable_added'));
        $violation_list = RulesComponent::create($rule)->checkIntegrity();
        $this->assertCount(0, $violation_list);
    }
    
    /**
     * Tests that refined context is respected when checking context.
     */
    public function testRefinedContextViolation() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        $rule->addAction('rules_variable_add', ContextConfig::create()->setValue('type', 'integer')
            ->map('value', 'text'));
        $violation_list = RulesComponent::create($rule)->addContextDefinition('text', ContextDefinition::create('string'))
            ->checkIntegrity();
        $this->assertCount(1, $violation_list);
    }
    
    /**
     * Tests context can be refined based upon mapped context.
     */
    public function testRefiningContextBasedOnMappedContext() {
        // DataComparison condition refines context based on selected data. Thus
        // it for the test and ensure checking integrity passes when the comparison
        // value is of a compatible type and fails else.
        $rule = $this->rulesExpressionManager
            ->createRule();
        $rule->addCondition('rules_data_comparison', ContextConfig::create()->map('data', 'text')
            ->map('value', 'text2'));
        $violation_list = RulesComponent::create($rule)->addContextDefinition('text', ContextDefinition::create('string'))
            ->addContextDefinition('text2', ContextDefinition::create('string'))
            ->checkIntegrity();
        $this->assertCount(0, $violation_list);
        $violation_list = RulesComponent::create($rule)->addContextDefinition('text', ContextDefinition::create('string'))
            ->addContextDefinition('text2', ContextDefinition::create('integer'))
            ->checkIntegrity();
        $this->assertCount(1, $violation_list);
    }
    
    /**
     * Tests using provided variables with refined context.
     */
    public function testUsingRefinedProvidedVariables() {
        $rule = $this->rulesExpressionManager
            ->createRule();
        $rule->addAction('rules_variable_add', ContextConfig::create()->setValue('type', 'string')
            ->setValue('value', 'foo'));
        $rule->addAction('rules_system_message', ContextConfig::create()->map('message', 'variable_added')
            ->setValue('type', MessengerInterface::TYPE_STATUS));
        // The message action requires a string, thus if the context is not refined
        // it will end up as "any" and integrity check would fail.
        $violation_list = RulesComponent::create($rule)->checkIntegrity();
        $this->assertCount(0, $violation_list);
    }

}

Classes

Title Deprecated Summary
IntegrityCheckTest Test the integrity check functionality during configuration time.