3 declare(strict_types
=1);
5 namespace PhpMyAdmin\Tests\Navigation\Nodes
;
7 use PhpMyAdmin\DatabaseInterface
;
8 use PhpMyAdmin\Navigation\NodeFactory
;
9 use PhpMyAdmin\Navigation\Nodes\Node
;
10 use PhpMyAdmin\Tests\AbstractTestCase
;
13 class NodeTest
extends AbstractTestCase
16 * SetUp for test cases
18 protected function setUp(): void
21 parent
::loadDefaultConfig();
22 $GLOBALS['server'] = 0;
23 $GLOBALS['cfg']['Server']['DisableIS'] = false;
29 public function testAddNode(): void
31 $parent = NodeFactory
::getInstance('Node', 'parent');
32 $child = NodeFactory
::getInstance('Node', 'child');
33 $parent->addChild($child);
35 $parent->getChild($child->name
),
39 $parent->getChild($child->realName
, true),
47 public function testGetChildError(): void
49 $parent = NodeFactory
::getInstance('Node', 'parent');
51 $parent->getChild('foo')
54 $parent->getChild('foo', true)
61 public function testRemoveNode(): void
63 $parent = NodeFactory
::getInstance('Node', 'parent');
64 $child = NodeFactory
::getInstance('Node', 'child');
65 $parent->addChild($child);
67 $parent->getChild($child->name
),
70 $parent->removeChild($child->name
);
72 $parent->getChild($child->name
)
77 * SetUp for hasChildren
79 public function testNodeHasChildren(): void
81 $parent = NodeFactory
::getInstance();
82 $emptyContainer = NodeFactory
::getInstance(
87 $child = NodeFactory
::getInstance();
88 // test with no children
90 $parent->hasChildren(true),
94 $parent->hasChildren(false),
97 // test with an empty container
98 $parent->addChild($emptyContainer);
100 $parent->hasChildren(true),
104 $parent->hasChildren(false),
107 // test with a real child
108 $parent->addChild($child);
110 $parent->hasChildren(true),
114 $parent->hasChildren(false),
120 * SetUp for numChildren
122 public function testNumChildren(): void
124 // start with root node only
125 $parent = NodeFactory
::getInstance();
126 $this->assertEquals($parent->numChildren(), 0);
128 $child = NodeFactory
::getInstance();
129 $parent->addChild($child);
130 $this->assertEquals($parent->numChildren(), 1);
131 // add a direct grandchild, this one doesn't count as
132 // it's not enclosed in a CONTAINER
133 $child->addChild(NodeFactory
::getInstance());
134 $this->assertEquals($parent->numChildren(), 1);
135 // add a container, this one doesn't count wither
136 $container = NodeFactory
::getInstance(
141 $parent->addChild($container);
142 $this->assertEquals($parent->numChildren(), 1);
143 // add a grandchild to container, this one counts
144 $container->addChild(NodeFactory
::getInstance());
145 $this->assertEquals($parent->numChildren(), 2);
146 // add another grandchild to container, this one counts
147 $container->addChild(NodeFactory
::getInstance());
148 $this->assertEquals($parent->numChildren(), 3);
154 public function testParents(): void
156 $parent = NodeFactory
::getInstance();
157 $this->assertEquals($parent->parents(), []); // exclude self
158 $this->assertEquals($parent->parents(true), [$parent]); // include self
160 $child = NodeFactory
::getInstance();
161 $parent->addChild($child);
163 $this->assertEquals($child->parents(), [$parent]); // exclude self
165 $child->parents(true),
174 * SetUp for realParent
176 public function testRealParent(): void
178 $parent = NodeFactory
::getInstance();
179 $this->assertFalse($parent->realParent());
181 $child = NodeFactory
::getInstance();
182 $parent->addChild($child);
183 $this->assertEquals($child->realParent(), $parent);
187 * Tests whether Node->hasSiblings() method returns false
188 * when the node does not have any siblings.
192 public function testHasSiblingsWithNoSiblings(): void
194 $parent = NodeFactory
::getInstance();
195 $child = NodeFactory
::getInstance();
196 $parent->addChild($child);
197 $this->assertFalse($child->hasSiblings());
201 * Tests whether Node->hasSiblings() method returns true
202 * when it actually has siblings.
206 public function testHasSiblingsWithSiblings(): void
208 $parent = NodeFactory
::getInstance();
209 $firstChild = NodeFactory
::getInstance();
210 $parent->addChild($firstChild);
211 $secondChild = NodeFactory
::getInstance();
212 $parent->addChild($secondChild);
213 // Normal case; two Node:NODE type siblings
214 $this->assertTrue($firstChild->hasSiblings());
216 $parent = NodeFactory
::getInstance();
217 $firstChild = NodeFactory
::getInstance();
218 $parent->addChild($firstChild);
219 $secondChild = NodeFactory
::getInstance(
224 $parent->addChild($secondChild);
225 // Empty Node::CONTAINER type node should not be considered in hasSiblings()
226 $this->assertFalse($firstChild->hasSiblings());
228 $grandChild = NodeFactory
::getInstance();
229 $secondChild->addChild($grandChild);
230 // Node::CONTAINER type nodes with children are counted for hasSiblings()
231 $this->assertTrue($firstChild->hasSiblings());
235 * It is expected that Node->hasSiblings() method always return true
236 * for Nodes that are 3 levels deep (columns and indexes).
240 public function testHasSiblingsForNodesAtLevelThree(): void
242 $parent = NodeFactory
::getInstance();
243 $child = NodeFactory
::getInstance();
244 $parent->addChild($child);
245 $grandChild = NodeFactory
::getInstance();
246 $child->addChild($grandChild);
247 $greatGrandChild = NodeFactory
::getInstance();
248 $grandChild->addChild($greatGrandChild);
250 // Should return false for node that are two levels deeps
251 $this->assertFalse($grandChild->hasSiblings());
252 // Should return true for node that are three levels deeps
253 $this->assertTrue($greatGrandChild->hasSiblings());
257 * Tests private method _getWhereClause()
261 public function testGetWhereClause(): void
263 $method = new ReflectionMethod(
267 $method->setAccessible(true);
270 $node = NodeFactory
::getInstance();
273 $method->invoke($node, 'SCHEMA_NAME')
276 // When a schema names is passed as search clause
278 "WHERE TRUE AND `SCHEMA_NAME` LIKE '%schemaName%' ",
279 $method->invoke($node, 'SCHEMA_NAME', 'schemaName')
282 if (! isset($GLOBALS['cfg']['Server'])) {
283 $GLOBALS['cfg']['Server'] = [];
286 // When hide_db regular expression is present
287 $GLOBALS['cfg']['Server']['hide_db'] = 'regexpHideDb';
289 "WHERE TRUE AND `SCHEMA_NAME` NOT REGEXP 'regexpHideDb' ",
290 $method->invoke($node, 'SCHEMA_NAME')
292 unset($GLOBALS['cfg']['Server']['hide_db']);
294 // When only_db directive is present and it's a single db
295 $GLOBALS['cfg']['Server']['only_db'] = 'stringOnlyDb';
297 "WHERE TRUE AND ( `SCHEMA_NAME` LIKE 'stringOnlyDb' ) ",
298 $method->invoke($node, 'SCHEMA_NAME')
300 unset($GLOBALS['cfg']['Server']['only_db']);
302 // When only_db directive is present and it's an array of dbs
303 $GLOBALS['cfg']['Server']['only_db'] = [
308 "WHERE TRUE AND ( `SCHEMA_NAME` LIKE 'onlyDbOne' "
309 . "OR `SCHEMA_NAME` LIKE 'onlyDbTwo' ) ",
310 $method->invoke($node, 'SCHEMA_NAME')
312 unset($GLOBALS['cfg']['Server']['only_db']);
316 * Tests getData() method when DisableIS is false and navigation tree
321 public function testGetDataWithEnabledISAndGroupingEnabled(): void
325 $GLOBALS['cfg']['Server']['DisableIS'] = false;
326 $GLOBALS['cfg']['NavigationTreeEnableGrouping'] = true;
327 $GLOBALS['cfg']['FirstLevelNavigationItems'] = $limit;
328 $GLOBALS['cfg']['NavigationTreeDbSeparator'] = '_';
330 $expectedSql = 'SELECT `SCHEMA_NAME` ';
331 $expectedSql .= 'FROM `INFORMATION_SCHEMA`.`SCHEMATA`, ';
333 $expectedSql .= 'SELECT DB_first_level ';
334 $expectedSql .= 'FROM ( ';
335 $expectedSql .= 'SELECT DISTINCT SUBSTRING_INDEX(SCHEMA_NAME, ';
336 $expectedSql .= "'_', 1) ";
337 $expectedSql .= 'DB_first_level ';
338 $expectedSql .= 'FROM INFORMATION_SCHEMA.SCHEMATA ';
339 $expectedSql .= 'WHERE TRUE ';
340 $expectedSql .= ') t ';
341 $expectedSql .= 'ORDER BY DB_first_level ASC ';
342 $expectedSql .= 'LIMIT ' . $pos . ', ' . $limit;
343 $expectedSql .= ') t2 ';
344 $expectedSql .= "WHERE TRUE AND 1 = LOCATE(CONCAT(DB_first_level, '_'), ";
345 $expectedSql .= "CONCAT(SCHEMA_NAME, '_')) ";
346 $expectedSql .= 'ORDER BY SCHEMA_NAME ASC';
348 // It would have been better to mock _getWhereClause method
349 // but strangely, mocking private methods is not supported in PHPUnit
350 $node = NodeFactory
::getInstance();
352 $dbi = $this->getMockBuilder(DatabaseInterface
::class)
353 ->disableOriginalConstructor()
355 $dbi->expects($this->once())
356 ->method('fetchResult')
357 ->with($expectedSql);
358 $dbi->expects($this->any())->method('escapeString')
359 ->will($this->returnArgument(0));
360 $GLOBALS['dbi'] = $dbi;
361 $node->getData('', $pos);
365 * Tests getData() method when DisableIS is false and navigation tree
370 public function testGetDataWithEnabledISAndGroupingDisabled(): void
374 $GLOBALS['cfg']['Server']['DisableIS'] = false;
375 $GLOBALS['cfg']['NavigationTreeEnableGrouping'] = false;
376 $GLOBALS['cfg']['FirstLevelNavigationItems'] = $limit;
378 $expectedSql = 'SELECT `SCHEMA_NAME` ';
379 $expectedSql .= 'FROM `INFORMATION_SCHEMA`.`SCHEMATA` ';
380 $expectedSql .= 'WHERE TRUE ';
381 $expectedSql .= 'ORDER BY `SCHEMA_NAME` ';
382 $expectedSql .= 'LIMIT ' . $pos . ', ' . $limit;
384 // It would have been better to mock _getWhereClause method
385 // but strangely, mocking private methods is not supported in PHPUnit
386 $node = NodeFactory
::getInstance();
388 $dbi = $this->getMockBuilder(DatabaseInterface
::class)
389 ->disableOriginalConstructor()
391 $dbi->expects($this->once())
392 ->method('fetchResult')
393 ->with($expectedSql);
394 $dbi->expects($this->any())->method('escapeString')
395 ->will($this->returnArgument(0));
397 $GLOBALS['dbi'] = $dbi;
398 $node->getData('', $pos);
402 * Tests getData() method when DisableIS is true and navigation tree
407 public function testGetDataWithDisabledISAndGroupingEnabled(): void
411 $GLOBALS['cfg']['Server']['DisableIS'] = true;
412 $GLOBALS['dbs_to_test'] = false;
413 $GLOBALS['cfg']['NavigationTreeEnableGrouping'] = true;
414 $GLOBALS['cfg']['FirstLevelNavigationItems'] = $limit;
415 $GLOBALS['cfg']['NavigationTreeDbSeparator'] = '_';
417 $node = NodeFactory
::getInstance();
419 $dbi = $this->getMockBuilder(DatabaseInterface
::class)
420 ->disableOriginalConstructor()
422 $dbi->expects($this->once())
424 ->with("SHOW DATABASES WHERE TRUE AND `Database` LIKE '%db%' ")
425 ->will($this->returnValue(true));
426 $dbi->expects($this->exactly(3))
427 ->method('fetchArray')
428 ->willReturnOnConsecutiveCalls(
434 $dbi->expects($this->once())
435 ->method('fetchResult')
437 "SHOW DATABASES WHERE TRUE AND `Database` LIKE '%db%' AND ("
438 . " LOCATE('db_', CONCAT(`Database`, '_')) = 1"
439 . " OR LOCATE('aa_', CONCAT(`Database`, '_')) = 1 )"
441 $dbi->expects($this->any())->method('escapeString')
442 ->will($this->returnArgument(0));
444 $GLOBALS['dbi'] = $dbi;
445 $node->getData('', $pos, 'db');
449 * Tests the getPresence method when DisableIS is false and navigation tree
454 public function testGetPresenceWithEnabledISAndGroupingEnabled(): void
456 $GLOBALS['cfg']['Server']['DisableIS'] = false;
457 $GLOBALS['cfg']['NavigationTreeEnableGrouping'] = true;
458 $GLOBALS['cfg']['NavigationTreeDbSeparator'] = '_';
460 $query = 'SELECT COUNT(*) ';
462 $query .= "SELECT DISTINCT SUBSTRING_INDEX(SCHEMA_NAME, '_', 1) ";
463 $query .= 'DB_first_level ';
464 $query .= 'FROM INFORMATION_SCHEMA.SCHEMATA ';
465 $query .= 'WHERE TRUE ';
468 // It would have been better to mock _getWhereClause method
469 // but strangely, mocking private methods is not supported in PHPUnit
470 $node = NodeFactory
::getInstance();
472 $dbi = $this->getMockBuilder(DatabaseInterface
::class)
473 ->disableOriginalConstructor()
475 $dbi->expects($this->once())
476 ->method('fetchValue')
478 $GLOBALS['dbi'] = $dbi;
479 $node->getPresence();
483 * Tests the getPresence method when DisableIS is false and navigation tree
488 public function testGetPresenceWithEnabledISAndGroupingDisabled(): void
490 $GLOBALS['cfg']['Server']['DisableIS'] = false;
491 $GLOBALS['cfg']['NavigationTreeEnableGrouping'] = false;
493 $query = 'SELECT COUNT(*) ';
494 $query .= 'FROM INFORMATION_SCHEMA.SCHEMATA ';
495 $query .= 'WHERE TRUE ';
497 $node = NodeFactory
::getInstance();
498 $dbi = $this->getMockBuilder(DatabaseInterface
::class)
499 ->disableOriginalConstructor()
501 $dbi->expects($this->once())
502 ->method('fetchValue')
504 $GLOBALS['dbi'] = $dbi;
505 $node->getPresence();
509 * Tests the getPresence method when DisableIS is true
513 public function testGetPresenceWithDisabledIS(): void
515 $GLOBALS['cfg']['Server']['DisableIS'] = true;
516 $GLOBALS['dbs_to_test'] = false;
517 $GLOBALS['cfg']['NavigationTreeEnableGrouping'] = true;
519 $node = NodeFactory
::getInstance();
521 // test with no search clause
522 $dbi = $this->getMockBuilder(DatabaseInterface
::class)
523 ->disableOriginalConstructor()
525 $dbi->expects($this->once())
527 ->with('SHOW DATABASES WHERE TRUE ');
528 $dbi->expects($this->any())->method('escapeString')
529 ->will($this->returnArgument(0));
531 $GLOBALS['dbi'] = $dbi;
532 $node->getPresence();
534 // test with a search clause
535 $dbi = $this->getMockBuilder(DatabaseInterface
::class)
536 ->disableOriginalConstructor()
538 $dbi->expects($this->once())
540 ->with("SHOW DATABASES WHERE TRUE AND `Database` LIKE '%dbname%' ");
541 $dbi->expects($this->any())->method('escapeString')
542 ->will($this->returnArgument(0));
544 $GLOBALS['dbi'] = $dbi;
545 $node->getPresence('', 'dbname');