[ 'name' => 'type', 'class' => 'CSegmentedRadioElement', 'selector' => 'xpath:./ul[contains(@class, "radio-list-control")]'. '|./ul/li/ul[contains(@class, "radio-list-control")]|./div/ul[contains(@class, "radio-list-control")]' ], 'Interval' => [ 'name' => 'delay', 'class' => 'CElement', 'selector' => 'xpath:./input[@name][not(@type) or @type="text" or @type="password"][not(@style) or '. 'not(contains(@style,"display: none"))]|./textarea[@name]' ], 'Period' => [ 'name' => 'period', 'class' => 'CElement', 'selector' => 'xpath:./input[@name][not(@type) or @type="text" or @type="password"][not(@style) or '. 'not(contains(@style,"display: none"))]|./textarea[@name]' ] ]; public static $created_id; public static $old_hash; /** * Attach Behaviors to the test. * * @return array */ public function getBehaviors() { return ['class' => CMessageBehavior::class]; } /* * Overrides data for LLD creation. */ public static function getCreateData() { return [ // #0. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => '' ], 'error' => 'Incorrect value for field "Name": cannot be empty.' ] ] ] ], // #1. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override without actions' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Item prototype' ] ] ], 'error' => 'At least one action is mandatory.' ] ] ] ], // #2. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override without actions' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Trigger prototype' ] ] ], 'error' => 'At least one action is mandatory.' ] ] ] ], // #3. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override without actions' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Graph prototype' ] ] ], 'error' => 'At least one action is mandatory.' ] ] ] ], // #4. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override without actions' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Host prototype' ] ] ], 'error' => 'At least one action is mandatory.' ] ] ] ], // #5. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with empty tags in trigger prototype' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Trigger prototype', 'Tags' => [] ] ] ], 'error' => 'Incorrect value for field "Tags": cannot be empty.' ] ] ] ], // #6. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with empty tag name in trigger prototype' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Trigger prototype', 'Tags' => [ ['tag' => '', 'value' => 'value1'] ] ] ] ], 'error' => 'Incorrect value for field "Tag": cannot be empty.' ] ] ] ], // #7. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with empty template' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Host prototype', 'Link templates' => [] ] ] ], 'error' => 'Incorrect value for field "Link templates": cannot be empty.' ] ] ] ], // #8. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with empty tag name in host prototype' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Host prototype', 'Tags' => [ ['tag' => '', 'value' => 'value1'] ] ] ] ], 'error' => 'Incorrect value for field "Tag": cannot be empty.' ] ] ] ], // #9. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with empty tags in host prototype' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Host prototype', 'Tags' => [] ] ] ], 'error' => 'Incorrect value for field "Tags": cannot be empty.' ] ] ] ], // #10. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with empty delay' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Item prototype' ], 'Update interval' => [ 'Delay' => '' ] ] ], 'error' => 'Incorrect value for field "Update interval": invalid delay.' ] ] ] ], // #11. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with zero delay' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Item prototype' ], 'Update interval' => [ 'Delay' => '0' ] ] ], 'error' => 'Item will not be refreshed. Specified update interval requires '. 'having at least one either flexible or scheduling interval.' ] ] ] ], // #12. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with 2 days delay' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Item prototype' ], 'Update interval' => [ 'Delay' => '2d' ] ] ], 'error' => 'Item will not be refreshed. Update interval should be between 1s and 1d. '. 'Also Scheduled/Flexible intervals can be used.' ] ] ] ], // #13. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with empty interval' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Item prototype' ], 'Update interval' => [ 'Delay' => '50m', 'Custom intervals' => [ [ 'action' => USER_ACTION_ADD, 'type' => 'Flexible', 'delay' => '', 'period' => '1-5,01:01-13:05' ] ] ] ] ], 'error' => 'Invalid interval "".' ] ] ] ], // #14. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with empty period' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Item prototype' ], 'Update interval' => [ 'Delay' => '50m', 'Custom intervals' => [ [ 'action' => USER_ACTION_ADD, 'type' => 'Flexible', 'delay' => '20s', 'period' => '' ] ] ] ] ], 'error' => 'Invalid interval "".' ] ] ] ], // #15. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with wrong period' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Item prototype' ], 'Update interval' => [ 'Delay' => '50m', 'Custom intervals' => [ [ 'action' => USER_ACTION_ADD, 'type' => 'Flexible', 'delay' => '20s', 'period' => '1-2' ] ] ] ] ], 'error' => 'Invalid interval "1-2".' ] ] ] ], // #16. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with wrong scheduling interval' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Item prototype' ], 'Update interval' => [ 'Delay' => '50m', 'Custom intervals' => [ [ 'action' => USER_ACTION_ADD, 'type' => 'Scheduling', 'delay' => 'wd1-9' ] ] ] ] ], 'error' => 'Invalid interval "wd1-9".' ] ] ] ], // #17. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with empty history' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Item prototype', 'History storage period' => [ 'ophistory_history_mode' => 'Storage period', 'ophistory_history' => '' ] ] ] ], 'error' => 'Incorrect value for field "History storage period": a time unit is expected.' ] ] ] ], // #18. [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override with empty history' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Item prototype', 'Trend storage period' => [ 'optrends_trends_mode' => 'Storage period', 'optrends_trends' => '' ] ] ] ], 'error' => 'Incorrect value for field "Trend storage period": a time unit is expected.' ] ] ] ], // #19. [ [ 'expected' => TEST_GOOD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Minimal override' ] ] ] ] ], // #20. [ [ 'expected' => TEST_GOOD, 'overrides' => [ [ 'fields' => [ 'Name' => 'Override_1', 'If filter matches' => 'Stop processing' ], 'Filters' => [ 'Type of calculation' => 'Custom expression', 'formula' => '(A and B) or (C and D)', 'filter_conditions' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'macro' => '{#MACRO1}', 'operator' => 'does not match', 'value' => 'expression_1' ], [ 'macro' => '{#MACRO2}', 'operator' => 'matches', 'value' => 'expression_2' ], [ 'macro' => '{#MACRO3}', 'operator' => 'exists' ], [ 'macro' => '{#MACRO4}', 'operator' => 'does not exist' ] ] ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Item prototype', 'Condition' => ['operator' => 'does not match', 'value' => 'item_pattern'], 'Create enabled' => 'No', 'Discover' => 'No', 'History storage period' => [ 'ophistory_history_mode' => 'Storage period', 'ophistory_history' => '500d' ], 'Trend storage period' => [ 'optrends_trends_mode' => 'Storage period', 'optrends_trends' => '200d' ] ], 'Update interval' => [ 'Delay' => '50m', 'Custom intervals' => [ ['type' => 'Flexible', 'delay' => '60s', 'period' => '1-5,01:01-13:05'], ['type' => 'Scheduling', 'delay' => 'wd1-3h10-17'] ] ] ], [ 'fields' => [ 'Object' => 'Trigger prototype', 'Condition' => ['operator' => 'contains', 'value' => 'trigger_Pattern'], 'Create enabled' => 'No', 'Discover' => 'No', 'Severity' => 'Warning', 'Tags' => [ ['tag' => 'tag1', 'value' => 'value1'], ['tag' => 'tag2', 'value' => 'value2'] ] ] ], [ 'fields' => [ 'Object' => 'Graph prototype', 'Condition' => ['operator' => 'matches', 'value' => 'Graph_Pattern'], 'Discover' => 'Yes' ] ], [ 'fields' => [ 'Object' => 'Host prototype', 'Condition' => ['operator' => 'does not match', 'value' => 'Host_Pattern'], 'Create enabled' => 'Yes', 'Discover' => 'Yes', 'Link templates' => 'Test Item Template', 'Host inventory' => 'Disabled' ] ] ] ], [ 'fields' => [ 'Name' => 'Override_2', 'If filter matches' => 'Continue overrides' ], 'Operations' => [ [ 'fields' => [ 'Object' => 'Graph prototype', 'Condition' => ['operator' => 'matches', 'value' => '2Graph_Pattern'], 'Discover' => 'No' ] ], [ 'fields' => [ 'Object' => 'Host prototype', 'Condition' => ['operator' => 'does not match', 'value' => '2Host_Pattern'], 'Create enabled' => 'Yes', 'Discover' => 'No', 'Link templates' => 'Test Item Template', 'Host inventory' => 'Automatic' ] ] ] ] ], 'screenshot' => true ] ] ]; } /** * @dataProvider getCreateData */ public function testFormLowLevelDiscoveryOverrides_Create($data) { $this->overridesCreate($data); } private function overridesCreate($data) { $this->page->login()->open('host_discovery.php?form=create&context=host&hostid='.self::HOST_ID); $form = $this->query('name:itemForm')->waitUntilPresent()->asForm()->one(); $key = 'lld_override'.time(); $form->fill([ 'Name' => 'LLD with overrides', 'Key' => $key ]); $form->selectTab('Overrides'); $form->invalidate(); $override_container = $form->getField('Overrides')->asTable(); // Add overrides from data to lld rule. foreach($data['overrides'] as $i => $override){ $override_container->query('button:Add')->one()->click(); $override_overlay = $this->query('id:lldoverride_form')->waitUntilPresent()->asCheckboxForm()->one(); // Fill Override name and what to do if Filter matches. if (array_key_exists('fields', $override)) { $override_overlay->fill($override['fields']); } $this->fillOverrideFilter($override); $this->fillOverrideOperations($data, $override); $this->checkSubmittedOverlay($data['expected'], $override_overlay, CTestArrayHelper::get($override, 'error')); if (CTestArrayHelper::get($data, 'expected') === TEST_GOOD) { // Check that Override with correct name was added to Overrides table. $this->assertEquals(CTestArrayHelper::get($override, 'fields.Name'), $override_container->getRow($i)->getColumn('Name')->getText() ); // Check that Override in table has correct processing status. $stop_processing = (CTestArrayHelper::get($override, 'fields.If filter matches') === 'Stop processing') ? 'Yes' : 'No'; $this->assertEquals($stop_processing, $override_container->getRow($i)->getColumn('Stop processing')->getText() ); } } // Take a screenshot to test draggable object position for overrides. if (array_key_exists('screenshot', $data)) { $this->page->removeFocus(); $this->assertScreenshot($this->query('class:lld-overrides-table')->one(), 'LLD override'); } if (CTestArrayHelper::get($data, 'expected') === TEST_GOOD) { // Submit LLD create. $form->submit(); $this->assertMessage(TEST_GOOD, 'Discovery rule created'); self::$created_id = CDBHelper::getValue('SELECT itemid FROM items WHERE key_='.zbx_dbstr($key)); } $this->checkSavedState($data); } /* * Overrides data for LLD update. */ public static function getUpdateData() { return [ [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'fields' => [ 'Name' => '' ], 'error' => 'Incorrect value for field "Name": cannot be empty.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_ADD, 'fields' => [ 'Name' => '' ], 'error' => 'Incorrect value for field "Name": cannot be empty.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_ADD, 'fields' => [ 'Name' => 'Override for update 1' ], 'error' => 'Override with name "Override for update 1" already exists.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_ADD, 'fields' => [ 'Object' => 'Item prototype' ] ] ], 'error' => 'At least one action is mandatory.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_ADD, 'fields' => [ 'Object' => 'Trigger prototype' ] ] ], 'error' => 'At least one action is mandatory.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_ADD, 'fields' => [ 'Object' => 'Graph prototype' ] ] ], 'error' => 'At least one action is mandatory.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_ADD, 'fields' => [ 'Object' => 'Host prototype' ] ] ], 'error' => 'At least one action is mandatory.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'fields' => [ 'Object' => 'Item prototype', 'Create enabled' => null, 'Discover' => null, 'History storage period' => null, 'Trend storage period' => null ], 'Update interval' => null ] ], 'error' => 'At least one action is mandatory.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'fields' => [ 'Object' => 'Trigger prototype', 'Create enabled' => null, 'Discover' => null, 'Severity' => null, 'Tags' => null ] ] ], 'error' => 'At least one action is mandatory.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 2', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'fields' => [ 'Object' => 'Graph prototype', 'Discover' => null ] ] ], 'error' => 'At least one action is mandatory.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 2', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'fields' => [ 'Object' => 'Host prototype', 'Create enabled' => null, 'Discover' => null, 'Link templates' => null, 'Tags' => null, 'Host inventory' => null ] ] ], 'error' => 'At least one action is mandatory.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'fields' => [ 'Object' => 'Trigger prototype', 'Tags' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'tag' => '', 'value' => '' ] ] ] ] ], 'error' => 'Incorrect value for field "Tags": cannot be empty.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'fields' => [ 'Object' => 'Trigger prototype', 'Tags' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'tag' => '', 'value' => 'value1' ] ] ] ] ], 'error' => 'Incorrect value for field "Tag": cannot be empty.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 2', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'fields' => [ 'Object' => 'Host prototype', 'Link templates' => [] ] ] ], 'error' => 'Incorrect value for field "Link templates": cannot be empty.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 2', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'fields' => [ 'Object' => 'Host prototype', 'Tags' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'tag' => '', 'value' => '' ], [ 'action' => USER_ACTION_REMOVE, 'index' => 1 ] ] ] ] ], 'error' => 'Incorrect value for field "Tags": cannot be empty.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 2', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'fields' => [ 'Object' => 'Host prototype', 'Tags' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'tag' => '', 'value' => 'new_value' ] ] ] ] ], 'error' => 'Incorrect value for field "Tag": cannot be empty.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 2', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'fields' => [ 'Object' => 'Host prototype', 'Tags' => [ [ 'action' => USER_ACTION_REMOVE, 'index' => 1 ], [ 'action' => USER_ACTION_REMOVE, 'index' => 0 ] ] ] ] ], 'error' => 'Incorrect value for field "Tags": cannot be empty.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'Update interval' => [ 'Delay' => '' ] ] ], 'error' => 'Incorrect value for field "Update interval": invalid delay.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'Update interval' => [ 'Delay' => '0', 'Custom intervals' => [ [ 'action' => USER_ACTION_REMOVE, 'index' => 1 ], [ 'action' => USER_ACTION_REMOVE, 'index' => 0 ] ] ] ] ], 'error' => 'Item will not be refreshed. Specified update interval requires '. 'having at least one either flexible or scheduling interval.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'Update interval' => [ 'Delay' => '2d', 'Custom intervals' => [ [ 'action' => USER_ACTION_REMOVE, 'index' => 1 ], [ 'action' => USER_ACTION_REMOVE, 'index' => 0 ] ] ] ] ], 'error' => 'Item will not be refreshed. Update interval should be between 1s and 1d. '. 'Also Scheduled/Flexible intervals can be used.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'Update interval' => [ 'Custom intervals' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'delay' => '' ] ] ] ] ], 'error' => 'Invalid interval "".' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'Update interval' => [ 'Custom intervals' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'period' => '' ] ] ] ] ], 'error' => 'Invalid interval "".' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'Update interval' => [ 'Custom intervals' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'period' => '1-2' ] ] ] ] ], 'error' => 'Invalid interval "1-2".' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'Update interval' => [ 'Custom intervals' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'delay' => 'wd1-9' ] ] ] ] ], 'error' => 'Invalid interval "wd1-9".' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'fields' => [ 'History storage period' => [ 'ophistory_history_mode' => 'Storage period', 'ophistory_history' => '' ] ] ] ], 'error' => 'Incorrect value for field "History storage period": a time unit is expected.' ] ] ] ], [ [ 'expected' => TEST_BAD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'fields' => [ 'Trend storage period' => [ 'optrends_trends_mode' => 'Storage period', 'optrends_trends' => '' ] ] ] ], 'error' => 'Incorrect value for field "Trend storage period": a time unit is expected.' ] ] ] ], [ [ 'expected' => TEST_GOOD, 'overrides' => [ [ 'action' => USER_ACTION_REMOVE, 'name' => 'Override for update 1' ], [ 'action' => USER_ACTION_REMOVE, 'name' => 'Override for update 2' ] ] ] ], [ [ 'expected' => TEST_GOOD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Filters' => [ 'formula' => 'A and B and C and D and E', 'filter_conditions' => [ [ 'action' => USER_ACTION_ADD, 'macro' => '{#UPDATED_MACRO3}', 'operator' => 'does not match', 'value' => 'ADDED expression_3' ] ] ], 'Operations' => [ [ 'action' => USER_ACTION_ADD, 'fields' => [ 'Object' => 'Host prototype', 'Condition' => ['operator' => 'contains', 'value' => 'new host pattern'], 'Create enabled' => 'No', 'Discover' => 'Yes', 'Link templates' => 'Test Item Template', 'Tags' => [ ['tag' => 'added_name1', 'value' => 'added_value1'] ], 'Host inventory' => 'Disabled' ] ] ] ] ] ] ], [ [ 'expected' => TEST_GOOD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'fields' => [ 'Name' => 'Updated Override for update 1', 'If filter matches' => 'Stop processing' ], 'Filters' => [ 'Type of calculation' => 'Custom expression', 'formula' => 'A and B and C and D', 'filter_conditions' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'macro' => '{#UPDATED_MACRO1}', 'operator' => 'does not match', 'value' => 'UPDATED expression_1' ], [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'macro' => '{#UPDATED_MACRO2}', 'operator' => 'matches', 'value' => 'UPDATED expression_2' ], [ 'action' => USER_ACTION_UPDATE, 'index' => 2, 'macro' => '{#UPDATED_MACRO3}', 'operator' => 'does not exist' ], [ 'action' => USER_ACTION_UPDATE, 'index' => 3, 'macro' => '{#UPDATED_MACRO4}', 'operator' => 'exists' ] ] ], 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'fields' => [ 'Create enabled' => 'No', 'Discover' => 'No', 'History storage period' => [ 'ophistory_history_mode' => 'Storage period', 'ophistory_history' => '500d' ], 'Trend storage period' => [ 'optrends_trends_mode' => 'Storage period', 'optrends_trends' => '200d' ] ], 'Update interval' => [ 'Delay' => '50m', 'Custom intervals' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'type' => 'Scheduling', 'delay' => 'wd1-3h10-17' ], [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'type' => 'Flexible', 'delay' => '60s', 'period' => '1-5,01:01-13:05' ] ] ] ], [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'fields' => [ 'Create enabled' => 'No', 'Discover' => 'Yes', 'Severity' => null, 'Tags' => null ] ] ] ], [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 2', 'Operations' => [ [ 'action' => USER_ACTION_UPDATE, 'index' => 0, 'fields' => [ 'Discover' => 'No' ] ], [ 'action' => USER_ACTION_UPDATE, 'index' => 1, 'fields' => [ 'Create enabled' => 'No', 'Discover' =>'Yes', 'Link templates' => null, 'Tags' => null, 'Host inventory' => null ] ] ] ] ] ] ], [ [ 'expected' => TEST_GOOD, 'overrides' => [ [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 1', 'Operations' => [ [ 'action' => USER_ACTION_REMOVE, 'index' => 1, 'fields' => [ 'Object' => 'Trigger prototype', 'Condition' => ['operator' => 'does not equal', 'value' => 'test trigger pattern'] ] ] ] ], [ 'action' => USER_ACTION_UPDATE, 'name' => 'Override for update 2', 'Operations' => [ [ 'action' => USER_ACTION_REMOVE, 'index' => 1, 'fields' => [ 'Object' => 'Host prototype', 'Condition' => ['operator' => 'does not match', 'value' => 'test host pattern'] ] ] ] ] ] ] ] ]; } /** * @dataProvider getUpdateData * * @backup items */ public function testFormLowLevelDiscoveryOverrides_Update($data) { $this->overridesUpdate($data); } private function overridesUpdate($data) { self::$old_hash = CDBHelper::getHash('SELECT * FROM items WHERE flags=1 ORDER BY itemid'); $this->page->login()->open('host_discovery.php?form=update&context=host&itemid='.self::UPDATED_ID); $form = $this->query('name:itemForm')->waitUntilPresent()->asForm()->one(); $form->selectTab('Overrides'); $form->invalidate(); $override_container = $form->getField('Overrides')->asTable(); $sources = [ [ 'fields' => [ 'Name' => 'Override for update 1', 'If filter matches' => 'Continue overrides' ], 'Filters' => [ 'Type of calculation' => 'And', 'formula' => 'A and B and C and D', 'filter_conditions' => [ [ 'macro' => '{#MACRO1}', 'operator' => 'matches', 'value' => 'test expression_1' ], [ 'macro' => '{#MACRO2}', 'operator' => 'does not match', 'value' => 'test expression_2' ], [ 'macro' => '{#MACRO3}', 'operator' => 'exists' ], [ 'macro' => '{#MACRO4}', 'operator' => 'does not exist' ] ] ], 'Operations' => [ [ 'Object' => 'Item prototype', 'Condition' => ['operator' => 'equals', 'value' => 'test item pattern'], 'Create enabled' => 'Yes', 'Discover' => 'Yes', // TODO: uncomment after fix DEV-1071 and it is possible to check this field. // 'Update interval' => [ // 'Delay' => '1m', // 'Custom intervals' => [ // ['type' => 'Flexible', 'delay' => '50s', 'period' => '1-7,00:00-24:00'], // ['type' => 'Scheduling', 'delay' => 'wd1-5h9-18'] // ] // ], 'History storage period' => ['ophistory_history_mode' => 'Do not keep history'], 'Trend storage period' => ['optrends_trends_mode' => 'Do not keep trends'] ], [ 'Object' => 'Trigger prototype', 'Condition' => ['operator' => 'does not equal', 'value' => 'test trigger pattern'], 'Create enabled' => null, 'Discover' => null, 'Severity' => 'Warning', 'Tags' => [ ['tag' => 'tag1', 'value' => 'value1'] ] ] ] ], [ 'fields' => [ 'Name' => 'Override for update 2', 'If filter matches' => 'Continue overrides' ], 'Operations' => [ [ 'Object' => 'Graph prototype', 'Condition' => ['operator' => 'matches', 'value' => 'test graph pattern'], 'Discover' => 'Yes' ], [ 'Object' => 'Host prototype', 'Condition' => ['operator' => 'does not match', 'value' => 'test host pattern'], 'Create enabled' => null, 'Discover' => null, 'Link templates' => 'Test Item Template', 'Tags' => [ ['tag' => 'name1', 'value' => 'value1'], ['tag' => 'name2', 'value' => 'value2'] ], 'Host inventory' => 'Automatic' ] ] ] ]; foreach ($data['overrides'] as $i => $override) { $override_action = CTestArrayHelper::get($override, 'action', USER_ACTION_ADD); // Preparing reference data for overrides. switch ($override_action) { case USER_ACTION_ADD: $sources[] = $override; break; case USER_ACTION_REMOVE: foreach ($sources as $id => $source) { if ($source['fields']['Name'] === $override['name']) { unset($sources[$id]); } } break; case USER_ACTION_UPDATE: // Find overrides by name. foreach ($sources as $id => $source) { if ($source['fields']['Name'] === $override['name']) { break; } } $this->assertNotNull($id, 'Cannot find reference data by override name '.$override['name']); // Check if source has fields from data to update them. foreach (CTestArrayHelper::get($override, 'fields', []) as $key => $value) { $this->assertArrayHasKey($key, $sources[$id]['fields'], 'Cannot find field '.$key.' in source'); $sources[$id]['fields'][$key] = $value; } // Preparing reference data for Filter conditions. $conditions = []; foreach (CTestArrayHelper::get($override, 'Filters', []) as $key => $value) { if ($key === 'filter_conditions') { $conditions = $value; continue; } $sources[$id]['Filters'][$key] = $value; } foreach ($conditions as $condition) { switch($condition['action']) { case USER_ACTION_ADD: $sources[$id]['Filters']['filter_conditions'][] = $condition; break; case USER_ACTION_UPDATE: foreach ($condition as $key => $value) { // Skipping 'action' and 'index' fields from reference data. if (in_array($key, ['action', 'index'])) { continue; } $sources[$id]['Filters']['filter_conditions'][$condition['index']][$key] = $value; } break; case USER_ACTION_REMOVE: unset($sources[$id]['Filters']['filter_conditions'][$condition['index']]); break; } } // Open Override overlay. $override_container->query('link', $override['name'])->one()->click(); $override_overlay = $this->query('id:lldoverride_form')->waitUntilPresent()->asCheckboxForm()->one(); // Get Operations Table. $operations_container = $override_overlay->getField('Operations')->asTable(); foreach (CTestArrayHelper::get($override, 'Operations', []) as $j => $operation) { $operation_action = CTestArrayHelper::get($operation, 'action', USER_ACTION_ADD); // Preparing reference data for Operations. switch ($operation_action) { case USER_ACTION_ADD: $temp = $operation['fields']; if (array_key_exists('Update interval', $operation)) { $temp['Update interval'] = $operation['Update interval']; } $sources[$id]['Operations'][] = $temp; break; case USER_ACTION_UPDATE: // Check if source has Operations from data to update them. foreach (CTestArrayHelper::get($operation, 'fields', []) as $key => $value) { // Skipping 'action' and 'index' fields from reference data. if (in_array($key, ['action', 'index'])) { continue; } $this->assertArrayHasKey($key, $sources[$id]['Operations'][$operation['index']], 'Cannot find field '.$key.' in source'); $sources[$id]['Operations'][$operation['index']][$key] = $value; } break; case USER_ACTION_REMOVE: unset($sources[$id]['Operations'][$operation['index']]); break; } } break; } switch ($override_action) { // Perform adding or updating Override. case USER_ACTION_ADD: $override_container->query('button:Add')->one()->click(); $id = null; case USER_ACTION_UPDATE: // Fill Override name and what to do if Filter matches. if (array_key_exists('fields', $override)) { $override_overlay = $this->query('id:lldoverride_form')->waitUntilPresent()->asCheckboxForm()->one(); $override_overlay->fill($override['fields']); } $this->fillOverrideFilter($override); $this->fillOverrideOperations($data, $override, $sources, $id); $this->checkSubmittedOverlay($data['expected'], $override_overlay, CTestArrayHelper::get($override, 'error')); if (CTestArrayHelper::get($data, 'expected') === TEST_GOOD) { // Check that Override with correct name was added to Overrides table. $fields = (CTestArrayHelper::get($override, 'fields')) ? $override['fields']['Name'] : $sources[$i]['fields']['Name']; $this->assertEquals($fields, $override_container->getRow($i)->getColumn('Name')->getText()); // Check that Override in table has correct processing status. $stop_processing = (CTestArrayHelper::get($override, 'fields.If filter matches') === 'Stop processing') ? 'Yes' : 'No'; $this->assertEquals($stop_processing, $override_container->getRow($i)->getColumn('Stop processing') ->getText()); } break; case USER_ACTION_REMOVE: $override_container->findRow('Name', $override['name'])->query('button:Remove')->one()->click(); break; default: throw new Exception('Cannot perform action "'.$override_action.'".'); } } if (CTestArrayHelper::get($data, 'expected') === TEST_GOOD) { // Submit LLD update. $form->submit(); $this->assertMessage(TEST_GOOD, 'Discovery rule updated'); $this->assertEquals(1, CDBHelper::getCount('SELECT NULL FROM items WHERE itemid ='.self::UPDATED_ID)); self::$created_id = self::UPDATED_ID; $this->checkSavedState(['overrides' => $sources]); } } /** * @param array $override override fields from data * * @return CFormElement $override_overlay override or condition form in overlay */ private function fillOverrideFilter($override) { $override_overlay = $this->query('id:lldoverride_form')->waitUntilPresent()->asCheckboxForm()->one(); // Add Filters to override. if (array_key_exists('Filters', $override)) { $filters_table = $override_overlay->query('id:overrides_filters')->asMultifieldTable()->one(); $mapping = $this->setFiltersTableMapping($filters_table); $filters_table->setFieldMapping($mapping)->fill($override['Filters']['filter_conditions']); // Add Type of calculation if there are more then 2 filters. if (array_key_exists('Type of calculation', $override['Filters'])) { $override_overlay->query('name:overrides_evaltype')->waitUntilPresent()->one() ->asDropdown()->fill($override['Filters']['Type of calculation']); // Add formula if Type of calculation is Custom. if (array_key_exists('formula', $override['Filters'])) { $override_overlay->query('id:overrides_formula')->waitUntilPresent()->one() ->fill($override['Filters']['formula']); } } } } /** * @param array $data data provider * @param array $override override fields from data * * @return CFormElement $override_overlay override or condition form in overlay */ private function fillOverrideOperations($data, $override, $sources = null, $id = null) { $override_overlay = $this->query('id:lldoverride_form')->waitUntilPresent()->asCheckboxForm()->one(); $operation_container = $override_overlay->getField('Operations')->asTable(); // Add Operations to override. foreach(CTestArrayHelper::get($override, 'Operations', []) as $i => $operation){ $operation_action = CTestArrayHelper::get($operation, 'action', USER_ACTION_ADD); unset($operation['action']); $row = null; switch ($operation_action) { case USER_ACTION_ADD: $row = $operation_container->getRows()->count() - 1; $operation_container->query('button:Add')->one()->click(); break; case USER_ACTION_UPDATE: $row = $operation['index']; $operation_container->getRow($row)->query('button:Edit')->one()->click(); unset($operation['index']); break; } switch ($operation_action) { case USER_ACTION_ADD: case USER_ACTION_UPDATE: $operation_overlay = $this->query('id:lldoperation_form')->waitUntilPresent()->asCheckboxForm()->one(); if (array_key_exists('fields', $operation)) { $operation_overlay->fill($operation['fields']); } // Fill Delay and Intervals. if (array_key_exists('Update interval', $operation)) { if ($operation['Update interval'] !== null) { $intervals = $operation_overlay->getField('Update interval'); $operation_overlay->query('id:visible_opperiod')->one()->fill(true); if (array_key_exists('Delay', $operation['Update interval'])) { $intervals->query('id:opperiod_delay')->waitUntilVisible()->one() ->fill($operation['Update interval']['Delay']); } if (array_key_exists('Custom intervals', $operation['Update interval'])) { $this->query('xpath:.//table[@id="lld_overrides_custom_intervals"]') ->asMultifieldTable(['mapping' => self::INTERVAL_MAPPING])->one() ->fill($operation['Update interval']['Custom intervals']); } } else { $operation_overlay->query('id:visible_opperiod')->one()->asCheckbox()->fill(false); } } $operation_overlay->submit(); $this->checkSubmittedOverlay($data['expected'], $operation_overlay, CTestArrayHelper::get($override, 'error') ); if (CTestArrayHelper::get($data, 'expected') === TEST_GOOD) { // Check that Operation was added to Operations table. $object = CTestArrayHelper::get($operation, 'fields.Object'); if ($object === null) { $object = $sources[$id]['Operations'][$i]['Object']; } $operator = CTestArrayHelper::get($operation, 'fields.Condition.operator'); if ($operator === null) { $operator = $sources[$id]['Operations'][$i]['Condition']['operator']; } $value = CTestArrayHelper::get($operation, 'fields.Condition.value'); if ($value === null) { $value = $sources[$id]['Operations'][$i]['Condition']['value']; } $condition_text = $object.' '.$operator.' '.$value; $this->assertEquals($condition_text, $operation_container->getRow($row)->getColumn('Condition')->getText() ); } break; case USER_ACTION_REMOVE: $condition_text = $operation['fields']['Object'].' '. $operation['fields']['Condition']['operator'].' '. $operation['fields']['Condition']['value']; $row = $operation_container->findRow('Condition', $condition_text)->query('button:Remove')->one()->click(); $row->waitUntilNotPresent(); break; } } // Submit Override. $override_overlay->submit(); } /** * Function for checking successful/failed overlay submitting. * * @param string $expected case GOOD or BAD * @param CFormElement $overlay override or condition form in overlay * @param string $error error message text */ private function checkSubmittedOverlay($expected, $overlay, $error) { switch ($expected) { case TEST_GOOD: $overlay->waitUntilNotPresent(); break; case TEST_BAD: $this->assertMessage(TEST_BAD, null, $error); break; } } private function checkSavedState($data) { // Skip Bad cases. if (CTestArrayHelper::get($data, 'expected') === TEST_BAD) { return; } // Open saved LLD. $this->page->login()->open('host_discovery.php?form=update&context=host&itemid='.self::$created_id); $form = $this->query('name:itemForm')->waitUntilPresent()->asForm()->one(); $form->selectTab('Overrides'); $override_container = $form->getField('Overrides')->asTable(); // Get Overrides count. $overrides_count = $override_container->getRows()->count(); // Write Override names from data to array. foreach ($data['overrides'] as $override) { $override_names[] = $override['fields']['Name']; $stop_processing[] = (CTestArrayHelper::get($override['fields'], 'If filter matches') === 'Stop processing') ? 'Yes' : 'No'; } // Compare Override names from table with data. for ($k = 0; $k < $overrides_count - 1; $k++) { $this->assertEquals($override_names[$k], $override_container->getRow($k)->getColumn('Name')->getText() ); // Check that Override in table has correct processing status. $this->assertEquals($stop_processing[$k], $override_container->getRow($k)->getColumn('Stop processing')->getText() ); } foreach ($data['overrides'] as $override) { // Open each override dialog. $row = $override_container->findRow('Name', $override['fields']['Name']); $row->query('link', $override['fields']['Name'])->one()->click(); $override_overlay = $this->query('id:lldoverride_form')->waitUntilPresent()->asCheckboxForm()->one(); // Check that Override fields filled with correct data. foreach ($override['fields'] as $field => $value) { $override_overlay->getField($field)->checkValue($value); } if (array_key_exists('Filters', $override)) { foreach ($override['Filters']['filter_conditions'] as &$condition) { unset($condition['index']); unset($condition['action']); } unset($condition); // Check that Filters are filled correctly. $filters_table = $override_overlay->query('id:overrides_filters')->asMultifieldTable()->one(); $mapping = $this->setFiltersTableMapping($filters_table); $filters_table->setFieldMapping($mapping)->checkValue($override['Filters']['filter_conditions']); // Check that Evaluation type is filled correctly. if (array_key_exists('Type of calculation', $override['Filters'])) { $evaluation_type = $override_overlay->query('name:overrides_evaltype')->one()->asDropdown()->getValue(); $this->assertEquals($override['Filters']['Type of calculation'], $evaluation_type); // Check that Formula is filled correctly. if (array_key_exists('formula', $override['Filters'])) { $formula = CTestArrayHelper::get($override['Filters'], 'Type of calculation') !== 'Custom expression' ? $override_overlay->query('id:overrides_expression')->one()->getText() : $override_overlay->query('id:overrides_formula')->one()->getValue(); $this->assertEquals($override['Filters']['formula'], $formula); } } } $operation_container = $override_overlay->getField('Operations')->asTable(); // Get Operations count. $operation_count = $operation_container->getRows()->count(); if (array_key_exists('Operations', $override)) { // Write Conditions from data to array. $condition_text = []; foreach ($override['Operations'] as $operation) { $fields = array_key_exists('fields', $operation) ? $operation['fields'] : $operation; $condition_text[] = $fields['Object'].' '. $fields['Condition']['operator'].' '. $fields['Condition']['value']; } // Compare Conditions from table with data. $actual_conditions = []; for ($n = 0; $n < $operation_count - 1; $n++) { $actual_conditions[] = $operation_container->getRow($n)->getColumn('Condition')->getText(); } $this->assertEquals($condition_text, $actual_conditions); foreach ($override['Operations'] as $i => $operation) { $operation_container->getRow($i)->query('button:Edit')->one()->click(); $operation_overlay = $this->query('id:lldoperation_form')->waitUntilPresent()->asCheckboxForm()->one(); $operation_overlay->checkValue( array_key_exists('fields', $operation) ? $operation['fields'] : $operation ); // Close Operation dialog. $operation_overlay->submit()->waitUntilNotPresent(); } } // Close Override dialog. COverlayDialogElement::find()->one()->close(); } } /** * Function for updating the mapping of the specified Filters multi-field table. * * @param CMultifieldTableElement $filters_table table which mapping needs to be updated * * @return array */ private function setFiltersTableMapping($filters_table) { $mapping = $filters_table->detectFieldMapping(); $mapping['Regular expression']['name'] = 'value'; $mapping['Regular expression']['selector'] = 'xpath:./div/input'; return $mapping; } }