1
|
<?php
|
2
|
|
3
|
/**
|
4
|
* @file
|
5
|
* Rules tests.
|
6
|
*/
|
7
|
|
8
|
class RulesTestCase extends DrupalWebTestCase {
|
9
|
|
10
|
static function getInfo() {
|
11
|
return array(
|
12
|
'name' => 'Rules Engine tests',
|
13
|
'description' => 'Test using the rules API to create and evaluate rules.',
|
14
|
'group' => 'Rules',
|
15
|
);
|
16
|
}
|
17
|
|
18
|
function setUp() {
|
19
|
parent::setUp('rules', 'rules_test');
|
20
|
RulesLog::logger()->clear();
|
21
|
variable_set('rules_debug_log', 1);
|
22
|
}
|
23
|
|
24
|
/**
|
25
|
* Calculates the output of t() given an array of placeholders to replace.
|
26
|
*/
|
27
|
static function t($text, $strings) {
|
28
|
$placeholders = array();
|
29
|
foreach ($strings as $key => $string) {
|
30
|
$key = !is_numeric($key) ? $key : $string;
|
31
|
$placeholders['%' . $key] = drupal_placeholder($string);
|
32
|
}
|
33
|
return strtr($text, $placeholders);
|
34
|
}
|
35
|
|
36
|
protected function createTestRule() {
|
37
|
$rule = rule();
|
38
|
$rule->condition('rules_test_condition_true')
|
39
|
->condition('rules_test_condition_true')
|
40
|
->condition(rules_or()
|
41
|
->condition(rules_condition('rules_test_condition_true')->negate())
|
42
|
->condition('rules_test_condition_false')
|
43
|
->condition(rules_and()
|
44
|
->condition('rules_test_condition_false')
|
45
|
->condition('rules_test_condition_true')
|
46
|
->negate()
|
47
|
)
|
48
|
);
|
49
|
$rule->action('rules_test_action');
|
50
|
return $rule;
|
51
|
}
|
52
|
|
53
|
/**
|
54
|
* Tests creating a rule and iterating over the rule elements.
|
55
|
*/
|
56
|
function testRuleCreation() {
|
57
|
$rule = $this->createTestRule();
|
58
|
$rule->integrityCheck();
|
59
|
$rule->execute();
|
60
|
$log = RulesLog::logger()->get();
|
61
|
$last = array_pop($log);
|
62
|
$last = array_pop($log);
|
63
|
$last = array_pop($log);
|
64
|
$this->assertEqual($last[0], 'action called', 'Action called');
|
65
|
RulesLog::logger()->checkLog();
|
66
|
|
67
|
// Make sure condition and action iterators are working.
|
68
|
$it = new RecursiveIteratorIterator($rule->conditions(), RecursiveIteratorIterator::SELF_FIRST);
|
69
|
$this->assertEqual(iterator_count($it), 8, 'Iterated over all conditions and condition containers');
|
70
|
$it = new RecursiveIteratorIterator($rule->conditions());
|
71
|
$this->assertEqual(iterator_count($it), 6, 'Iterated over all conditions');
|
72
|
$this->assertEqual(iterator_count($rule->actions()), 1, 'Iterated over all actions');
|
73
|
$this->assertEqual(iterator_count($rule->elements()), 10, 'Iterated over all rule elements.');
|
74
|
|
75
|
// Test getting dependencies and the integrity check.
|
76
|
$rule->integrityCheck();
|
77
|
$this->assertTrue($rule->dependencies() === array('rules_test'), 'Dependencies correctly returned.');
|
78
|
}
|
79
|
|
80
|
/**
|
81
|
* Test handling dependencies.
|
82
|
*/
|
83
|
function testdependencies() {
|
84
|
$action = rules_action('rules_node_publish_action');
|
85
|
$this->assertEqual($action->dependencies(), array('rules_test'), 'Providing module is returned as dependency.');
|
86
|
|
87
|
$container = new RulesTestContainer();
|
88
|
$this->assertEqual($container->dependencies(), array('rules_test'), 'Providing module for container plugin is returned as dependency.');
|
89
|
|
90
|
// Test handling unmet dependencies.
|
91
|
$rule = rules_config_load('rules_export_test');
|
92
|
$this->assertTrue(in_array('comment', $rule->dependencies) && !$rule->dirty, 'Dependencies have been imported.');
|
93
|
|
94
|
// Remove the required comment module and make sure the rule is dirty then.
|
95
|
module_disable(array('comment'));
|
96
|
rules_clear_cache();
|
97
|
$rule = rules_config_load('rules_export_test');
|
98
|
$this->assertTrue($rule->dirty, 'Rule has been marked as dirty');
|
99
|
|
100
|
// Now try re-enabling.
|
101
|
module_enable(array('comment'));
|
102
|
rules_clear_cache();
|
103
|
$rule = rules_config_load('rules_export_test');
|
104
|
$this->assertTrue(!$rule->dirty, 'Rule has been marked as not dirty again.');
|
105
|
|
106
|
// Test it with components.
|
107
|
module_enable(array('path'));
|
108
|
$action_set = rules_action_set(array('node' => array('type' => 'node')));
|
109
|
$action_set->action('node_path_alias');
|
110
|
$action_set->save('rules_test_alias');
|
111
|
|
112
|
$rule = rule(array('node' => array('type' => 'node')));
|
113
|
$rule->action('component_rules_test_alias');
|
114
|
$rule->integrityCheck();
|
115
|
$rule->save('rules_test_rule');
|
116
|
|
117
|
$rule = rules_config_load('rules_test_rule');
|
118
|
$component = rules_config_load('rules_test_alias');
|
119
|
$this->assertTrue(in_array('path', $component->dependencies) && !$rule->dirty && !$component->dirty, 'Component has path module dependency.');
|
120
|
|
121
|
// Now disable path module and make sure both configs are marked as dirty.
|
122
|
module_disable(array('path'));
|
123
|
rules_clear_cache();
|
124
|
$rule = rules_config_load('rules_test_rule');
|
125
|
$component = rules_config_load('rules_test_alias');
|
126
|
|
127
|
$this->assertTrue($component->dirty, 'Component has been marked as dirty');
|
128
|
$node = $this->drupalCreateNode();
|
129
|
$result = rules_invoke_component('rules_test_alias', $node);
|
130
|
$this->assertTrue($result === FALSE, 'Unable to execute a dirty component.');
|
131
|
|
132
|
// When the rule is evaluated, the broken component is detected and the
|
133
|
// rule should be marked as dirty too.
|
134
|
$rule->execute($node);
|
135
|
$this->assertTrue($rule->dirty, 'Rule has been marked as dirty');
|
136
|
|
137
|
module_enable(array('path'));
|
138
|
rules_clear_cache();
|
139
|
|
140
|
// Trigger rebuilding the cache, so configs are checked again.
|
141
|
rules_get_cache();
|
142
|
|
143
|
$rule = rules_config_load('rules_test_rule');
|
144
|
$component = rules_config_load('rules_test_alias');
|
145
|
$this->assertTrue(!$component->dirty, 'Component has been marked as not dirty again.');
|
146
|
$this->assertTrue(!$rule->dirty, 'Rule has been marked as not dirty again.');
|
147
|
}
|
148
|
|
149
|
/**
|
150
|
* Test setting up an action with some action_info and serializing and
|
151
|
* executing it.
|
152
|
*/
|
153
|
function testActionSetup() {
|
154
|
$action = rules_action('rules_node_publish_action');
|
155
|
|
156
|
$s = serialize($action);
|
157
|
$action2 = unserialize($s);
|
158
|
$node = (object) array('status' => 0, 'type' => 'page');
|
159
|
$node->title = 'test';
|
160
|
|
161
|
$action2->execute($node);
|
162
|
$this->assertEqual($node->status, 1, 'Action executed correctly');
|
163
|
|
164
|
$this->assertTrue(in_array('node', array_keys($action2->parameterInfo())), 'Parameter info returned.');
|
165
|
|
166
|
$node->status = 0;
|
167
|
$action2->integrityCheck();
|
168
|
$action2->executeByArgs(array('node' => $node));
|
169
|
$this->assertEqual($node->status, 1, 'Action executed correctly');
|
170
|
|
171
|
// Test calling an extended + overriden method.
|
172
|
$this->assertEqual($action2->help(), 'custom', 'Using custom help callback.');
|
173
|
|
174
|
// Inspect the cache
|
175
|
//$this->pass(serialize(rules_get_cache()));
|
176
|
RulesLog::logger()->checkLog();
|
177
|
}
|
178
|
|
179
|
/**
|
180
|
* Test executing with wrong arguments.
|
181
|
*/
|
182
|
function testActionExecutionFails() {
|
183
|
$action = rules_action('rules_node_publish_action');
|
184
|
try {
|
185
|
$action->execute();
|
186
|
$this->fail("Execution hasn't created an exception.");
|
187
|
}
|
188
|
catch (RulesEvaluationException $e) {
|
189
|
$this->pass("RulesEvaluationException was thrown: ". $e);
|
190
|
}
|
191
|
}
|
192
|
|
193
|
/**
|
194
|
* Test setting up a rule and mapping variables.
|
195
|
*/
|
196
|
function testVariableMapping() {
|
197
|
$rule = rule(array(
|
198
|
'node' => array('type' => 'node'),
|
199
|
'node_unchanged' => array('type' => 'node'),
|
200
|
));
|
201
|
$rule->condition(rules_condition('rules_condition_content_is_published')->negate())
|
202
|
->condition('rules_condition_content_is_type', array('type' => array('page', 'story')))
|
203
|
->action('rules_node_publish_action', array('node:select' => 'node_unchanged'));
|
204
|
|
205
|
$node1 = $this->drupalCreateNode(array('status' => 0, 'type' => 'page'));
|
206
|
$node2 = $this->drupalCreateNode(array('status' => 0, 'type' => 'page'));
|
207
|
$rule->integrityCheck();
|
208
|
$rule->execute($node1, $node2);
|
209
|
$this->assertEqual($node2->status, 1, 'Action executed correctly on node2.');
|
210
|
$this->assertEqual($node1->status, 0, 'Action not executed on node1.');
|
211
|
|
212
|
RulesLog::logger()->checkLog();
|
213
|
}
|
214
|
|
215
|
/**
|
216
|
* Tests making use of class based actions.
|
217
|
*/
|
218
|
function testClassBasedActions() {
|
219
|
$cache = rules_get_cache();
|
220
|
$this->assertTrue(!empty($cache['action_info']['rules_test_class_action']), 'Action has been discovered.');
|
221
|
$action = rules_action('rules_test_class_action');
|
222
|
|
223
|
$parameters = $action->parameterInfo();
|
224
|
$this->assertTrue($parameters['node'], 'Action parameter needs a value.');
|
225
|
|
226
|
$node = $this->drupalCreateNode();
|
227
|
$action->execute($node);
|
228
|
$log = RulesLog::logger()->get();
|
229
|
$last = array_pop($log);
|
230
|
$last = array_pop($log);
|
231
|
$this->assertEqual($last[0], 'Action called with node ' . $node->nid, 'Action called');
|
232
|
RulesLog::logger()->checkLog();
|
233
|
}
|
234
|
|
235
|
/**
|
236
|
* Tests CRUD functionality.
|
237
|
*/
|
238
|
function testRulesCRUD() {
|
239
|
$rule = $this->createTestRule();
|
240
|
$rule->integrityCheck()->save('test');
|
241
|
|
242
|
$this->assertEqual(TRUE, $rule->active, 'Rule is active.');
|
243
|
$this->assertEqual(0, $rule->weight, 'Rule weight is zero.');
|
244
|
|
245
|
$results = entity_load('rules_config', array('test'));
|
246
|
$rule2 = array_pop($results);
|
247
|
$this->assertEqual($rule->id, $rule2->id, 'Rule created and loaded');
|
248
|
$this->assertEqual(get_class($rule2), get_class($rule), 'Class properly instantiated.');
|
249
|
$rule2->execute();
|
250
|
// Update.
|
251
|
$rule2->save();
|
252
|
|
253
|
// Make sure all rule elements are still here.
|
254
|
$it = new RecursiveIteratorIterator($rule2->conditions(), RecursiveIteratorIterator::SELF_FIRST);
|
255
|
$this->assertEqual(iterator_count($it), 8, 'Iterated over all conditions and condition containers');
|
256
|
$it = new RecursiveIteratorIterator($rule2->conditions());
|
257
|
$this->assertEqual(iterator_count($it), 6, 'Iterated over all conditions');
|
258
|
$this->assertEqual(iterator_count($rule2->actions()), 1, 'Iterated over all actions');
|
259
|
|
260
|
// Delete.
|
261
|
$rule2->delete();
|
262
|
$this->assertEqual(entity_load('rules_config', FALSE, array('id' => $rule->id)), array(), 'Deleted.');
|
263
|
|
264
|
// Tests CRUD for tags - making sure the tags are stored properly..
|
265
|
$rule = $this->createTestRule();
|
266
|
$tag = $this->randomString();
|
267
|
$rule->tags = array($tag);
|
268
|
$rule->save();
|
269
|
$result = db_select('rules_tags')
|
270
|
->fields('rules_tags', array('tag'))
|
271
|
->condition('id', $rule->id)
|
272
|
->execute();
|
273
|
$this->assertEqual($result->fetchField(), $tag, 'Associated tag has been saved.');
|
274
|
// Try updating.
|
275
|
$rule->tags = array($this->randomName(), $this->randomName());
|
276
|
$rule->integrityCheck()->save();
|
277
|
$result = db_select('rules_tags')
|
278
|
->fields('rules_tags', array('tag'))
|
279
|
->condition('id', $rule->id)
|
280
|
->execute()
|
281
|
->fetchCol();
|
282
|
$this->assertTrue(in_array($rule->tags[0], $result) && in_array($rule->tags[1], $result), 'Updated associated tags.');
|
283
|
// Try loading multiple rules by tags.
|
284
|
$rule2 = $this->createTestRule();
|
285
|
$rule2->tags = array($this->randomName());
|
286
|
$rule2->save();
|
287
|
$loaded = entity_load('rules_config', FALSE, array('tags' => array($rule->tags[0], $rule2->tags[0])));
|
288
|
$this->assertTrue($loaded[$rule->id]->id == $rule->id && $loaded[$rule2->id]->id == $rule2->id, 'Loading configs by tags');
|
289
|
// Try deleting.
|
290
|
$rule->delete();
|
291
|
$result = db_select('rules_tags')
|
292
|
->fields('rules_tags', array('tag'))
|
293
|
->condition('id', $rule->id)
|
294
|
->execute();
|
295
|
$this->assertEqual($result->fetchField(), FALSE, 'Deleted associated tags.');
|
296
|
}
|
297
|
|
298
|
/**
|
299
|
* Test automatic saving of variables.
|
300
|
*/
|
301
|
function testActionSaving() {
|
302
|
// Test saving a parameter.
|
303
|
$action = rules_action('rules_node_publish_action_save');
|
304
|
$node = $this->drupalCreateNode(array('status' => 0, 'type' => 'page'));
|
305
|
$action->executeByArgs(array('node' => $node));
|
306
|
|
307
|
$this->assertEqual($node->status, 1, 'Action executed correctly on node.');
|
308
|
// Sync node_load cache with node_save
|
309
|
entity_get_controller('node')->resetCache();
|
310
|
|
311
|
$node = node_load($node->nid);
|
312
|
$this->assertEqual($node->status, 1, 'Node has been saved.');
|
313
|
|
314
|
// Now test saving a provided variable, which is renamed and modified before
|
315
|
// it is saved.
|
316
|
$title = $this->randomName();
|
317
|
$rule = rule();
|
318
|
$rule->action('entity_create', array(
|
319
|
'type' => 'node',
|
320
|
'param_type' => 'article',
|
321
|
'param_author:select' => 'site:current-user',
|
322
|
'param_title' => $title,
|
323
|
'entity_created:var' => 'node',
|
324
|
));
|
325
|
$rule->action('data_set', array(
|
326
|
'data:select' => 'node:body',
|
327
|
'value' => array('value' => 'body'),
|
328
|
));
|
329
|
$rule->integrityCheck();
|
330
|
$rule->execute();
|
331
|
|
332
|
$node = $this->drupalGetNodeByTitle($title);
|
333
|
$this->assertTrue(!empty($node) && $node->body[LANGUAGE_NONE][0]['value'] == 'body', 'Saved a provided variable');
|
334
|
RulesLog::logger()->checkLog();
|
335
|
}
|
336
|
|
337
|
/**
|
338
|
* Test adding a variable and optional parameters.
|
339
|
*/
|
340
|
function testVariableAdding() {
|
341
|
$node = $this->drupalCreateNode();
|
342
|
$rule = rule(array('nid' => array('type' => 'integer')));
|
343
|
$rule->condition('rules_test_condition_true')
|
344
|
->action('rules_action_load_node')
|
345
|
->action('rules_action_delete_node', array('node:select' => 'node_loaded'))
|
346
|
->execute($node->nid);
|
347
|
|
348
|
$this->assertEqual(FALSE, node_load($node->nid), 'Variable added and skipped optional parameter.');
|
349
|
RulesLog::logger()->checkLog();
|
350
|
|
351
|
$vars = $rule->conditions()->offsetGet(0)->availableVariables();
|
352
|
$this->assertEqual(!isset($vars['node_loaded']), 'Loaded variable is not available to conditions.');
|
353
|
|
354
|
|
355
|
// Test adding a variable with a custom variable name.
|
356
|
$node = $this->drupalCreateNode();
|
357
|
$rule = rule(array('nid' => array('type' => 'integer')));
|
358
|
$rule->action('rules_action_load_node', array('node_loaded:var' => 'node'))
|
359
|
->action('rules_action_delete_node')
|
360
|
->execute($node->nid);
|
361
|
|
362
|
$this->assertEqual(FALSE, node_load($node->nid), 'Variable with custom name added.');
|
363
|
RulesLog::logger()->checkLog();
|
364
|
}
|
365
|
|
366
|
/**
|
367
|
* Test custom access for using component actions/conditions.
|
368
|
*/
|
369
|
function testRuleComponentAccess() {
|
370
|
// Create a normal user.
|
371
|
$normal_user = $this->drupalCreateUser();
|
372
|
// Create a role for granting access to the rule component.
|
373
|
$this->normal_role = $this->drupalCreateRole(array(), 'test_role');
|
374
|
$normal_user->roles[$this->normal_role] = 'test_role';
|
375
|
user_save($normal_user, array('roles' => $normal_user->roles));
|
376
|
// Create an 'action set' rule component making use of a permission.
|
377
|
$action_set = rules_action_set(array('node' => array('type' => 'node')));
|
378
|
$action_set->access_exposed = TRUE;
|
379
|
$action_set->save('rules_test_roles');
|
380
|
|
381
|
// Set the global user to be the current one as access is checked for the
|
382
|
// global user.
|
383
|
global $user;
|
384
|
$user = user_load($normal_user->uid);
|
385
|
$this->assertFalse(rules_action('component_rules_test_roles')->access(), 'Authenticated user without the correct role can\'t use the rule component.');
|
386
|
|
387
|
// Assign the role that will have permissions for the rule component.
|
388
|
user_role_change_permissions($this->normal_role, array('use Rules component rules_test_roles' => TRUE));
|
389
|
$this->assertTrue(rules_action('component_rules_test_roles')->access(), 'Authenticated user with the correct role can use the rule component.');
|
390
|
|
391
|
// Reset global user to anonyous.
|
392
|
$user = user_load(0);
|
393
|
$this->assertFalse(rules_action('component_rules_test_roles')->access(), 'Anonymous user can\'t use the rule component.');
|
394
|
}
|
395
|
|
396
|
/**
|
397
|
* Test passing arguments by reference to an action.
|
398
|
*/
|
399
|
function testPassingByReference() {
|
400
|
// Keeping references of variables is unsupported, though the
|
401
|
// EntityMetadataArrayObject may be used to achieve that.
|
402
|
$array = array('foo' => 'bar');
|
403
|
$data = new EntityMetadataArrayObject($array);
|
404
|
rules_action('rules_action_test_reference')->execute($data);
|
405
|
$this->assertTrue($data['changed'], 'Parameter has been passed by reference');
|
406
|
}
|
407
|
|
408
|
/**
|
409
|
* Test sorting rule elements.
|
410
|
*/
|
411
|
function testSorting() {
|
412
|
$rule = $this->createTestRule();
|
413
|
$conditions = $rule->conditions();
|
414
|
$conditions[0]->weight = 10;
|
415
|
$conditions[2]->weight = 10;
|
416
|
$id[0] = $conditions[0]->elementId();
|
417
|
$id[1] = $conditions[1]->elementId();
|
418
|
$id[2] = $conditions[2]->elementId();
|
419
|
// For testing use a deep sort, even if not necessary here.
|
420
|
$rule->sortChildren(TRUE);
|
421
|
$conditions = $rule->conditions();
|
422
|
$this->assertEqual($conditions[0]->elementId(), $id[1], 'Condition sorted correctly.');
|
423
|
$this->assertEqual($conditions[1]->elementId(), $id[0], 'Condition sorted correctly.');
|
424
|
$this->assertEqual($conditions[2]->elementId(), $id[2], 'Condition sorted correctly.');
|
425
|
}
|
426
|
|
427
|
/**
|
428
|
* Tests using data selectors.
|
429
|
*/
|
430
|
function testDataSelectors() {
|
431
|
$body[LANGUAGE_NONE][0] = array('value' => '<b>The body & nothing.</b>');
|
432
|
$node = $this->drupalCreateNode(array('body' => $body, 'type' => 'page', 'summary' => ''));
|
433
|
|
434
|
$rule = rule(array('nid' => array('type' => 'integer')));
|
435
|
$rule->action('rules_action_load_node')
|
436
|
->action('drupal_message', array('message:select' => 'node_loaded:body:value'))
|
437
|
->execute($node->nid);
|
438
|
|
439
|
RulesLog::logger()->checkLog();
|
440
|
$msg = drupal_get_messages('status');
|
441
|
$last_msg = array_pop($msg['status']);
|
442
|
$wrapper = entity_metadata_wrapper('node', $node);
|
443
|
$this->assertEqual($last_msg, $wrapper->body->value->value(array('sanitize' => TRUE)), 'Data selector for getting parameter applied.');
|
444
|
|
445
|
// Get a "reference" on the same object as returned by node_load().
|
446
|
$node = node_load($node->nid);
|
447
|
$rule = rule(array('nid' => array('type' => 'integer')));
|
448
|
$rule->action('rules_action_load_node')
|
449
|
->action('data_set', array('data:select' => 'node_loaded:title', 'value' => 'Test title'))
|
450
|
// Use two actions and make sure the node get saved only once.
|
451
|
->action('data_set', array('data:select' => 'node_loaded:title', 'value' => 'Test title2'))
|
452
|
->execute($node->nid);
|
453
|
|
454
|
$wrapper = entity_metadata_wrapper('node', $node);
|
455
|
$this->assertEqual('Test title2', $wrapper->title->value(), 'Data has been modified and saved.');
|
456
|
|
457
|
RulesLog::logger()->checkLog();
|
458
|
$text = RulesLog::logger()->render();
|
459
|
$msg = RulesTestCase::t('Saved %node_loaded of type %node.', array('node_loaded', 'node'));
|
460
|
if ($pos1 = strpos($text, $msg)) {
|
461
|
$pos2 = strpos($text, $msg, $pos1 + 1);
|
462
|
}
|
463
|
$this->assertTrue($pos1 && $pos2 === FALSE, 'Data has been saved only once.');
|
464
|
|
465
|
// Test validation.
|
466
|
try {
|
467
|
rules_action('data_set', array('data' => 'no-selector', 'value' => ''))->integrityCheck();
|
468
|
$this->fail("Validation hasn't created an exception.");
|
469
|
}
|
470
|
catch (RulesIntegrityException $e) {
|
471
|
$this->pass("Validation error correctly detected: ". $e);
|
472
|
}
|
473
|
|
474
|
// Test auto creation of nested data structures, like the node body field.
|
475
|
// I.e. if $node->body is not set, it is automatically initialized to an
|
476
|
// empty array, so that the nested value can be set and the wrappers do not
|
477
|
// complain about missing parent data structures.
|
478
|
$rule = rule();
|
479
|
$rule->action('entity_create', array(
|
480
|
'type' => 'node',
|
481
|
'param_type' => 'page',
|
482
|
'param_title' => 'foo',
|
483
|
'param_author' => $GLOBALS['user'],
|
484
|
));
|
485
|
$rule->action('data_set', array('data:select' => 'entity_created:body:value', 'value' => 'test content'))
|
486
|
->execute();
|
487
|
try {
|
488
|
RulesLog::logger()->checkLog();
|
489
|
$this->pass('Auto creation of nested data structures.');
|
490
|
}
|
491
|
catch (Exception $e) {
|
492
|
$this->fail('Auto creation of nested data structures.');
|
493
|
}
|
494
|
|
495
|
// Make sure variables that are passed wrapped work.
|
496
|
$result = rules_condition('rules_test_condition_node_wrapped')->execute($node->nid);
|
497
|
$this->assertTrue($result, 'Condition receiving wrapped parameter.');
|
498
|
|
499
|
// Make sure wrapped parameters are checked for containing NULL values.
|
500
|
$rule = rule(array('node' => array('type' => 'node', 'optional' => TRUE)));
|
501
|
$rule->condition('rules_test_condition_node_wrapped', array('node:select' => 'node'));
|
502
|
$rule->execute(entity_metadata_wrapper('node'));
|
503
|
$text = RulesLog::logger()->render();
|
504
|
$msg = RulesTestCase::t('The variable or parameter %node is empty.', array('node'));
|
505
|
$this->assertTrue(strpos($text, $msg) !== FALSE, 'Evaluation aborted due to an empty argument value.');
|
506
|
}
|
507
|
|
508
|
/**
|
509
|
* Tests making use of rule sets.
|
510
|
*/
|
511
|
function testRuleSets() {
|
512
|
$set = rules_rule_set(array(
|
513
|
'node' => array('type' => 'node', 'label' => 'node'),
|
514
|
));
|
515
|
$set->rule(rule()->action('drupal_message', array('message:select' => 'node:title')))
|
516
|
->rule(rule()->condition('rules_condition_content_is_published')
|
517
|
->action('drupal_message', array('message' => 'Node is published.'))
|
518
|
);
|
519
|
$set->integrityCheck()->save('rules_test_set_1');
|
520
|
|
521
|
$node = $this->drupalCreateNode(array('title' => 'The title.', 'status' => 1));
|
522
|
// Execute.
|
523
|
rules_invoke_component('rules_test_set_1', $node);
|
524
|
|
525
|
$msg = drupal_get_messages();
|
526
|
$this->assertEqual($msg['status'][0], 'The title.', 'First rule evaluated.');
|
527
|
$this->assertEqual($msg['status'][1], 'Node is published.', 'Second rule evaluated.');
|
528
|
|
529
|
// Test a condition set.
|
530
|
$set = rules_or(array(
|
531
|
'node' => array('type' => 'node', 'label' => 'node'),
|
532
|
));
|
533
|
$set->condition('data_is', array('data:select' => 'node:author:name', 'value' => 'notthename'))
|
534
|
->condition('data_is', array('data:select' => 'node:nid', 'value' => $node->nid))
|
535
|
->integrityCheck()
|
536
|
->save('test', 'rules_test');
|
537
|
// Load and execute condition set.
|
538
|
$set = rules_config_load('test');
|
539
|
$this->assertTrue($set->execute($node), 'Set has been correctly evaluated.');
|
540
|
RulesLog::logger()->checkLog();
|
541
|
}
|
542
|
|
543
|
/**
|
544
|
* Tests invoking components from the action.
|
545
|
*/
|
546
|
function testComponentInvocations() {
|
547
|
$set = rules_rule_set(array(
|
548
|
'node1' => array('type' => 'node', 'label' => 'node'),
|
549
|
));
|
550
|
$set->rule(rule()->condition('node_is_published', array('node:select' => 'node1'))
|
551
|
->action('node_unpublish', array('node:select' => 'node1'))
|
552
|
);
|
553
|
$set->integrityCheck()->save('rules_test_set_2');
|
554
|
|
555
|
// Use different names for the variables to ensure they are properly mapped
|
556
|
// when taking over the variables to be saved.
|
557
|
$rule = rule(array(
|
558
|
'node2' => array('type' => 'node', 'label' => 'node'),
|
559
|
));
|
560
|
$rule->action('component_rules_test_set_2', array('node1:select' => 'node2'));
|
561
|
$rule->action('node_make_sticky', array('node:select' => 'node2'));
|
562
|
|
563
|
$node = $this->drupalCreateNode(array('title' => 'The title.', 'status' => 1, 'sticky' => 0));
|
564
|
$rule->execute($node);
|
565
|
|
566
|
$node = node_load($node->nid, NULL, TRUE);
|
567
|
$this->assertFalse($node->status, 'The component changes have been saved correctly.');
|
568
|
$this->assertTrue($node->sticky, 'The action changes have been saved correctly.');
|
569
|
|
570
|
// Check that we have saved the changes only once.
|
571
|
$text = RulesLog::logger()->render();
|
572
|
// Make sure both saves are handled in one save operation.
|
573
|
$this->assertEqual(substr_count($text, 'Saved'), 1, 'Changes have been saved in one save operation.');
|
574
|
RulesLog::logger()->checkLog();
|
575
|
|
576
|
// Test recursion prevention on components by invoking the component from
|
577
|
// itself, what should be prevented.
|
578
|
$set->action('component_rules_test_set_2', array('node1:select' => 'node1'))
|
579
|
->save();
|
580
|
|
581
|
$rule->execute($node);
|
582
|
$text1 = RulesLog::logger()->render();
|
583
|
$text2 = RulesTestCase::t('Not evaluating rule set %rules_test_set_2 to prevent recursion.', array('rules_test_set_2'));
|
584
|
$this->assertTrue((strpos($text1, $text2) !== FALSE), "Recursion of component invocation prevented.");
|
585
|
|
586
|
// Test executing the component provided in code via the action. This makes
|
587
|
// sure the component in code has been properly picked up.
|
588
|
$node->status = 0;
|
589
|
node_save($node);
|
590
|
rules_action('component_rules_test_action_set')->execute($node);
|
591
|
$this->assertTrue($node->status == 1, 'Component provided in code has been executed.');
|
592
|
}
|
593
|
|
594
|
|
595
|
/**
|
596
|
* Test asserting metadata, customizing action info and make sure integrity
|
597
|
* is checked.
|
598
|
*/
|
599
|
function testMetadataAssertion() {
|
600
|
$action = rules_action('rules_node_make_sticky_action');
|
601
|
|
602
|
// Test failing integrity check.
|
603
|
try {
|
604
|
$rule = rule(array('node' => array('type' => 'entity')));
|
605
|
$rule->action($action);
|
606
|
// Fails due to the 'node' variable not matching the node type.
|
607
|
$rule->integrityCheck();
|
608
|
$this->fail('Integrity check has not thrown an exception.');
|
609
|
}
|
610
|
catch (RulesIntegrityException $e) {
|
611
|
$this->pass('Integrity check has thrown exception: ' . $e->getMessage());
|
612
|
}
|
613
|
|
614
|
// Test asserting additional metadata.
|
615
|
$rule = rule(array('node' => array('type' => 'node')));
|
616
|
// Customize action info using the settings.
|
617
|
$rule->condition('data_is', array('data:select' => 'node:type', 'value' => 'page'))
|
618
|
// Configure an condition using the body. As the body is a field,
|
619
|
// tis requires the bundle to be correctly asserted.
|
620
|
->condition(rules_condition('data_is', array('data:select' => 'node:body:value', 'value' => 'foo'))->negate())
|
621
|
// The action also requires the page bundle in order to work.
|
622
|
->action($action);
|
623
|
// Make sure the integrity check doesn't throw an exception.
|
624
|
$rule->integrityCheck();
|
625
|
// Test the rule.
|
626
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
|
627
|
$rule->execute($node);
|
628
|
$this->assertTrue($node->sticky, 'Rule with asserted metadata executed.');
|
629
|
|
630
|
|
631
|
// Test asserting metadata on a derived property, i.e. not a variable.
|
632
|
$rule = rule(array('node' => array('type' => 'node')));
|
633
|
$rule->condition('entity_is_of_type', array('entity:select' => 'node:reference', 'type' => 'node'))
|
634
|
->condition('data_is', array('data:select' => 'node:reference:type', 'value' => 'page'))
|
635
|
->action('rules_node_page_make_sticky_action', array('node:select' => 'node:reference'));
|
636
|
$rule->integrityCheck();
|
637
|
$rule->execute($node);
|
638
|
|
639
|
// Test asserting an entity field.
|
640
|
$rule = rule(array('node' => array('type' => 'node')));
|
641
|
$rule->condition('entity_has_field', array('entity:select' => 'node:reference', 'field' => 'field_tags'))
|
642
|
->action('data_set', array('data:select' => 'node:reference:field-tags', 'value' => array()));
|
643
|
$rule->integrityCheck();
|
644
|
$rule->execute($node);
|
645
|
|
646
|
// Make sure an asserted bundle can be used as argument.
|
647
|
$rule = rule(array('node' => array('type' => 'node')));
|
648
|
$rule->condition('entity_is_of_type', array('entity:select' => 'node:reference', 'type' => 'node'))
|
649
|
->condition('node_is_of_type', array('node:select' => 'node:reference', 'type' => array('page')))
|
650
|
->action('rules_node_page_make_sticky_action', array('node:select' => 'node:reference'));
|
651
|
$rule->integrityCheck();
|
652
|
$rule->execute($node);
|
653
|
|
654
|
// Test asserting metadata on a derived property being a list item.
|
655
|
$rule = rule(array('node' => array('type' => 'node')));
|
656
|
$rule->condition('node_is_of_type', array('node:select' => 'node:ref-nodes:0', 'type' => array('article')))
|
657
|
->action('data_set', array('data:select' => 'node:ref-nodes:0:field-tags', 'value' => array()));
|
658
|
$rule->integrityCheck();
|
659
|
$rule->execute($node);
|
660
|
|
661
|
// Give green lights if there were no exceptions and check rules-log errors.
|
662
|
$this->pass('Rules asserting metadata on a derived property pass integrity checks.');
|
663
|
RulesLog::logger()->checkLog();
|
664
|
|
665
|
// Make sure assertions of a one list item are not valid for another item.
|
666
|
$rule = rule(array('node' => array('type' => 'node')));
|
667
|
$rule->condition('node_is_of_type', array('node:select' => 'node:ref-nodes:0', 'type' => array('article')))
|
668
|
->action('data_set', array('data:select' => 'node:ref-nodes:1:field-tags', 'value' => array()));
|
669
|
try {
|
670
|
$rule->integrityCheck();
|
671
|
$this->fail('Assertion of a list item is not valid for another item.');
|
672
|
}
|
673
|
catch (RulesException $e) {
|
674
|
$this->pass('Assertion of a list item is not valid for another item.');
|
675
|
}
|
676
|
}
|
677
|
|
678
|
/**
|
679
|
* Test using loops.
|
680
|
*/
|
681
|
function testLoops() {
|
682
|
// Test passing the list parameter as argument to ensure that is working
|
683
|
// generally for plugin container too.
|
684
|
drupal_get_messages(NULL, TRUE);
|
685
|
$loop = rules_loop();
|
686
|
$loop->action('drupal_message', array('message' => 'test'));
|
687
|
$arg_info = $loop->parameterInfo();
|
688
|
$this->assert($arg_info['list']['type'] == 'list', 'Argument info contains list.');
|
689
|
$loop->execute(array(1, 2));
|
690
|
|
691
|
// Ensure the action has been executed twice, once for each list item.
|
692
|
$msg = drupal_get_messages();
|
693
|
$this->assert($msg['status'][0] == 'test' && $msg['status'][1], 'Loop has been properly executed');
|
694
|
|
695
|
// Now test looping over nodes.
|
696
|
$node1 = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
|
697
|
$node2 = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
|
698
|
$node3 = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
|
699
|
|
700
|
$rule = rule(array(
|
701
|
'list' => array(
|
702
|
'type' => 'list<node>',
|
703
|
'label' => 'A list of nodes',
|
704
|
)
|
705
|
));
|
706
|
$loop = rules_loop(array('list:select' => 'list', 'item:var' => 'node'));
|
707
|
$loop->action('data_set', array('data:select' => 'node:sticky', 'value' => TRUE));
|
708
|
$rule->action($loop);
|
709
|
// Test using a list with data selectors, just output the last nodes type.
|
710
|
$rule->action('drupal_message', array('message:select' => 'list:2:type'));
|
711
|
|
712
|
$rule->execute(array($node1->nid, $node2->nid, $node3->nid));
|
713
|
$text = RulesLog::logger()->render();
|
714
|
$save_msg = RulesTestCase::t('Saved %node of type %node.', array('node', 'node'));
|
715
|
$this->assertTrue(substr_count($text, $save_msg) == 3, 'List item variables have been saved.');
|
716
|
RulesLog::logger()->checkLog();
|
717
|
}
|
718
|
|
719
|
/**
|
720
|
* Test access checks.
|
721
|
*/
|
722
|
function testAccessCheck() {
|
723
|
$rule = rule();
|
724
|
// Try to set a property which is provided by the test module and is not
|
725
|
// accessible, so the access check has to return FALSE.
|
726
|
$rule->action('data_set', array('data:select' => 'site:no-access-user', 'value' => 'foo'));
|
727
|
$this->assertTrue($rule->access() === FALSE, 'Access check is working.');
|
728
|
}
|
729
|
|
730
|
/**
|
731
|
* Test returning provided variables.
|
732
|
*/
|
733
|
function testReturningVariables() {
|
734
|
$node = $this->drupalCreateNode();
|
735
|
$action = rules_action('entity_fetch', array('type' => 'node', 'id' => $node->nid));
|
736
|
list($node2) = $action->execute();
|
737
|
$this->assertTrue($node2->nid == $node->nid, 'Action returned a variable.');
|
738
|
|
739
|
// Create a simple set that just passed through the given node.
|
740
|
$set = rules_rule_set(array('node' => array('type' => 'node')), array('node'));
|
741
|
$set->integrityCheck()->save('rules_test_set_1');
|
742
|
|
743
|
$provides = $set->providesVariables();
|
744
|
$this->assertTrue($provides['node']['type'] == 'node', 'Rule set correctly passed through the node.');
|
745
|
|
746
|
list($node2) = $set->execute($node);
|
747
|
$this->assertTrue($node2->nid == $node->nid, 'Rule set returned a variable.');
|
748
|
|
749
|
// Create an action set returning a variable that is no parameter.
|
750
|
$set = rules_action_set(array(
|
751
|
'node' => array(
|
752
|
'type' => 'node',
|
753
|
'parameter' => FALSE,
|
754
|
)), array('node'));
|
755
|
$set->action('entity_fetch', array('type' => 'node', 'id' => $node->nid))
|
756
|
->action('data_set', array('data:select' => 'node', 'value:select' => 'entity_fetched'));
|
757
|
$set->integrityCheck();
|
758
|
list($node3) = $set->execute();
|
759
|
$this->assertTrue($node3->nid == $node->nid, 'Action set returned a variable that has not been passed as parameter.');
|
760
|
|
761
|
// Test the same again with a variable holding a not wrapped data type.
|
762
|
$set = rules_action_set(array(
|
763
|
'number' => array(
|
764
|
'type' => 'integer',
|
765
|
'parameter' => FALSE,
|
766
|
)), array('number'));
|
767
|
$set->action('data_set', array('data:select' => 'number', 'value' => 3));
|
768
|
$set->integrityCheck();
|
769
|
list($number) = $set->execute();
|
770
|
$this->assertTrue($number == 3, 'Actions set returned a number.');
|
771
|
}
|
772
|
|
773
|
/**
|
774
|
* Tests using input evaluators.
|
775
|
*/
|
776
|
function testInputEvaluators() {
|
777
|
$node = $this->drupalCreateNode(array('title' => '<b>The body & nothing.</b>', 'type' => 'page'));
|
778
|
|
779
|
$rule = rule(array('nid' => array('type' => 'integer')));
|
780
|
$rule->action('rules_action_load_node')
|
781
|
->action('drupal_message', array('message' => 'Title: [node_loaded:title]'))
|
782
|
->execute($node->nid);
|
783
|
|
784
|
RulesLog::logger()->checkLog();
|
785
|
$msg = drupal_get_messages();
|
786
|
$this->assertEqual(array_pop($msg['status']), 'Title: ' . check_plain('<b>The body & nothing.</b>'), 'Token input evaluator applied.');
|
787
|
|
788
|
// Test token replacements on a list of text values.
|
789
|
$component = rules_action_set(array('var' => array('type' => 'list<text>', 'label' => 'var')), array('var'));
|
790
|
$component->save('rules_test_input');
|
791
|
|
792
|
$action = rules_action('component_rules_test_input', array('var' => array('uid: [site:current-user:uid]')));
|
793
|
list($var) = $action->execute();
|
794
|
$uid = $GLOBALS['user']->uid;
|
795
|
$this->assertEqual(array("uid: $uid"), $var, 'Token replacements on a list of values applied.');
|
796
|
}
|
797
|
|
798
|
/**
|
799
|
* Test importing and exporting a rule.
|
800
|
*/
|
801
|
function testRuleImportExport() {
|
802
|
$rule = rule(array('nid' => array('type' => 'integer')));
|
803
|
$rule->name = "rules_export_test";
|
804
|
$rule->action('rules_action_load_node')
|
805
|
->action('drupal_message', array('message' => 'Title: [node_loaded:title]'));
|
806
|
|
807
|
$export =
|
808
|
'{ "rules_export_test" : {
|
809
|
"PLUGIN" : "rule",
|
810
|
"REQUIRES" : [ "rules_test", "rules" ],
|
811
|
"USES VARIABLES" : { "nid" : { "type" : "integer" } },
|
812
|
"DO" : [
|
813
|
{ "rules_action_load_node" : { "PROVIDE" : { "node_loaded" : { "node_loaded" : "Loaded content" } } } },
|
814
|
{ "drupal_message" : { "message" : "Title: [node_loaded:title]" } }
|
815
|
]
|
816
|
}
|
817
|
}';
|
818
|
$this->assertEqual($export, $rule->export(), 'Rule has been exported correctly.');
|
819
|
|
820
|
// Test importing a rule which makes use of almost all features.
|
821
|
$export = _rules_export_get_test_export();
|
822
|
$rule = rules_import($export);
|
823
|
$this->assertTrue(!empty($rule) && $rule->integrityCheck(), 'Rule has been imported.');
|
824
|
|
825
|
// Test loading the same export provided as default rule.
|
826
|
$rule = rules_config_load('rules_export_test');
|
827
|
$this->assertTrue(!empty($rule) && $rule->integrityCheck(), 'Export has been provided in code.');
|
828
|
|
829
|
// Export it and make sure the same export is generated again.
|
830
|
$this->assertEqual($export, $rule->export(), 'Export of imported rule equals original export.');
|
831
|
|
832
|
// Now try importing a rule set.
|
833
|
$export =
|
834
|
'{ "rules_test_set" : {
|
835
|
"LABEL" : "Test set",
|
836
|
"PLUGIN" : "rule set",
|
837
|
"REQUIRES" : [ "rules" ],
|
838
|
"USES VARIABLES" : { "node" : { "label" : "Test node", "type" : "node" } },
|
839
|
"RULES" : [
|
840
|
{ "RULE" : {
|
841
|
"IF" : [ { "NOT data_is" : { "data" : [ "node:title" ], "value" : "test" } } ],
|
842
|
"DO" : [ { "data_set" : { "data" : [ "node:title" ], "value" : "test" } } ],
|
843
|
"LABEL" : "Test Rule"
|
844
|
}
|
845
|
},
|
846
|
{ "RULE" : {
|
847
|
"DO" : [ { "drupal_message" : { "message" : "hi" } } ],
|
848
|
"LABEL" : "Test Rule 2"
|
849
|
}
|
850
|
}
|
851
|
]
|
852
|
}
|
853
|
}';
|
854
|
$set = rules_import($export);
|
855
|
$this->assertTrue(!empty($set) && $set->integrityCheck(), 'Rule set has been imported.');
|
856
|
// Export it and make sure the same export is generated again.
|
857
|
$this->assertEqual($export, $set->export(), 'Export of imported rule set equals original export.');
|
858
|
|
859
|
// Try executing the imported rule set.
|
860
|
$node = $this->drupalCreateNode();
|
861
|
$set->execute($node);
|
862
|
$this->assertEqual($node->title, 'test', 'Imported rule set has been executed.');
|
863
|
RulesLog::logger()->checkLog();
|
864
|
|
865
|
// Try import / export for a rule component providing a variable.
|
866
|
$rule = rule(array(
|
867
|
'number' => array(
|
868
|
'type' => 'integer',
|
869
|
'label' => 'Number',
|
870
|
'parameter' => FALSE,
|
871
|
)), array('number'));
|
872
|
$rule->action('data_set', array('data:select' => 'number', 'value' => 3));
|
873
|
$rule->name = 'rules_test_provides';
|
874
|
|
875
|
$export = '{ "rules_test_provides" : {
|
876
|
"PLUGIN" : "rule",
|
877
|
"REQUIRES" : [ "rules" ],
|
878
|
"USES VARIABLES" : { "number" : { "type" : "integer", "label" : "Number", "parameter" : false } },
|
879
|
"DO" : [ { "data_set" : { "data" : [ "number" ], "value" : 3 } } ],
|
880
|
"PROVIDES VARIABLES" : [ "number" ]
|
881
|
}
|
882
|
}';
|
883
|
$this->assertEqual($export, $rule->export(), 'Rule 2 has been exported correctly.');
|
884
|
$imported_rule = rules_import($rule->export());
|
885
|
|
886
|
$this->assertTrue(!empty($imported_rule) && $imported_rule->integrityCheck(), 'Rule 2 has been imported.');
|
887
|
$this->assertEqual($export, $imported_rule->export(), 'Export of imported rule 2 equals original export.');
|
888
|
|
889
|
// Test importing a negated condition component.
|
890
|
$export = '{ "rules_negated_component" : {
|
891
|
"LABEL" : "negated_component",
|
892
|
"PLUGIN" : "or",
|
893
|
"REQUIRES" : [ "rules" ],
|
894
|
"NOT OR" : [ { "data_is_empty" : { "data" : [ "site:slogan" ] } } ]
|
895
|
}
|
896
|
}';
|
897
|
$or = rules_import($export);
|
898
|
$this->assertTrue($or->integrityCheck() && $or->isNegated(), 'Negated condition component imported.');
|
899
|
}
|
900
|
|
901
|
/**
|
902
|
* Test the named parameter mode.
|
903
|
*/
|
904
|
function testNamedParameters() {
|
905
|
$rule = rule(array('node' => array('type' => 'node')));
|
906
|
$rule->action('rules_action_node_set_title', array('title' => 'foo'));
|
907
|
$rule->integrityCheck();
|
908
|
|
909
|
// Test the rule.
|
910
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
|
911
|
$rule->execute($node);
|
912
|
$this->assertTrue($node->title == 'foo', 'Action with named parameters has been correctly executed.');
|
913
|
RulesLog::logger()->checkLog();
|
914
|
}
|
915
|
|
916
|
/**
|
917
|
* Make sure Rules aborts when NULL values are used.
|
918
|
*/
|
919
|
function testAbortOnNULLValues() {
|
920
|
$rule = rule(array('node' => array('type' => 'node')));
|
921
|
$rule->action('drupal_message', array('message:select' => 'node:log'));
|
922
|
$rule->integrityCheck();
|
923
|
|
924
|
// Test the rule.
|
925
|
$node = $this->drupalCreateNode();
|
926
|
$node->log = NULL;
|
927
|
$rule->execute($node);
|
928
|
|
929
|
$text = RulesLog::logger()->render();
|
930
|
$msg = RulesTestCase::t('The variable or parameter %message is empty.', array('message'));
|
931
|
$this->assertTrue(strpos($text, $msg) !== FALSE, 'Evaluation aborted due to an empty argument value.');
|
932
|
}
|
933
|
}
|
934
|
|
935
|
/**
|
936
|
* Test rules data wrappers.
|
937
|
*/
|
938
|
class RulesTestDataCase extends DrupalWebTestCase {
|
939
|
|
940
|
static function getInfo() {
|
941
|
return array(
|
942
|
'name' => 'Rules Data tests',
|
943
|
'description' => 'Tests rules data saving and type matching.',
|
944
|
'group' => 'Rules',
|
945
|
);
|
946
|
}
|
947
|
|
948
|
function setUp() {
|
949
|
parent::setUp('rules', 'rules_test');
|
950
|
variable_set('rules_debug_log', 1);
|
951
|
// Make sure we don't ran over issues with the node_load static cache.
|
952
|
entity_get_controller('node')->resetCache();
|
953
|
}
|
954
|
|
955
|
/**
|
956
|
* Tests intelligently saving data.
|
957
|
*/
|
958
|
function testDataSaving() {
|
959
|
$node = $this->drupalCreateNode();
|
960
|
$state = new RulesState(rule());
|
961
|
$state->addVariable('node', $node, array('type' => 'node'));
|
962
|
$wrapper = $state->get('node');
|
963
|
$node->title = 'test';
|
964
|
$wrapper->set($node);
|
965
|
$state->saveChanges('node', $wrapper, FALSE);
|
966
|
|
967
|
$this->assertFalse($this->drupalGetNodeByTitle('test'), 'Changes have not been saved.');
|
968
|
$state->saveChanges('node', $wrapper, TRUE);
|
969
|
$this->assertTrue($this->drupalGetNodeByTitle('test'), 'Changes have been saved.');
|
970
|
|
971
|
// Test skipping saving.
|
972
|
$state->addVariable('node2', $node, array(
|
973
|
'type' => 'node',
|
974
|
'skip save' => TRUE,
|
975
|
));
|
976
|
$wrapper = $state->get('node2');
|
977
|
$node->title = 'test2';
|
978
|
$wrapper->set($node);
|
979
|
$state->saveChanges('node2', $wrapper, TRUE);
|
980
|
$this->assertFalse($this->drupalGetNodeByTitle('test2'), 'Changes have not been saved.');
|
981
|
|
982
|
// Try saving a non-entity wrapper, which should result in saving the
|
983
|
// parent entity containing the property.
|
984
|
$wrapper = $state->get('node');
|
985
|
$wrapper->title->set('test3');
|
986
|
$state->saveChanges('node:title', $wrapper, TRUE);
|
987
|
$this->assertTrue($this->drupalGetNodeByTitle('test3'), 'Parent entity has been saved.');
|
988
|
}
|
989
|
|
990
|
/**
|
991
|
* Test type matching
|
992
|
*/
|
993
|
function testTypeMatching() {
|
994
|
$entity = array('type' => 'entity');
|
995
|
$node = array('type' => 'node');
|
996
|
$this->assertTrue(RulesData::typesMatch($node, $entity), 'Types match.');
|
997
|
$this->assertFalse(RulesData::typesMatch($entity, $node), 'Types don\'t match.');
|
998
|
|
999
|
$this->assertTrue(RulesData::typesMatch($node + array('bundle' => 'page'), $node), 'Types match.');
|
1000
|
$this->assertTrue(RulesData::typesMatch($node + array('bundle' => 'page'), $entity), 'Types match.');
|
1001
|
$this->assertTrue(RulesData::typesMatch(array('type' => 'list<node>'), array('type' => 'list')), 'Types match.');
|
1002
|
$this->assertTrue(RulesData::typesMatch($node + array('bundle' => 'page'), $node + array('bundles' => array('page', 'story'))), 'Types match.');
|
1003
|
$this->assertFalse(RulesData::typesMatch($node, $node + array('bundles' => array('page', 'story'))), 'Types don\'t match.');
|
1004
|
|
1005
|
// Test that a type matches its grand-parent type (text > decimal > integer)
|
1006
|
$this->assertTrue(RulesData::typesMatch(array('type' => 'integer'), array('type' => 'text')), 'Types match.');
|
1007
|
$this->assertFalse(RulesData::typesMatch(array('type' => 'text'), array('type' => 'integer')), 'Types don\'t match.');
|
1008
|
}
|
1009
|
|
1010
|
/**
|
1011
|
* Tests making use of custom wrapper classes.
|
1012
|
*/
|
1013
|
function testCustomWrapperClasses() {
|
1014
|
// Test loading a vocabulary by name, which is done by a custom wrapper.
|
1015
|
$set = rules_action_set(array('vocab' => array('type' => 'taxonomy_vocabulary')), array('vocab'));
|
1016
|
$set->action('drupal_message', array('message:select' => 'vocab:name'));
|
1017
|
$set->integrityCheck();
|
1018
|
list($vocab) = $set->execute('tags');
|
1019
|
$this->assertTrue($vocab->machine_name == 'tags', 'Loaded vocabulary by name.');
|
1020
|
|
1021
|
// Now test wrapper creation for a direct input argument value.
|
1022
|
$set = rules_action_set(array('term' => array('type' => 'taxonomy_term')));
|
1023
|
$set->action('data_set', array('data:select' => 'term:vocabulary', 'value' => 'tags'));
|
1024
|
$set->integrityCheck();
|
1025
|
|
1026
|
$vocab = entity_create('taxonomy_vocabulary', array(
|
1027
|
'name' => 'foo',
|
1028
|
'machine_name' => 'foo',
|
1029
|
));
|
1030
|
entity_save('taxonomy_vocabulary', $vocab);
|
1031
|
$term_wrapped = entity_property_values_create_entity('taxonomy_term', array(
|
1032
|
'name' => $this->randomName(),
|
1033
|
'vocabulary' => $vocab,
|
1034
|
))->save();
|
1035
|
$set->execute($term_wrapped);
|
1036
|
$this->assertEqual($term_wrapped->vocabulary->machine_name->value(), 'tags', 'Vocabulary name used as direct input value.');
|
1037
|
RulesLog::logger()->checkLog();
|
1038
|
}
|
1039
|
|
1040
|
/**
|
1041
|
* Makes sure the RulesIdentifiableDataWrapper is working correctly.
|
1042
|
*/
|
1043
|
function testRulesIdentifiableDataWrapper() {
|
1044
|
$node = $this->drupalCreateNode();
|
1045
|
$wrapper = new RulesTestTypeWrapper('rules_test_type', $node);
|
1046
|
$this->assertTrue($wrapper->value() == $node, 'Data correctly wrapped.');
|
1047
|
|
1048
|
// Test serializing and make sure only the id is stored.
|
1049
|
$this->assertTrue(strpos(serialize($wrapper), $node->title) === FALSE, 'Data has been correctly serialized.');
|
1050
|
$this->assertEqual(unserialize(serialize($wrapper))->value()->title, $node->title, 'Serializing works right.');
|
1051
|
|
1052
|
$wrapper2 = unserialize(serialize($wrapper));
|
1053
|
// Test serializing the unloaded wrapper.
|
1054
|
$this->assertEqual(unserialize(serialize($wrapper2))->value()->title, $node->title, 'Serializing works right.');
|
1055
|
|
1056
|
// Test loading a not more existing node.
|
1057
|
$s = serialize($wrapper2);
|
1058
|
node_delete($node->nid);
|
1059
|
$this->assertFalse(node_load($node->nid), 'Node deleted.');
|
1060
|
try {
|
1061
|
unserialize($s)->value();
|
1062
|
$this->fail("Loading hasn't created an exception.");
|
1063
|
}
|
1064
|
catch (EntityMetadataWrapperException $e) {
|
1065
|
$this->pass("Exception was thrown: ". $e->getMessage());
|
1066
|
}
|
1067
|
|
1068
|
// Test saving a savable custom, identifiable wrapper.
|
1069
|
$action = rules_action('test_type_save');
|
1070
|
$node = $this->drupalCreateNode(array('status' => 0, 'type' => 'page'));
|
1071
|
$node->status = 1;
|
1072
|
$action->execute($node);
|
1073
|
|
1074
|
// Load the node fresh from the db.
|
1075
|
$node = node_load($node->nid, NULL, TRUE);
|
1076
|
$this->assertEqual($node->status, 1, 'Savable non-entity has been saved.');
|
1077
|
}
|
1078
|
}
|
1079
|
|
1080
|
/**
|
1081
|
* Test triggering rules.
|
1082
|
*/
|
1083
|
class RulesTriggerTestCase extends DrupalWebTestCase {
|
1084
|
|
1085
|
static function getInfo() {
|
1086
|
return array(
|
1087
|
'name' => 'Reaction Rules',
|
1088
|
'description' => 'Tests triggering reactive rules.',
|
1089
|
'group' => 'Rules',
|
1090
|
);
|
1091
|
}
|
1092
|
|
1093
|
function setUp() {
|
1094
|
parent::setUp('rules', 'rules_test');
|
1095
|
RulesLog::logger()->clear();
|
1096
|
variable_set('rules_debug_log', 1);
|
1097
|
}
|
1098
|
|
1099
|
protected function createTestRule($action = TRUE, $event = 'node_presave') {
|
1100
|
$rule = rules_reaction_rule();
|
1101
|
$rule->event($event)
|
1102
|
->condition(rules_condition('data_is', array('data:select' => 'node:status', 'value' => TRUE))->negate())
|
1103
|
->condition('data_is', array('data:select' => 'node:type', 'value' => 'page'));
|
1104
|
if ($action) {
|
1105
|
$rule->action('rules_action_delete_node');
|
1106
|
}
|
1107
|
return $rule;
|
1108
|
}
|
1109
|
|
1110
|
/**
|
1111
|
* Tests CRUD for reaction rules - making sure the events are stored properly.
|
1112
|
*/
|
1113
|
function testReactiveRuleCreation() {
|
1114
|
$rule = $this->createTestRule();
|
1115
|
$rule->save();
|
1116
|
$result = db_query("SELECT event FROM {rules_trigger} WHERE id = :id", array(':id' => $rule->id));
|
1117
|
$this->assertEqual($result->fetchField(), 'node_presave', 'Associated event has been saved.');
|
1118
|
// Try updating.
|
1119
|
$rule->removeEvent('node_presave');
|
1120
|
$rule->event('node_insert');
|
1121
|
$rule->event('node_update');
|
1122
|
$rule->active = FALSE;
|
1123
|
$rule->integrityCheck()->save();
|
1124
|
$result = db_query("SELECT event FROM {rules_trigger} WHERE id = :id", array(':id' => $rule->id));
|
1125
|
$this->assertEqual($result->fetchCol(), array_values($rule->events()), 'Updated associated events.');
|
1126
|
// Try deleting.
|
1127
|
$rule->delete();
|
1128
|
$result = db_query("SELECT event FROM {rules_trigger} WHERE id = :id", array(':id' => $rule->id));
|
1129
|
$this->assertEqual($result->fetchField(), FALSE, 'Deleted associated events.');
|
1130
|
}
|
1131
|
|
1132
|
/**
|
1133
|
* Tests creating and triggering a basic reaction rule.
|
1134
|
*/
|
1135
|
function testBasicReactionRule() {
|
1136
|
$node = $this->drupalCreateNode(array('type' => 'page'));
|
1137
|
$rule = $this->createTestRule();
|
1138
|
$rule->integrityCheck()->save();
|
1139
|
// Test the basics of the event set work right.
|
1140
|
$event = rules_get_cache('event_node_presave');
|
1141
|
$this->assertEqual(array_keys($event->parameterInfo()), array('node'), 'EventSet returns correct argument info.');
|
1142
|
|
1143
|
// Trigger the rule by updating the node.
|
1144
|
$nid = $node->nid;
|
1145
|
$node->status = 0;
|
1146
|
node_save($node);
|
1147
|
|
1148
|
RulesLog::logger()->checkLog();
|
1149
|
$this->assertFalse(node_load($nid), 'Rule successfully triggered and executed');
|
1150
|
//debug(RulesLog::logger()->render());
|
1151
|
}
|
1152
|
|
1153
|
/**
|
1154
|
* Test a rule using a handler to load a variable.
|
1155
|
*/
|
1156
|
function testVariableHandler() {
|
1157
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1158
|
$rule = $this->createTestRule(FALSE, 'node_update');
|
1159
|
$rule->action('rules_node_publish_action_save', array('node:select' => 'node_unchanged'));
|
1160
|
// Test without recursion prevention to make sure recursive invocations
|
1161
|
// work right too. This rule won't ran in an infinite loop anyway.
|
1162
|
$rule->recursion = TRUE;
|
1163
|
$rule->label = 'rule 1';
|
1164
|
$rule->integrityCheck()->save();
|
1165
|
|
1166
|
$node->status = 0;
|
1167
|
$node->sticky = 1;
|
1168
|
node_save($node);
|
1169
|
|
1170
|
RulesLog::logger()->checkLog();
|
1171
|
entity_get_controller('node')->resetCache();
|
1172
|
$node = node_load($node->nid);
|
1173
|
|
1174
|
$this->assertFalse($node->sticky, 'Parameter has been loaded and saved.');
|
1175
|
$this->assertTrue($node->status, 'Action has been executed.');
|
1176
|
|
1177
|
// Ensure the rule was evaluated a second time
|
1178
|
$text = RulesLog::logger()->render();
|
1179
|
$msg = RulesTestCase::t('Evaluating conditions of rule %rule 1', array('rule 1'));
|
1180
|
$pos = strpos($text, $msg);
|
1181
|
$pos = ($pos !== FALSE) ? strpos($text, $msg, $pos) : FALSE;
|
1182
|
$this->assertTrue($pos !== FALSE, "Recursion prevented.");
|
1183
|
//debug(RulesLog::logger()->render());
|
1184
|
}
|
1185
|
|
1186
|
/**
|
1187
|
* Test aborting silently when handlers are not able to load.
|
1188
|
*/
|
1189
|
function testVariableHandlerFailing() {
|
1190
|
$rule = $this->createTestRule(FALSE, 'node_presave');
|
1191
|
$rule->action('rules_node_publish_action_save', array('node:select' => 'node_unchanged'));
|
1192
|
$rule->integrityCheck()->save();
|
1193
|
|
1194
|
// On insert it's not possible to get the unchanged node during presave.
|
1195
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1196
|
|
1197
|
//debug(RulesLog::logger()->render());
|
1198
|
$text = RulesTestCase::t('Unable to load variable %node_unchanged, aborting.', array('node_unchanged'));
|
1199
|
$this->assertTrue(strpos(RulesLog::logger()->render(), $text) !== FALSE, "Aborted evaluation.");
|
1200
|
}
|
1201
|
|
1202
|
/**
|
1203
|
* Tests preventing recursive rule invocations by creating a rule that reacts
|
1204
|
* on node-update and generates a node update that would trigger it itself.
|
1205
|
*/
|
1206
|
function testRecursionPrevention() {
|
1207
|
$rule = $this->createTestRule(FALSE, 'node_update');
|
1208
|
$rule->action('rules_node_make_sticky_action');
|
1209
|
$rule->integrityCheck()->save();
|
1210
|
|
1211
|
// Now trigger the rule.
|
1212
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1213
|
node_save($node);
|
1214
|
|
1215
|
$text = RulesTestCase::t('Not evaluating reaction rule %label to prevent recursion.', array('label' => $rule->name));
|
1216
|
//debug(RulesLog::logger()->render());
|
1217
|
$this->assertTrue((strpos(RulesLog::logger()->render(), $text) !== FALSE), "Recursion prevented.");
|
1218
|
//debug(RulesLog::logger()->render());
|
1219
|
}
|
1220
|
|
1221
|
/**
|
1222
|
* Ensure the recursion prevention still allows to let the rule trigger again
|
1223
|
* during evaluation of the same event set, if the event isn't caused by the
|
1224
|
* rule itself - thus we won't run in an infinte loop.
|
1225
|
*/
|
1226
|
function testRecursionOnDifferentArguments() {
|
1227
|
// Create rule1 - which might recurse.
|
1228
|
$rule = $this->createTestRule(FALSE, 'node_update');
|
1229
|
$rule->action('rules_node_make_sticky_action');
|
1230
|
$rule->label = 'rule 1';
|
1231
|
$rule->integrityCheck()->save();
|
1232
|
|
1233
|
// Create rule2 - which triggers rule1 on another node.
|
1234
|
$node2 = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1235
|
$rule2 = $this->createTestRule(FALSE, 'node_update');
|
1236
|
$rule2->action('rules_action_load_node', array('nid' => $node2->nid))
|
1237
|
->action('rules_node_make_sticky_action', array('node:select' => 'node_loaded'));
|
1238
|
$rule2->label = 'rule 2';
|
1239
|
$rule2->save();
|
1240
|
|
1241
|
// Now trigger both rules by generating the event.
|
1242
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1243
|
node_save($node);
|
1244
|
|
1245
|
//debug(RulesLog::logger()->render());
|
1246
|
$text = RulesLog::logger()->render();
|
1247
|
$pos = strpos($text, RulesTestCase::t('Evaluating conditions of rule %rule 1', array('rule 1')));
|
1248
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Evaluating conditions of rule %rule 2', array('rule 2')), $pos) : FALSE;
|
1249
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Saved %node_loaded of type %node.', array('node_loaded', 'node')), $pos) : FALSE;
|
1250
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Evaluating conditions of rule %rule 1', array('rule 1')), $pos) : FALSE;
|
1251
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Not evaluating reaction rule %rule 2 to prevent recursion', array('rule 2')), $pos) : FALSE;
|
1252
|
$this->assertTrue($pos !== FALSE, 'Rule1 was triggered on the event caused by Rule2.');
|
1253
|
}
|
1254
|
|
1255
|
/**
|
1256
|
* Tests the provided default rule 'rules_test_default_1'.
|
1257
|
*/
|
1258
|
function testDefaultRule() {
|
1259
|
$rule = rules_config_load('rules_test_default_1');
|
1260
|
$this->assertTrue($rule->status & ENTITY_IN_CODE && !($rule->status & ENTITY_IN_DB), 'Default rule can be loaded and has the right status.');
|
1261
|
// Enable.
|
1262
|
$rule->active = TRUE;
|
1263
|
$rule->save();
|
1264
|
|
1265
|
// Create a node that triggers the rule.
|
1266
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1267
|
// Clear messages.
|
1268
|
drupal_get_messages();
|
1269
|
// Let event node_update occur.
|
1270
|
node_save($node);
|
1271
|
|
1272
|
$msg = drupal_get_messages();
|
1273
|
$this->assertEqual($msg['status'][0], 'A node has been updated.', 'Default rule has been triggered.');
|
1274
|
}
|
1275
|
|
1276
|
/**
|
1277
|
* Tests creating and triggering a reaction rule with event settings.
|
1278
|
*/
|
1279
|
function testEventSettings() {
|
1280
|
$rule = rules_reaction_rule();
|
1281
|
$rule->event('node_presave', array('bundle' => 'article'))
|
1282
|
->condition('data_is_empty', array('data:select' => 'node:field-tags'))
|
1283
|
->action('node_publish', array('node:select' => 'node'));
|
1284
|
$rule->integrityCheck()->save();
|
1285
|
|
1286
|
$node = $this->drupalCreateNode(array('type' => 'page', 'status' => 0));
|
1287
|
$this->assertEqual($node->status, 0, 'Rule has not been triggered.');
|
1288
|
$node = $this->drupalCreateNode(array('type' => 'article', 'status' => 0));
|
1289
|
$this->assertEqual($node->status, 1, 'Rule has been triggered.');
|
1290
|
RulesLog::logger()->checkLog();
|
1291
|
|
1292
|
// Make sure an invalid bundle raises integrity problems.
|
1293
|
$rule->event('node_presave', array('bundle' => 'invalid'));
|
1294
|
try {
|
1295
|
$rule->integrityCheck();
|
1296
|
$this->fail('Integrity check failed.');
|
1297
|
}
|
1298
|
catch (RulesIntegrityException $e) {
|
1299
|
$this->pass('Integrity check failed: ' . $e);
|
1300
|
}
|
1301
|
}
|
1302
|
}
|
1303
|
|
1304
|
/**
|
1305
|
* Tests provided module integration.
|
1306
|
*/
|
1307
|
class RulesIntegrationTestCase extends DrupalWebTestCase {
|
1308
|
|
1309
|
static function getInfo() {
|
1310
|
return array(
|
1311
|
'name' => 'Rules Core Integration',
|
1312
|
'description' => 'Tests provided integration for drupal core.',
|
1313
|
'group' => 'Rules',
|
1314
|
);
|
1315
|
}
|
1316
|
|
1317
|
function setUp() {
|
1318
|
parent::setUp('rules', 'rules_test', 'php', 'path');
|
1319
|
RulesLog::logger()->clear();
|
1320
|
variable_set('rules_debug_log', 1);
|
1321
|
}
|
1322
|
|
1323
|
/**
|
1324
|
* Just make sure the access callback run without errors.
|
1325
|
*/
|
1326
|
function testAccessCallbacks() {
|
1327
|
$cache = rules_get_cache();
|
1328
|
foreach (array('action', 'condition', 'event') as $type) {
|
1329
|
foreach (rules_fetch_data($type . '_info') as $name => $info) {
|
1330
|
if (isset($info['access callback'])) {
|
1331
|
$info['access callback']($type, $name);
|
1332
|
}
|
1333
|
}
|
1334
|
}
|
1335
|
}
|
1336
|
|
1337
|
/**
|
1338
|
* Test data integration.
|
1339
|
*/
|
1340
|
function testDataIntegration() {
|
1341
|
// Test data_create action.
|
1342
|
$action = rules_action('data_create', array(
|
1343
|
'type' => 'log_entry',
|
1344
|
'param_type' => 'rules_test',
|
1345
|
'param_message' => 'Rules test log message',
|
1346
|
'param_severity' => WATCHDOG_WARNING,
|
1347
|
'param_request_uri' => 'http://example.com',
|
1348
|
'param_link' => '',
|
1349
|
));
|
1350
|
$action->access();
|
1351
|
$action->execute();
|
1352
|
$text = RulesLog::logger()->render();
|
1353
|
$pos = strpos($text, RulesTestCase::t('Added the provided variable %data_created of type %log_entry', array('data_created', 'log_entry')));
|
1354
|
$this->assertTrue($pos !== FALSE, 'Data of type log entry has been created.');
|
1355
|
|
1356
|
|
1357
|
// Test variable_add action.
|
1358
|
$action = rules_action('variable_add', array(
|
1359
|
'type' => 'text_formatted',
|
1360
|
'value' => array(
|
1361
|
'value' => 'test text',
|
1362
|
'format' => 1,
|
1363
|
)
|
1364
|
));
|
1365
|
$action->access();
|
1366
|
$action->execute();
|
1367
|
$text = RulesLog::logger()->render();
|
1368
|
$pos = strpos($text, RulesTestCase::t('Added the provided variable %variable_added of type %text_formatted', array('variable_added', 'text_formatted')));
|
1369
|
$this->assertTrue($pos !== FALSE, 'Data of type text formatted has been created.');
|
1370
|
|
1371
|
|
1372
|
// Test using the list actions.
|
1373
|
$rule = rule(array(
|
1374
|
'list' => array(
|
1375
|
'type' => 'list<text>',
|
1376
|
'label' => 'A list of text',
|
1377
|
)
|
1378
|
));
|
1379
|
$rule->action('list_add', array('list:select' => 'list', 'item' => 'bar2'));
|
1380
|
$rule->action('list_add', array('list:select' => 'list', 'item' => 'bar', 'pos' => 'start'));
|
1381
|
$rule->action('list_add', array('list:select' => 'list', 'item' => 'bar', 'unique' => TRUE));
|
1382
|
$rule->action('list_remove', array('list:select' => 'list', 'item' => 'bar2'));
|
1383
|
$list = entity_metadata_wrapper('list', array('foo', 'foo2'));
|
1384
|
$rule->execute($list);
|
1385
|
RulesLog::logger()->checkLog();
|
1386
|
$this->assertEqual($list->value(), array('bar', 'foo', 'foo2'), 'List items removed and added.');
|
1387
|
$this->assertFalse(rules_condition('list_contains')->execute($list, 'foo-bar'), 'Condition "List item contains" evaluates to FALSE');
|
1388
|
$this->assertTrue(rules_condition('list_contains')->execute($list, 'foo'), 'Condition "List item contains" evaluates to TRUE');
|
1389
|
//debug(RulesLog::logger()->render());
|
1390
|
|
1391
|
// Test data_is condition with IN operation.
|
1392
|
$rule = rule(array('node' => array('type' => 'node')));
|
1393
|
$rule->condition('data_is', array('data:select' => 'node:title', 'op' => 'IN', 'value' => array('foo', 'bar')));
|
1394
|
$rule->action('data_set', array('data:select' => 'node:title', 'value' => 'bar'));
|
1395
|
$rule->integrityCheck();
|
1396
|
|
1397
|
$node = $this->drupalCreateNode(array('title' => 'foo'));
|
1398
|
$rule->execute($node);
|
1399
|
$this->assertEqual($node->title, 'bar', "Data comparision using IN operation evaluates to TRUE.");
|
1400
|
|
1401
|
|
1402
|
// Test Condition: Data is empty.
|
1403
|
$rule = rule(array('node' => array('type' => 'node')));
|
1404
|
$rule->condition('data_is_empty', array('data:select' => 'node:title'));
|
1405
|
$rule->action('data_set', array('data:select' => 'node:title', 'value' => 'bar'));
|
1406
|
$rule->integrityCheck();
|
1407
|
|
1408
|
// Data is empty condition evaluates to TRUE
|
1409
|
// for node with empty title, action sets title to 'bar'.
|
1410
|
$node = $this->drupalCreateNode(array('title' => '', 'type' => 'article'));
|
1411
|
$rule->execute($node);
|
1412
|
$this->assertEqual($node->title, 'bar', "Data is empty condition evaluates to TRUE for node with empty title, action sets title to 'bar'.");
|
1413
|
|
1414
|
// Data is empty condition evaluates to FALSE
|
1415
|
// for node with title 'foo', action is not executed.
|
1416
|
$node = $this->drupalCreateNode(array('title' => 'foo', 'type' => 'article'));
|
1417
|
$rule->execute($node);
|
1418
|
$this->assertEqual($node->title, 'foo', "Data is empty condition evaluates to FALSE for node with title 'foo', action is not executed.");
|
1419
|
|
1420
|
// Data is empty condition evaluates to TRUE for the parent of a
|
1421
|
// not existing term in the tags field of the node.
|
1422
|
$rule = rule(array('node' => array('type' => 'node')));
|
1423
|
$rule->condition('node_is_of_type', array('type' => array('article')));
|
1424
|
$rule->condition('data_is_empty', array('data:select' => 'node:field-tags:0:parent'));
|
1425
|
$rule->action('data_set', array('data:select' => 'node:title', 'value' => 'bar'));
|
1426
|
$rule->integrityCheck();
|
1427
|
$node = $this->drupalCreateNode(array('title' => 'foo', 'type' => 'article'));
|
1428
|
$rule->execute($node);
|
1429
|
$this->assertEqual($node->title, 'bar', "Data is empty condition evaluates to TRUE for not existing data structures");
|
1430
|
|
1431
|
// Test Action: Calculate a value.
|
1432
|
$rule = rule(array('node' => array('type' => 'node')));
|
1433
|
$rule->action('data_calc', array('input_1:select' => 'node:nid', 'op' => '*', 'input_2' => 2));
|
1434
|
$rule->action('data_set', array('data:select' => 'node:title', 'value:select' => 'result'));
|
1435
|
$rule->integrityCheck();
|
1436
|
$rule->execute($node);
|
1437
|
$this->assertEqual($node->title, $node->nid * 2, "Value has been calculated.");
|
1438
|
|
1439
|
// Test moving a date.
|
1440
|
$action_set = rules_action_set(array('date' => array('type' => 'date')), array('date'));
|
1441
|
$action_set->action('data_calc', array('input_1:select' => 'date', 'op' => '+', 'input_2' => 3600))
|
1442
|
->action('data_set', array('data:select' => 'date', 'value:select' => 'result'));
|
1443
|
$action_set->integrityCheck();
|
1444
|
list($result) = $action_set->execute(REQUEST_TIME);
|
1445
|
$this->assertEqual($result, REQUEST_TIME + 3600, 'Used data calculation action to move a date by an hour.');
|
1446
|
|
1447
|
// Test data type conversion action.
|
1448
|
$set = rules_action_set(array('result' => array('type' => 'text', 'parameter' => FALSE)), array('result'));
|
1449
|
$set->action('data_convert', array('type' => 'text', 'value:select' => 'site:login-url'));
|
1450
|
$set->action('data_set', array('data:select' => 'result', 'value:select' => 'conversion_result'));
|
1451
|
list($result) = $set->execute();
|
1452
|
$set->integrityCheck();
|
1453
|
$this->assertEqual($result, url('user', array('absolute' => TRUE)), 'Converted URI to text.');
|
1454
|
|
1455
|
$set = rules_action_set(array(
|
1456
|
'result' => array('type' => 'integer', 'parameter' => FALSE),
|
1457
|
'source' => array('type' => 'text'),
|
1458
|
), array('result'));
|
1459
|
$set->action('data_convert', array('type' => 'integer', 'value:select' => 'source'));
|
1460
|
$set->action('data_set', array('data:select' => 'result', 'value:select' => 'conversion_result'));
|
1461
|
list($result) = $set->execute('9.4');
|
1462
|
$this->assertEqual($result, 9, 'Converted decimal to integer using rounding.');
|
1463
|
|
1464
|
$set = rules_action_set(array(
|
1465
|
'result' => array('type' => 'integer', 'parameter' => FALSE),
|
1466
|
'source' => array('type' => 'text'),
|
1467
|
), array('result'));
|
1468
|
$set->action('data_convert', array('type' => 'integer', 'value:select' => 'source', 'rounding_behavior' => 'down'));
|
1469
|
$set->action('data_set', array('data:select' => 'result', 'value:select' => 'conversion_result'));
|
1470
|
list($result) = $set->execute('9.6');
|
1471
|
$this->assertEqual($result, 9, 'Converted decimal to integer using roundin behavio down.');
|
1472
|
|
1473
|
$set = rules_action_set(array(
|
1474
|
'result' => array('type' => 'integer', 'parameter' => FALSE),
|
1475
|
'source' => array('type' => 'text'),
|
1476
|
), array('result'));
|
1477
|
$set->action('data_convert', array('type' => 'integer', 'value:select' => 'source', 'rounding_behavior' => 'up'));
|
1478
|
$set->action('data_set', array('data:select' => 'result', 'value:select' => 'conversion_result'));
|
1479
|
list($result) = $set->execute('9.4');
|
1480
|
$this->assertEqual($result, 10, 'Converted decimal to integer using rounding behavior up.');
|
1481
|
|
1482
|
// Test text matching condition.
|
1483
|
$result = rules_condition('text_matches')->execute('my-text', 'text', 'contains');
|
1484
|
$result2 = rules_condition('text_matches')->execute('my-text', 'tex2t', 'contains');
|
1485
|
$this->assertTrue($result && !$result2, 'Text matching condition using operation contain evaluated.');
|
1486
|
|
1487
|
$result = rules_condition('text_matches')->execute('my-text', 'my', 'starts');
|
1488
|
$result2 = rules_condition('text_matches')->execute('my-text', 'text', 'starts');
|
1489
|
$this->assertTrue($result && !$result2, 'Text matching condition using operation starts evaluated.');
|
1490
|
|
1491
|
$result = rules_condition('text_matches')->execute('my-text', 'text', 'ends');
|
1492
|
$result2 = rules_condition('text_matches')->execute('my-text', 'my', 'ends');
|
1493
|
$this->assertTrue($result && !$result2, 'Text matching condition using operation ends evaluated.');
|
1494
|
|
1495
|
$result = rules_condition('text_matches')->execute('my-text', 'me?y-texx?t', 'regex');
|
1496
|
$result2 = rules_condition('text_matches')->execute('my-text', 'me+y-texx?t', 'regex');
|
1497
|
$this->assertTrue($result && !$result2, 'Text matching condition using operation regex evaluated.');
|
1498
|
}
|
1499
|
|
1500
|
/**
|
1501
|
* Tests entity related integration.
|
1502
|
*/
|
1503
|
function testEntityIntegration() {
|
1504
|
global $user;
|
1505
|
|
1506
|
$page = $this->drupalCreateNode(array('type' => 'page'));
|
1507
|
$article = $this->drupalCreateNode(array('type' => 'article'));
|
1508
|
|
1509
|
$result = rules_condition('entity_field_access')
|
1510
|
->execute(entity_metadata_wrapper('node', $article), 'field_tags');
|
1511
|
$this->assertTrue($result);
|
1512
|
|
1513
|
// Test entiy_is_of_bundle condition.
|
1514
|
$result = rules_condition('entity_is_of_bundle', array(
|
1515
|
'type' => 'node',
|
1516
|
'bundle' => array('article'),
|
1517
|
))->execute(entity_metadata_wrapper('node', $page));
|
1518
|
$this->assertFalse($result, 'Entity is of bundle condition has not been met.');
|
1519
|
$result = rules_condition('entity_is_of_bundle', array(
|
1520
|
'type' => 'node',
|
1521
|
'bundle' => array('article'),
|
1522
|
))->execute(entity_metadata_wrapper('node', $article));
|
1523
|
$this->assertTrue($result, 'Entity is of bundle condition has been met.');
|
1524
|
|
1525
|
// Also test a full rule so the integrity check must work.
|
1526
|
$term_wrapped = entity_property_values_create_entity('taxonomy_term', array(
|
1527
|
'name' => $this->randomName(),
|
1528
|
'vocabulary' => 1,
|
1529
|
))->save();
|
1530
|
$rule = rule(array(
|
1531
|
'node' => array('type' => 'node'),
|
1532
|
));
|
1533
|
$rule->condition('entity_is_of_bundle', array(
|
1534
|
'entity:select' => 'node',
|
1535
|
'bundle' => array('article'),
|
1536
|
));
|
1537
|
$rule->action('data_set', array('data:select' => 'node:field_tags', 'value' => array($term_wrapped->getIdentifier())));
|
1538
|
$rule->integrityCheck();
|
1539
|
$rule->execute($article);
|
1540
|
$this->assertEqual($term_wrapped->getIdentifier(), $article->field_tags[LANGUAGE_NONE][0]['tid'], 'Entity is of bundle condition has been met.');
|
1541
|
|
1542
|
// Test again using an entity variable.
|
1543
|
$article = $this->drupalCreateNode(array('type' => 'article'));
|
1544
|
$rule = rule(array(
|
1545
|
'entity' => array('type' => 'entity'),
|
1546
|
));
|
1547
|
$rule->condition('entity_is_of_bundle', array(
|
1548
|
'entity:select' => 'entity',
|
1549
|
'type' => 'node',
|
1550
|
'bundle' => array('article'),
|
1551
|
));
|
1552
|
$rule->action('data_set', array('data:select' => 'entity:field_tags', 'value' => array($term_wrapped->getIdentifier())));
|
1553
|
$rule->integrityCheck();
|
1554
|
$rule->execute(entity_metadata_wrapper('node', $article));
|
1555
|
$this->assertEqual($term_wrapped->getIdentifier(), $article->field_tags[LANGUAGE_NONE][0]['tid'], 'Entity is of bundle condition has been met.');
|
1556
|
|
1557
|
// Test CRUD actions.
|
1558
|
$action = rules_action('entity_create', array(
|
1559
|
'type' => 'node',
|
1560
|
'param_type' => 'page',
|
1561
|
'param_title' => 'foo',
|
1562
|
'param_author' => $GLOBALS['user'],
|
1563
|
));
|
1564
|
$action->access();
|
1565
|
$action->execute();
|
1566
|
$text = RulesLog::logger()->render();
|
1567
|
$pos = strpos($text, RulesTestCase::t('Added the provided variable %entity_created of type %node', array('entity_created', 'node')));
|
1568
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Saved %entity_created of type %node.', array('entity_created', 'node')), $pos) : FALSE;
|
1569
|
$this->assertTrue($pos !== FALSE, 'Data has been created and saved.');
|
1570
|
|
1571
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1572
|
$rule = rule();
|
1573
|
$rule->action('entity_fetch', array('type' => 'node', 'id' => $node->nid, 'entity_fetched:var' => 'node'));
|
1574
|
$rule->action('entity_save', array('data:select' => 'node', 'immediate' => TRUE));
|
1575
|
$rule->action('entity_delete', array('data:select' => 'node'));
|
1576
|
$rule->access();
|
1577
|
$rule->integrityCheck()->execute();
|
1578
|
|
1579
|
$text = RulesLog::logger()->render();
|
1580
|
$pos = strpos($text, RulesTestCase::t('Evaluating the action %entity_fetch.', array('entity_fetch')));
|
1581
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Added the provided variable %node of type %node', array('node')), $pos) : FALSE;
|
1582
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Saved %node of type %node.', array('node')), $pos) : FALSE;
|
1583
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Evaluating the action %entity_delete.', array('entity_delete')), $pos) : FALSE;
|
1584
|
$this->assertTrue($pos !== FALSE, 'Data has been fetched, saved and deleted.');
|
1585
|
//debug(RulesLog::logger()->render());
|
1586
|
|
1587
|
|
1588
|
|
1589
|
$node = entity_property_values_create_entity('node', array(
|
1590
|
'type' => 'article',
|
1591
|
'author' => $user,
|
1592
|
'title' => 'foo',
|
1593
|
))->value();
|
1594
|
$term_wrapped = entity_property_values_create_entity('taxonomy_term', array(
|
1595
|
'name' => $this->randomName(),
|
1596
|
'vocabulary' => 1,
|
1597
|
))->save();
|
1598
|
|
1599
|
// Test asserting the field and using it afterwards.
|
1600
|
$rule = rule(array('node' => array('type' => 'node')));
|
1601
|
$rule->condition('entity_has_field', array('entity:select' => 'node', 'field' => 'field_tags'));
|
1602
|
$rule->condition('entity_is_new', array('entity:select' => 'node'));
|
1603
|
$rule->action('list_add', array('list:select' => 'node:field-tags', 'item' => $term_wrapped));
|
1604
|
$rule->integrityCheck();
|
1605
|
$rule->execute($node);
|
1606
|
|
1607
|
$tid = $term_wrapped->getIdentifier();
|
1608
|
$this->assertEqual(array_values($node->field_tags[LANGUAGE_NONE]), array(0 => array('tid' => $tid)), 'Entity has field conditions evaluted.');
|
1609
|
|
1610
|
// Test loading a non-node entity.
|
1611
|
$action = rules_action('entity_fetch', array('type' => 'taxonomy_term', 'id' => $tid));
|
1612
|
list($term) = $action->execute();
|
1613
|
$this->assertEqual($term->tid, $tid, 'Fetched a taxonomy term using "entity_fetch".');
|
1614
|
|
1615
|
// Test the entity is of type condition.
|
1616
|
$rule = rule(array('entity' => array('type' => 'entity', 'label' => 'entity')));
|
1617
|
$rule->condition('entity_is_of_type', array('type' => 'node'));
|
1618
|
$rule->action('data_set', array('data:select' => 'entity:title', 'value' => 'bar'));
|
1619
|
$rule->integrityCheck();
|
1620
|
$rule->execute(entity_metadata_wrapper('node', $node));
|
1621
|
|
1622
|
$this->assertEqual(entity_metadata_wrapper('node', $node->nid)->title->value(), 'bar', 'Entity is of type condition correctly asserts the entity type.');
|
1623
|
|
1624
|
|
1625
|
// Test the entity_query action.
|
1626
|
$node = $this->drupalCreateNode(array('type' => 'page', 'title' => 'foo2'));
|
1627
|
$rule = rule();
|
1628
|
$rule->action('entity_query', array('type' => 'node', 'property' => 'title', 'value' => 'foo2'))
|
1629
|
->action('data_set', array('data:select' => 'entity_fetched:0:title', 'value' => 'bar'));
|
1630
|
$rule->access();
|
1631
|
$rule->integrityCheck();
|
1632
|
$rule->execute();
|
1633
|
$node = node_load($node->nid);
|
1634
|
$this->assertEqual('bar', $node->title, 'Fetched a node by title and modified it.');
|
1635
|
|
1636
|
RulesLog::logger()->checkLog();
|
1637
|
}
|
1638
|
|
1639
|
/**
|
1640
|
* Test integration for the taxonomy module.
|
1641
|
*/
|
1642
|
function testTaxonomyIntegration() {
|
1643
|
$term = entity_property_values_create_entity('taxonomy_term', array(
|
1644
|
'name' => $this->randomName(),
|
1645
|
'vocabulary' => 1,
|
1646
|
))->value();
|
1647
|
$term2 = clone $term;
|
1648
|
taxonomy_term_save($term);
|
1649
|
taxonomy_term_save($term2);
|
1650
|
|
1651
|
$tags[LANGUAGE_NONE][0]['tid'] = $term->tid;
|
1652
|
$node = $this->drupalCreateNode(array('title' => 'foo', 'type' => 'article', 'field_tags' => $tags));
|
1653
|
|
1654
|
// Test assigning and remove a term from an article.
|
1655
|
$rule = rule(array('node' => array('type' => 'node', 'bundle' => 'article')));
|
1656
|
$term_wrapped = rules_wrap_data($term->tid, array('type' => 'taxonomy_term'));
|
1657
|
$term_wrapped2 = rules_wrap_data($term2->tid, array('type' => 'taxonomy_term'));
|
1658
|
$rule->action('list_add', array('list:select' => 'node:field-tags', 'item' => $term_wrapped2));
|
1659
|
$rule->action('list_remove', array('list:select' => 'node:field-tags', 'item' => $term_wrapped));
|
1660
|
$rule->execute($node);
|
1661
|
RulesLog::logger()->checkLog();
|
1662
|
$this->assertEqual(array_values($node->field_tags[LANGUAGE_NONE]), array(0 => array('tid' => $term2->tid)), 'Term removed and added from a node.');
|
1663
|
|
1664
|
// Test using the taxonomy term reference field on a term object.
|
1665
|
$field_name = drupal_strtolower($this->randomName() . '_field_name');
|
1666
|
$field = field_create_field(array(
|
1667
|
'field_name' => $field_name,
|
1668
|
'type' => 'taxonomy_term_reference',
|
1669
|
// Set cardinality to unlimited for tagging.
|
1670
|
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
|
1671
|
'settings' => array(
|
1672
|
'allowed_values' => array(
|
1673
|
array(
|
1674
|
'vocabulary' => 'tags',
|
1675
|
'parent' => 0,
|
1676
|
),
|
1677
|
),
|
1678
|
),
|
1679
|
));
|
1680
|
$instance = array(
|
1681
|
'field_name' => $field_name,
|
1682
|
'entity_type' => 'taxonomy_term',
|
1683
|
'bundle' => 'tags', // Machine name of vocabulary.
|
1684
|
'label' => $this->randomName() . '_label',
|
1685
|
'description' => $this->randomName() . '_description',
|
1686
|
'weight' => mt_rand(0, 127),
|
1687
|
'widget' => array(
|
1688
|
'type' => 'taxonomy_autocomplete',
|
1689
|
'weight' => -4,
|
1690
|
),
|
1691
|
'display' => array(
|
1692
|
'default' => array(
|
1693
|
'type' => 'taxonomy_term_reference_link',
|
1694
|
'weight' => 10,
|
1695
|
),
|
1696
|
),
|
1697
|
);
|
1698
|
field_create_instance($instance);
|
1699
|
|
1700
|
$term1 = entity_property_values_create_entity('taxonomy_term', array(
|
1701
|
'name' => $this->randomName(),
|
1702
|
'vocabulary' => 1,
|
1703
|
))->save();
|
1704
|
$term2 = entity_property_values_create_entity('taxonomy_term', array(
|
1705
|
'name' => $this->randomName(),
|
1706
|
'vocabulary' => 1,
|
1707
|
))->save();
|
1708
|
|
1709
|
// Test asserting the term reference field and using it afterwards.
|
1710
|
$rule = rule(array('taxonomy_term' => array('type' => 'taxonomy_term')));
|
1711
|
$rule->condition('entity_has_field', array('entity:select' => 'taxonomy-term', 'field' => $field_name));
|
1712
|
// Add $term2 to $term1 using the term reference field.
|
1713
|
$selector = str_replace('_', '-', 'taxonomy_term:' . $field_name);
|
1714
|
$rule->action('list_add', array('list:select' => $selector, 'item' => $term2));
|
1715
|
$rule->integrityCheck();
|
1716
|
$rule->execute($term1);
|
1717
|
|
1718
|
RulesLog::logger()->checkLog();
|
1719
|
$this->assertEqual($term1->{$field_name}[0]->getIdentifier(), $term2->getIdentifier(), 'Rule appended a term to the term reference field on a term.');
|
1720
|
|
1721
|
// Test an action set for merging term parents, which is provided as default
|
1722
|
// config.
|
1723
|
$term = entity_property_values_create_entity('taxonomy_term', array(
|
1724
|
'name' => $this->randomName(),
|
1725
|
'vocabulary' => 1,
|
1726
|
'parent' => array($term1->value()),
|
1727
|
))->save();
|
1728
|
|
1729
|
$action = rules_action('component_rules_retrieve_term_parents');
|
1730
|
list($parents) = $action->execute(array($term->getIdentifier()));
|
1731
|
$this->assertTrue($parents[0]->tid == $term1->getIdentifier(), 'Invoked component to retrieve term parents.');
|
1732
|
RulesLog::logger()->checkLog();
|
1733
|
}
|
1734
|
|
1735
|
/**
|
1736
|
* Test integration for the node module.
|
1737
|
*/
|
1738
|
function testNodeIntegration() {
|
1739
|
$tests = array(
|
1740
|
array('node_unpublish', 'node_is_published', 'node_publish', 'status'),
|
1741
|
array('node_make_unsticky', 'node_is_sticky', 'node_make_sticky', 'sticky'),
|
1742
|
array('node_unpromote', 'node_is_promoted', 'node_promote', 'promote'),
|
1743
|
);
|
1744
|
$node = $this->drupalCreateNode(array('type' => 'page', 'status' => 1, 'sticky' => 1, 'promote' => 1));
|
1745
|
|
1746
|
foreach ($tests as $info) {
|
1747
|
list($action1, $condition, $action2, $property) = $info;
|
1748
|
rules_action($action1)->execute($node);
|
1749
|
|
1750
|
$node = node_load($node->nid, NULL, TRUE);
|
1751
|
$this->assertFalse($node->$property, 'Action has permanently disabled node '. $property);
|
1752
|
$return = rules_condition($condition)->execute($node);
|
1753
|
$this->assertFalse($return, 'Condition determines node '. $property . ' is disabled.');
|
1754
|
|
1755
|
rules_action($action2)->execute($node);
|
1756
|
$node = node_load($node->nid, NULL, TRUE);
|
1757
|
$this->assertTrue($node->$property, 'Action has permanently enabled node '. $property);
|
1758
|
$return = rules_condition($condition)->execute($node);
|
1759
|
$this->assertTrue($return, 'Condition determines node '. $property . ' is enabled.');
|
1760
|
}
|
1761
|
|
1762
|
$return = rules_condition('node_is_of_type', array('type' => array('page', 'article')))->execute($node);
|
1763
|
$this->assertTrue($return, 'Condition determines node is of type page.');
|
1764
|
$return = rules_condition('node_is_of_type', array('type' => array('article')))->execute($node);
|
1765
|
$this->assertFalse($return, 'Condition determines node is not of type article.');
|
1766
|
|
1767
|
|
1768
|
// Test auto saving of a new node after it has been inserted into the DB.
|
1769
|
$rule = rules_reaction_rule();
|
1770
|
$rand = $this->randomName();
|
1771
|
$rule->event('node_insert')
|
1772
|
->action('data_set', array('data:select' => 'node:title', 'value' => $rand));
|
1773
|
$rule->save('test');
|
1774
|
$node = $this->drupalCreateNode();
|
1775
|
$node = node_load($node->nid);
|
1776
|
$this->assertEqual($node->title, $rand, 'Node title is correct.');
|
1777
|
RulesLog::logger()->checkLog();
|
1778
|
}
|
1779
|
|
1780
|
/**
|
1781
|
* Test integration for the user module.
|
1782
|
*/
|
1783
|
function testUserIntegration() {
|
1784
|
$rid = $this->drupalCreateRole(array('administer nodes'), 'foo');
|
1785
|
$user = $this->drupalCreateUser();
|
1786
|
|
1787
|
// Test assigning a role with the list_add action.
|
1788
|
$rule = rule(array('user' => array('type' => 'user')));
|
1789
|
$rule->action('list_add', array('list:select' => 'user:roles', 'item' => $rid));
|
1790
|
$rule->execute($user);
|
1791
|
$this->assertTrue(isset($user->roles[$rid]), 'Role assigned to user.');
|
1792
|
|
1793
|
// Test removing a role with the list_remove action.
|
1794
|
$rule = rule(array('user' => array('type' => 'user')));
|
1795
|
$rule->action('list_remove', array('list:select' => 'user:roles', 'item' => $rid));
|
1796
|
$rule->execute($user);
|
1797
|
$this->assertTrue(!isset($user->roles[$rid]), 'Role removed from user.');
|
1798
|
|
1799
|
// Test assigning a role with user_add_role action.
|
1800
|
$rule = rule(array('user' => array('type' => 'user')));
|
1801
|
$rule->action('user_add_role', array('account:select' => 'user', 'roles' => array($rid)));
|
1802
|
$rule->execute($user);
|
1803
|
|
1804
|
$user = user_load($user->uid, TRUE);
|
1805
|
$result = rules_condition('user_has_role', array('roles' => array($rid)))->execute($user);
|
1806
|
$this->assertTrue($result, 'Role assigned to user.');
|
1807
|
|
1808
|
// Test removing a role with the user_remove_role action.
|
1809
|
$rule = rule(array('user' => array('type' => 'user')));
|
1810
|
$rule->action('user_remove_role', array('account:select' => 'user', 'roles' => array($rid)));
|
1811
|
$rule->execute($user);
|
1812
|
|
1813
|
$user = user_load($user->uid, TRUE);
|
1814
|
$result = rules_condition('user_has_role', array('roles' => array($rid)))->execute($user);
|
1815
|
$this->assertFalse($result, 'Role removed from user.');
|
1816
|
|
1817
|
// Test user blocking.
|
1818
|
rules_action('user_block')->execute($user);
|
1819
|
$user = user_load($user->uid, TRUE);
|
1820
|
$this->assertTrue(rules_condition('user_is_blocked')->execute($user), 'User has been blocked.');
|
1821
|
|
1822
|
rules_action('user_unblock')->execute($user);
|
1823
|
$user = user_load($user->uid, TRUE);
|
1824
|
$this->assertFalse(rules_condition('user_is_blocked')->execute($user), 'User has been unblocked.');
|
1825
|
|
1826
|
RulesLog::logger()->checkLog();
|
1827
|
}
|
1828
|
|
1829
|
/**
|
1830
|
* Test integration for the php module.
|
1831
|
*/
|
1832
|
function testPHPIntegration() {
|
1833
|
$node = $this->drupalCreateNode(array('title' => 'foo'));
|
1834
|
$rule = rule(array('var_name' => array('type' => 'node')));
|
1835
|
$rule->condition('php_eval', array('code' => 'return TRUE;'))
|
1836
|
->action('php_eval', array('code' => 'drupal_set_message("Executed-" . $var_name->title);'))
|
1837
|
->action('drupal_message', array('message' => 'Title: <?php echo $var_name->title; ?> Token: [var_name:title]'));
|
1838
|
|
1839
|
$rule->execute($node);
|
1840
|
$rule->access();
|
1841
|
RulesLog::logger()->checkLog();
|
1842
|
$msg = drupal_get_messages();
|
1843
|
$this->assertEqual(array_pop($msg['status']), "Title: foo Token: foo", 'PHP input evaluation has been applied.');
|
1844
|
$this->assertEqual(array_pop($msg['status']), "Executed-foo", 'PHP code condition and action have been evaluated.');
|
1845
|
|
1846
|
// Test PHP data processor
|
1847
|
$rule = rule(array('var_name' => array('type' => 'node')));
|
1848
|
$rule->action('drupal_message', array(
|
1849
|
'message:select' => 'var_name:title',
|
1850
|
'message:process' => array(
|
1851
|
'php' => array('code' => 'return "Title: $value";')
|
1852
|
),
|
1853
|
));
|
1854
|
$rule->execute($node);
|
1855
|
$rule->access();
|
1856
|
RulesLog::logger()->checkLog();
|
1857
|
$msg = drupal_get_messages();
|
1858
|
$this->assertEqual(array_pop($msg['status']), "Title: foo", 'PHP data processor has been applied.');
|
1859
|
}
|
1860
|
|
1861
|
/**
|
1862
|
* Test the "rules_core" integration.
|
1863
|
*/
|
1864
|
function testRulesCoreIntegration() {
|
1865
|
// Make sure the date input evaluator evaluates properly using strtotime().
|
1866
|
$node = $this->drupalCreateNode(array('title' => 'foo'));
|
1867
|
$rule = rule(array('node' => array('type' => 'node')));
|
1868
|
$rule->action('data_set', array('data:select' => 'node:created', 'value' => '+1 day'));
|
1869
|
|
1870
|
$rule->execute($node);
|
1871
|
RulesLog::logger()->checkLog();
|
1872
|
$node = node_load($node->nid, NULL, TRUE);
|
1873
|
$now = RulesDateInputEvaluator::gmstrtotime('now');
|
1874
|
// Tolerate a difference of a second.
|
1875
|
$this->assertTrue(abs($node->created - $now - 86400) <= 1, 'Date input has been evaluated.');
|
1876
|
|
1877
|
// Test using a numeric offset.
|
1878
|
$rule = rule(array('number' => array('type' => 'decimal')), array('number'));
|
1879
|
$rule->action('data_set', array(
|
1880
|
'data:select' => 'number',
|
1881
|
'value:select' => 'number',
|
1882
|
'value:process' => array(
|
1883
|
'num_offset' => array('value' => 1),
|
1884
|
),
|
1885
|
));
|
1886
|
$rule->integrityCheck();
|
1887
|
list($result) = $rule->execute(10);
|
1888
|
$this->assertTrue($result == 11, 'Numeric offset has been applied');
|
1889
|
|
1890
|
// Test using a date offset.
|
1891
|
$set = rules_action_set(array('date' => array('type' => 'date')), array('date'));
|
1892
|
$set->action('data_set', array(
|
1893
|
'data:select' => 'date',
|
1894
|
'value:select' => 'date',
|
1895
|
'value:process' => array(
|
1896
|
'date_offset' => array('value' => 1000),
|
1897
|
),
|
1898
|
));
|
1899
|
$date = date_create("14 Mar 1984 10:19:23 +01:00")->format('U');
|
1900
|
list($result) = $set->execute($date);
|
1901
|
$this->assertEqual($result, $date + 1000, 'Date offset in seconds has been added.');
|
1902
|
|
1903
|
// Test using a negative offset of 2 months.
|
1904
|
$set = rules_action_set(array('date' => array('type' => 'date')), array('date'));
|
1905
|
$set->action('data_set', array(
|
1906
|
'data:select' => 'date',
|
1907
|
'value:select' => 'date',
|
1908
|
'value:process' => array(
|
1909
|
'date_offset' => array('value' => - 86400 * 30 * 2),
|
1910
|
),
|
1911
|
));
|
1912
|
$date = date_create("14 Mar 1984 10:19:23 +01:00")->format('U');
|
1913
|
list($result) = $set->execute($date);
|
1914
|
$this->assertEqual($result, date_create("14 Jan 1984 10:19:23 +01:00")->format('U'), 'Date offset of -2 months has been added.');
|
1915
|
|
1916
|
// Test using a positive offset of 1 year 6 months and 30 minutes.
|
1917
|
$set = rules_action_set(array('date' => array('type' => 'date')), array('date'));
|
1918
|
$set->action('data_set', array(
|
1919
|
'data:select' => 'date',
|
1920
|
'value:select' => 'date',
|
1921
|
'value:process' => array(
|
1922
|
'date_offset' => array('value' => 86400 * 30 * 18 + 30 * 60),
|
1923
|
),
|
1924
|
));
|
1925
|
$date = date_create("14 Mar 1984 10:19:23 +01:00")->format('U');
|
1926
|
list($result) = $set->execute($date);
|
1927
|
$this->assertEqual($result, date_create("14 Sep 1985 10:49:23 +01:00")->format('U'), 'Date offset of 1 year 6 months and 30 minutes has been added.');
|
1928
|
|
1929
|
RulesLog::logger()->checkLog();
|
1930
|
}
|
1931
|
|
1932
|
/**
|
1933
|
* Test site/system integration.
|
1934
|
*/
|
1935
|
function testSystemIntegration() {
|
1936
|
// Test using the 'site' variable.
|
1937
|
$condition = rules_condition('data_is', array('data:select' => 'site:current-user:name', 'value' => $GLOBALS['user']->name));
|
1938
|
$this->assertTrue($condition->execute(), 'Retrieved the current user\'s name.');
|
1939
|
// Another test using a token replacement.
|
1940
|
$condition = rules_condition('data_is', array('data:select' => 'site:current-user:name', 'value' => '[site:current-user:name]'));
|
1941
|
$this->assertTrue($condition->execute(), 'Replaced the token for the current user\'s name.');
|
1942
|
|
1943
|
// Test breadcrumbs and drupal set message.
|
1944
|
$rule = rules_reaction_rule();
|
1945
|
$rule->event('init')
|
1946
|
->action('breadcrumb_set', array('titles' => array('foo'), 'paths' => array('bar')))
|
1947
|
->action('drupal_message', array('message' => 'A message.'));
|
1948
|
$rule->save('test');
|
1949
|
|
1950
|
$this->drupalGet('node');
|
1951
|
$this->assertLink('foo', 0, 'Breadcrumb has been set.');
|
1952
|
$this->assertText('A message.', 'Drupal message has been shown.');
|
1953
|
|
1954
|
// Test the page redirect.
|
1955
|
$node = $this->drupalCreateNode();
|
1956
|
$rule = rules_reaction_rule();
|
1957
|
$rule->event('node_view')
|
1958
|
->action('redirect', array('url' => 'user'));
|
1959
|
$rule->save('test2');
|
1960
|
|
1961
|
$this->drupalGet('node/' . $node->nid);
|
1962
|
$this->assertEqual($this->getUrl(), url('user', array('absolute' => TRUE)), 'Redirect has been issued.');
|
1963
|
|
1964
|
// Also test using a url including a fragment.
|
1965
|
$actions = $rule->actions();
|
1966
|
$actions[0]->settings['url'] = 'user#fragment';
|
1967
|
$rule->save();
|
1968
|
|
1969
|
$this->drupalGet('node/' . $node->nid);
|
1970
|
$this->assertEqual($this->getUrl(), url('user', array('absolute' => TRUE, 'fragment' => 'fragment')), 'Redirect has been issued.');
|
1971
|
|
1972
|
|
1973
|
// Test sending mail.
|
1974
|
$settings = array('to' => 'mail@example.com', 'subject' => 'subject', 'message' => 'hello.');
|
1975
|
rules_action('mail', $settings)->execute();
|
1976
|
$this->assertMail('to', 'mail@example.com', 'Mail has been sent.');
|
1977
|
$this->assertMail('from', variable_get('site_mail', ini_get('sendmail_from')), 'Default from address has been used');
|
1978
|
|
1979
|
rules_action('mail', $settings + array('from' => 'sender@example.com'))->execute();
|
1980
|
$this->assertMail('from', 'sender@example.com', 'Specified from address has been used');
|
1981
|
|
1982
|
// Test sending mail to all users of a role. First make sure there is a
|
1983
|
// custom role and a user for it.
|
1984
|
$user = $this->drupalCreateUser(array('administer nodes'));
|
1985
|
$roles = $user->roles;
|
1986
|
// Remove the authenticate role so we only use the new role created by
|
1987
|
// drupalCreateUser().
|
1988
|
unset($roles[DRUPAL_AUTHENTICATED_RID]);
|
1989
|
rules_action('mail_to_users_of_role', $settings + array('roles' => array_keys($roles)))->execute();
|
1990
|
$this->assertMail('to', $user->mail, 'Mail to users of a role has been sent.');
|
1991
|
|
1992
|
// Test reacting on new log entries and make sure the log entry is usable.
|
1993
|
$rule = rules_reaction_rule();
|
1994
|
$rule->event('watchdog');
|
1995
|
$rule->action('drupal_message', array('message:select' => 'log_entry:message'));
|
1996
|
$rule->integrityCheck()->save('test_watchdog');
|
1997
|
|
1998
|
watchdog('php', 'test %message', array('%message' => 'message'));
|
1999
|
$msg = drupal_get_messages();
|
2000
|
$this->assertEqual(array_pop($msg['status']), t('test %message', array('%message' => 'message')), 'Watchdog event occurred and log entry properties can be used.');
|
2001
|
}
|
2002
|
|
2003
|
/**
|
2004
|
* Tests the path module integration.
|
2005
|
*/
|
2006
|
function testPathIntegration() {
|
2007
|
rules_action('path_alias')->execute('foo', 'bar');
|
2008
|
$path = path_load('foo');
|
2009
|
$this->assertTrue($path['alias'] == 'bar', 'URL alias has been created.');
|
2010
|
|
2011
|
$alias_exists = rules_condition('path_alias_exists', array('alias' => 'bar'))->execute();
|
2012
|
$this->assertTrue($alias_exists, 'Created URL alias exists.');
|
2013
|
|
2014
|
$has_alias = rules_condition('path_has_alias', array('source' => 'foo'))->execute();
|
2015
|
$this->assertTrue($has_alias, 'System path has an alias.');
|
2016
|
|
2017
|
// Test node alias action.
|
2018
|
$node = $this->drupalCreateNode();
|
2019
|
rules_action('node_path_alias')->execute($node, 'test');
|
2020
|
$path = path_load("node/$node->nid");
|
2021
|
$this->assertTrue($path['alias'] == 'test', 'Node URL alias has been created.');
|
2022
|
|
2023
|
// Test term alias action.
|
2024
|
$term = entity_property_values_create_entity('taxonomy_term', array(
|
2025
|
'name' => $this->randomName(),
|
2026
|
'vocabulary' => 1,
|
2027
|
))->value();
|
2028
|
rules_action('taxonomy_term_path_alias')->execute($term, 'term-test');
|
2029
|
$path = path_load("taxonomy/term/$term->tid");
|
2030
|
$this->assertTrue($path['alias'] == 'term-test', 'Term URL alias has been created.');
|
2031
|
|
2032
|
RulesLog::logger()->checkLog();
|
2033
|
}
|
2034
|
}
|
2035
|
|
2036
|
/**
|
2037
|
* Test event dispatcher functionality.
|
2038
|
*/
|
2039
|
class RulesEventDispatcherTestCase extends DrupalWebTestCase {
|
2040
|
|
2041
|
static function getInfo() {
|
2042
|
return array(
|
2043
|
'name' => 'Rules event dispatchers',
|
2044
|
'description' => 'Tests event dispatcher functionality.',
|
2045
|
'group' => 'Rules',
|
2046
|
);
|
2047
|
}
|
2048
|
|
2049
|
function setUp() {
|
2050
|
parent::setUp('rules', 'rules_test');
|
2051
|
}
|
2052
|
|
2053
|
/**
|
2054
|
* Tests start and stop functionality.
|
2055
|
*/
|
2056
|
public function testStartAndStop() {
|
2057
|
$handler = rules_get_event_handler('rules_test_event');
|
2058
|
$rule = rules_reaction_rule();
|
2059
|
$rule->event('rules_test_event');
|
2060
|
|
2061
|
// The handler should not yet be watching.
|
2062
|
$this->assertFalse($handler->isWatching());
|
2063
|
|
2064
|
// Once saved, the event cache rebuild should start the watcher.
|
2065
|
$rule->save();
|
2066
|
RulesEventSet::rebuildEventCache();
|
2067
|
$this->assertTrue($handler->isWatching());
|
2068
|
|
2069
|
// Deleting should stop the watcher.
|
2070
|
$rule->delete();
|
2071
|
$this->assertFalse($handler->isWatching());
|
2072
|
}
|
2073
|
|
2074
|
/**
|
2075
|
* Tests start and stop functionality when used with multiple events.
|
2076
|
*/
|
2077
|
public function testStartAndStopMultiple() {
|
2078
|
$handler = rules_get_event_handler('rules_test_event');
|
2079
|
|
2080
|
// Initially, the task handler should not be watching.
|
2081
|
$this->assertFalse($handler->isWatching());
|
2082
|
|
2083
|
// Set up five rules that all use the same event.
|
2084
|
$rules = array();
|
2085
|
foreach (array(1, 2, 3, 4, 5) as $key) {
|
2086
|
$rules[$key] = rules_reaction_rule();
|
2087
|
$rules[$key]->event('rules_test_event');
|
2088
|
$rules[$key]->save();
|
2089
|
}
|
2090
|
|
2091
|
// Once saved, the event cache rebuild should start the watcher.
|
2092
|
RulesEventSet::rebuildEventCache();
|
2093
|
$this->assertTrue($handler->isWatching());
|
2094
|
|
2095
|
// It should continue watching until all events are deleted.
|
2096
|
foreach ($rules as $key => $rule) {
|
2097
|
$rule->delete();
|
2098
|
$this->assertEqual($key !== 5, $handler->isWatching());
|
2099
|
}
|
2100
|
}
|
2101
|
}
|
2102
|
|
2103
|
/**
|
2104
|
* Test early bootstrap Rules invocation.
|
2105
|
*/
|
2106
|
class RulesInvocationEnabledTestCase extends DrupalWebTestCase {
|
2107
|
|
2108
|
/**
|
2109
|
* {@inheritdoc}
|
2110
|
*/
|
2111
|
public static function getInfo() {
|
2112
|
return array(
|
2113
|
'name' => 'Rules invocation enabled',
|
2114
|
'description' => 'Tests that Rules events are enabled during menu item loads.',
|
2115
|
'group' => 'Rules',
|
2116
|
);
|
2117
|
}
|
2118
|
|
2119
|
/**
|
2120
|
* {@inheritdoc}
|
2121
|
*/
|
2122
|
public function setUp() {
|
2123
|
parent::setUp('dblog', 'rules', 'rules_test', 'rules_test_invocation');
|
2124
|
}
|
2125
|
|
2126
|
/**
|
2127
|
* Tests that a Rules event is triggered on node menu item loading.
|
2128
|
*
|
2129
|
* @see rules_test_invocation_node_load()
|
2130
|
*/
|
2131
|
public function testInvocationOnNodeMenuLoading() {
|
2132
|
// Create a test node.
|
2133
|
$node = $this->drupalCreateNode(array('title' => 'Test'));
|
2134
|
// Enable Rules logging on the INFO level so that entries are written to
|
2135
|
// dblog.
|
2136
|
variable_set('rules_log_errors', RulesLog::INFO);
|
2137
|
// Create an empty rule that will fire in our node load hook.
|
2138
|
$rule = rules_reaction_rule();
|
2139
|
$rule->event('rules_test_event');
|
2140
|
$rule->save('test_rule');
|
2141
|
|
2142
|
// Visit the node page which should trigger the load hook.
|
2143
|
$this->drupalGet('node/' . $node->nid);
|
2144
|
$result = db_query("SELECT * FROM {watchdog} WHERE type = 'rules' AND message = 'Reacting on event %label.'")->fetch();
|
2145
|
$this->assertFalse(empty($result), 'Rules event was triggered and logged.');
|
2146
|
}
|
2147
|
|
2148
|
}
|