BehaviorRegistryTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\ORM;
  16. use Cake\Core\Plugin;
  17. use Cake\ORM\BehaviorRegistry;
  18. use Cake\ORM\Table;
  19. use Cake\TestSuite\TestCase;
  20. /**
  21. * Test case for BehaviorRegistry.
  22. */
  23. class BehaviorRegistryTest extends TestCase
  24. {
  25. /**
  26. * setup method.
  27. *
  28. * @return void
  29. */
  30. public function setUp()
  31. {
  32. parent::setUp();
  33. $this->Table = new Table(['table' => 'articles']);
  34. $this->EventManager = $this->Table->getEventManager();
  35. $this->Behaviors = new BehaviorRegistry($this->Table);
  36. static::setAppNamespace();
  37. }
  38. /**
  39. * tearDown
  40. *
  41. * @return void
  42. */
  43. public function tearDown()
  44. {
  45. $this->clearPlugins();
  46. unset($this->Table, $this->EventManager, $this->Behaviors);
  47. parent::tearDown();
  48. }
  49. /**
  50. * Test classname resolution.
  51. *
  52. * @return void
  53. */
  54. public function testClassName()
  55. {
  56. $this->loadPlugins(['TestPlugin']);
  57. $expected = 'Cake\ORM\Behavior\TranslateBehavior';
  58. $result = BehaviorRegistry::className('Translate');
  59. $this->assertSame($expected, $result);
  60. $expected = 'TestPlugin\Model\Behavior\PersisterOneBehavior';
  61. $result = BehaviorRegistry::className('TestPlugin.PersisterOne');
  62. $this->assertSame($expected, $result);
  63. $this->assertNull(BehaviorRegistry::className('NonExistent'));
  64. }
  65. /**
  66. * Test loading behaviors.
  67. *
  68. * @return void
  69. */
  70. public function testLoad()
  71. {
  72. $this->loadPlugins(['TestPlugin']);
  73. $config = ['alias' => 'Sluggable', 'replacement' => '-'];
  74. $result = $this->Behaviors->load('Sluggable', $config);
  75. $this->assertInstanceOf('TestApp\Model\Behavior\SluggableBehavior', $result);
  76. $this->assertEquals($config, $result->getConfig());
  77. $result = $this->Behaviors->load('TestPlugin.PersisterOne');
  78. $this->assertInstanceOf('TestPlugin\Model\Behavior\PersisterOneBehavior', $result);
  79. }
  80. /**
  81. * Test load() binding listeners.
  82. *
  83. * @return void
  84. */
  85. public function testLoadBindEvents()
  86. {
  87. $result = $this->EventManager->listeners('Model.beforeFind');
  88. $this->assertCount(0, $result);
  89. $this->Behaviors->load('Sluggable');
  90. $result = $this->EventManager->listeners('Model.beforeFind');
  91. $this->assertCount(1, $result);
  92. $this->assertInstanceOf('TestApp\Model\Behavior\SluggableBehavior', $result[0]['callable'][0]);
  93. $this->assertEquals('beforeFind', $result[0]['callable'][1], 'Method name should match.');
  94. }
  95. /**
  96. * Test load() with enabled = false
  97. *
  98. * @return void
  99. */
  100. public function testLoadEnabledFalse()
  101. {
  102. $result = $this->EventManager->listeners('Model.beforeFind');
  103. $this->assertCount(0, $result);
  104. $this->Behaviors->load('Sluggable', ['enabled' => false]);
  105. $result = $this->EventManager->listeners('Model.beforeFind');
  106. $this->assertCount(0, $result);
  107. }
  108. /**
  109. * Test loading plugin behaviors
  110. *
  111. * @return void
  112. */
  113. public function testLoadPlugin()
  114. {
  115. $this->loadPlugins(['TestPlugin']);
  116. $result = $this->Behaviors->load('TestPlugin.PersisterOne');
  117. $expected = 'TestPlugin\Model\Behavior\PersisterOneBehavior';
  118. $this->assertInstanceOf($expected, $result);
  119. $this->assertInstanceOf($expected, $this->Behaviors->PersisterOne);
  120. $this->Behaviors->unload('PersisterOne');
  121. $result = $this->Behaviors->load('TestPlugin.PersisterOne', ['foo' => 'bar']);
  122. $this->assertInstanceOf($expected, $result);
  123. $this->assertInstanceOf($expected, $this->Behaviors->PersisterOne);
  124. }
  125. /**
  126. * Test load() on undefined class
  127. *
  128. * @return void
  129. */
  130. public function testLoadMissingClass()
  131. {
  132. $this->expectException(\Cake\ORM\Exception\MissingBehaviorException::class);
  133. $this->Behaviors->load('DoesNotExist');
  134. }
  135. /**
  136. * Test load() duplicate method error
  137. *
  138. * @return void
  139. */
  140. public function testLoadDuplicateMethodError()
  141. {
  142. $this->expectException(\LogicException::class);
  143. $this->expectExceptionMessage('TestApp\Model\Behavior\DuplicateBehavior contains duplicate method "slugify"');
  144. $this->Behaviors->load('Sluggable');
  145. $this->Behaviors->load('Duplicate');
  146. }
  147. /**
  148. * Test load() duplicate method aliasing
  149. *
  150. * @return void
  151. */
  152. public function testLoadDuplicateMethodAliasing()
  153. {
  154. $this->Behaviors->load('Tree');
  155. $this->Behaviors->load('Duplicate', [
  156. 'implementedFinders' => [
  157. 'renamed' => 'findChildren',
  158. ],
  159. 'implementedMethods' => [
  160. 'renamed' => 'slugify',
  161. ],
  162. ]);
  163. $this->assertTrue($this->Behaviors->hasMethod('renamed'));
  164. }
  165. /**
  166. * Test load() duplicate finder error
  167. *
  168. * @return void
  169. */
  170. public function testLoadDuplicateFinderError()
  171. {
  172. $this->expectException(\LogicException::class);
  173. $this->expectExceptionMessage('TestApp\Model\Behavior\DuplicateBehavior contains duplicate finder "children"');
  174. $this->Behaviors->load('Tree');
  175. $this->Behaviors->load('Duplicate');
  176. }
  177. /**
  178. * Test load() duplicate finder aliasing
  179. *
  180. * @return void
  181. */
  182. public function testLoadDuplicateFinderAliasing()
  183. {
  184. $this->Behaviors->load('Tree');
  185. $this->Behaviors->load('Duplicate', [
  186. 'implementedFinders' => [
  187. 'renamed' => 'findChildren',
  188. ],
  189. ]);
  190. $this->assertTrue($this->Behaviors->hasFinder('renamed'));
  191. }
  192. /**
  193. * test hasMethod()
  194. *
  195. * @return void
  196. */
  197. public function testHasMethod()
  198. {
  199. $this->loadPlugins(['TestPlugin']);
  200. $this->Behaviors->load('TestPlugin.PersisterOne');
  201. $this->Behaviors->load('Sluggable');
  202. $this->assertTrue($this->Behaviors->hasMethod('slugify'));
  203. $this->assertTrue($this->Behaviors->hasMethod('SLUGIFY'));
  204. $this->assertTrue($this->Behaviors->hasMethod('persist'));
  205. $this->assertTrue($this->Behaviors->hasMethod('PERSIST'));
  206. $this->assertFalse($this->Behaviors->hasMethod('__construct'));
  207. $this->assertFalse($this->Behaviors->hasMethod('config'));
  208. $this->assertFalse($this->Behaviors->hasMethod('implementedEvents'));
  209. $this->assertFalse($this->Behaviors->hasMethod('nope'));
  210. $this->assertFalse($this->Behaviors->hasMethod('beforeFind'));
  211. $this->assertFalse($this->Behaviors->hasMethod('noSlug'));
  212. }
  213. /**
  214. * Test hasFinder() method.
  215. *
  216. * @return void
  217. */
  218. public function testHasFinder()
  219. {
  220. $this->Behaviors->load('Sluggable');
  221. $this->assertTrue($this->Behaviors->hasFinder('noSlug'));
  222. $this->assertTrue($this->Behaviors->hasFinder('noslug'));
  223. $this->assertTrue($this->Behaviors->hasFinder('NOSLUG'));
  224. $this->assertFalse($this->Behaviors->hasFinder('slugify'));
  225. $this->assertFalse($this->Behaviors->hasFinder('beforeFind'));
  226. $this->assertFalse($this->Behaviors->hasFinder('nope'));
  227. }
  228. /**
  229. * test call
  230. *
  231. * Setup a behavior, then replace it with a mock to verify methods are called.
  232. * use dummy return values to verify the return value makes it back
  233. *
  234. * @return void
  235. */
  236. public function testCall()
  237. {
  238. $this->Behaviors->load('Sluggable');
  239. $mockedBehavior = $this->getMockBuilder('Cake\ORM\Behavior')
  240. ->setMethods(['slugify'])
  241. ->disableOriginalConstructor()
  242. ->getMock();
  243. $this->Behaviors->set('Sluggable', $mockedBehavior);
  244. $mockedBehavior
  245. ->expects($this->once())
  246. ->method('slugify')
  247. ->with(['some value'])
  248. ->will($this->returnValue('some-thing'));
  249. $return = $this->Behaviors->call('slugify', [['some value']]);
  250. $this->assertSame('some-thing', $return);
  251. }
  252. /**
  253. * Test errors on unknown methods.
  254. *
  255. */
  256. public function testCallError()
  257. {
  258. $this->expectException(\BadMethodCallException::class);
  259. $this->expectExceptionMessage('Cannot call "nope"');
  260. $this->Behaviors->load('Sluggable');
  261. $this->Behaviors->call('nope');
  262. }
  263. /**
  264. * test call finder
  265. *
  266. * Setup a behavior, then replace it with a mock to verify methods are called.
  267. * use dummy return values to verify the return value makes it back
  268. *
  269. * @return void
  270. */
  271. public function testCallFinder()
  272. {
  273. $this->Behaviors->load('Sluggable');
  274. $mockedBehavior = $this->getMockBuilder('Cake\ORM\Behavior')
  275. ->setMethods(['findNoSlug'])
  276. ->disableOriginalConstructor()
  277. ->getMock();
  278. $this->Behaviors->set('Sluggable', $mockedBehavior);
  279. $query = $this->getMockBuilder('Cake\ORM\Query')
  280. ->setConstructorArgs([null, null])
  281. ->getMock();
  282. $mockedBehavior
  283. ->expects($this->once())
  284. ->method('findNoSlug')
  285. ->with($query, [])
  286. ->will($this->returnValue('example'));
  287. $return = $this->Behaviors->callFinder('noSlug', [$query, []]);
  288. $this->assertSame('example', $return);
  289. }
  290. /**
  291. * Test errors on unknown methods.
  292. *
  293. */
  294. public function testCallFinderError()
  295. {
  296. $this->expectException(\BadMethodCallException::class);
  297. $this->expectExceptionMessage('Cannot call finder "nope"');
  298. $this->Behaviors->load('Sluggable');
  299. $this->Behaviors->callFinder('nope');
  300. }
  301. /**
  302. * Test errors on unloaded behavior methods.
  303. *
  304. */
  305. public function testUnloadBehaviorThenCall()
  306. {
  307. $this->expectException(\BadMethodCallException::class);
  308. $this->expectExceptionMessage('Cannot call "slugify" it does not belong to any attached behavior.');
  309. $this->Behaviors->load('Sluggable');
  310. $this->Behaviors->unload('Sluggable');
  311. $this->Behaviors->call('slugify');
  312. }
  313. /**
  314. * Test errors on unloaded behavior finders.
  315. *
  316. */
  317. public function testUnloadBehaviorThenCallFinder()
  318. {
  319. $this->expectException(\BadMethodCallException::class);
  320. $this->expectExceptionMessage('Cannot call finder "noslug" it does not belong to any attached behavior.');
  321. $this->Behaviors->load('Sluggable');
  322. $this->Behaviors->unload('Sluggable');
  323. $this->Behaviors->callFinder('noSlug');
  324. }
  325. /**
  326. * Test that unloading then reloading a behavior does not throw any errors.
  327. *
  328. * @return void
  329. */
  330. public function testUnloadBehaviorThenReload()
  331. {
  332. $this->Behaviors->load('Sluggable');
  333. $this->Behaviors->unload('Sluggable');
  334. $this->assertEmpty($this->Behaviors->loaded());
  335. $this->Behaviors->load('Sluggable');
  336. $this->assertEquals(['Sluggable'], $this->Behaviors->loaded());
  337. }
  338. /**
  339. * Test that unloading a none existing behavior triggers an error.
  340. *
  341. * @return void
  342. */
  343. public function testUnload()
  344. {
  345. $this->Behaviors->load('Sluggable');
  346. $this->Behaviors->unload('Sluggable');
  347. $this->assertEmpty($this->Behaviors->loaded());
  348. $this->assertCount(0, $this->EventManager->listeners('Model.beforeFind'));
  349. }
  350. /**
  351. * Test that unloading a none existing behavior triggers an error.
  352. *
  353. * @return void
  354. */
  355. public function testUnloadUnknown()
  356. {
  357. $this->expectException(\Cake\ORM\Exception\MissingBehaviorException::class);
  358. $this->expectExceptionMessage('Behavior class FooBehavior could not be found.');
  359. $this->Behaviors->unload('Foo');
  360. }
  361. /**
  362. * Test setTable() method.
  363. *
  364. * @return void
  365. */
  366. public function testSetTable()
  367. {
  368. $table = $this->getMockBuilder('Cake\ORM\Table')->getMock();
  369. $table->expects($this->once())->method('getEventManager');
  370. $this->Behaviors->setTable($table);
  371. }
  372. }