FixtureManagerTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\TestSuite;
  17. use Cake\Core\Exception\CakeException;
  18. use Cake\Database\Driver\Sqlserver;
  19. use Cake\Database\Schema\TableSchema;
  20. use Cake\Datasource\ConnectionManager;
  21. use Cake\Log\Log;
  22. use Cake\TestSuite\Fixture\FixtureManager;
  23. use Cake\TestSuite\Stub\ConsoleOutput;
  24. use Cake\TestSuite\TestCase;
  25. use PDOException;
  26. /**
  27. * Fixture manager test case.
  28. */
  29. class FixtureManagerTest extends TestCase
  30. {
  31. /**
  32. * @var string[]
  33. */
  34. protected $cleanup = [];
  35. /**
  36. * @var \Cake\TestSuite\Fixture\FixtureManager
  37. */
  38. protected $manager;
  39. /**
  40. * Setup method
  41. */
  42. public function setUp(): void
  43. {
  44. parent::setUp();
  45. $this->manager = new FixtureManager();
  46. }
  47. public function tearDown(): void
  48. {
  49. parent::tearDown();
  50. Log::reset();
  51. $this->clearPlugins();
  52. foreach ($this->cleanup as $name) {
  53. $table = $this->getTableLocator()->get($name);
  54. $table->deleteAll('1=1');
  55. }
  56. }
  57. /**
  58. * Test loading core fixtures.
  59. */
  60. public function testFixturizeCore(): void
  61. {
  62. $this->cleanup = ['articles'];
  63. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  64. $test->expects($this->any())
  65. ->method('getFixtures')
  66. ->willReturn(['core.Articles']);
  67. $this->manager->fixturize($test);
  68. $fixtures = $this->manager->loaded();
  69. $this->manager->unload($test);
  70. $this->assertCount(1, $fixtures);
  71. $this->assertArrayHasKey('core.Articles', $fixtures);
  72. $this->assertInstanceOf('Cake\Test\Fixture\ArticlesFixture', $fixtures['core.Articles']);
  73. }
  74. /**
  75. * Test logging depends on fixture manager debug.
  76. */
  77. public function testLogSchemaWithDebug(): void
  78. {
  79. $db = ConnectionManager::get('test');
  80. $restore = $db->isQueryLoggingEnabled();
  81. $db->enableQueryLogging(true);
  82. $this->manager->setDebug(true);
  83. $buffer = new ConsoleOutput();
  84. Log::setConfig('testQueryLogger', [
  85. 'className' => 'Console',
  86. 'stream' => $buffer,
  87. ]);
  88. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  89. $test->expects($this->any())
  90. ->method('getFixtures')
  91. ->willReturn(['core.Articles']);
  92. $this->manager->fixturize($test);
  93. // Need to load/shutdown twice to ensure fixture is created.
  94. $this->manager->load($test);
  95. $this->manager->shutdown();
  96. $this->manager->load($test);
  97. $this->manager->shutdown();
  98. $db->enableQueryLogging($restore);
  99. $this->assertStringContainsString('CREATE TABLE', implode('', $buffer->messages()));
  100. }
  101. /**
  102. * Test that if a table already exists in the test database, it will dropped
  103. * before being recreated
  104. */
  105. public function testResetDbIfTableExists(): void
  106. {
  107. $db = ConnectionManager::get('test');
  108. $restore = $db->isQueryLoggingEnabled();
  109. $db->enableQueryLogging(true);
  110. $this->manager->setDebug(true);
  111. $buffer = new ConsoleOutput();
  112. Log::setConfig('testQueryLogger', [
  113. 'className' => 'Console',
  114. 'stream' => $buffer,
  115. ]);
  116. $table = new TableSchema('articles', [
  117. 'id' => ['type' => 'integer', 'unsigned' => true],
  118. 'title' => ['type' => 'string', 'length' => 255],
  119. ]);
  120. $table->addConstraint('primary', ['type' => 'primary', 'columns' => ['id']]);
  121. $sql = $table->createSql($db);
  122. foreach ($sql as $stmt) {
  123. $db->execute($stmt);
  124. }
  125. $this->cleanup = ['articles'];
  126. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  127. $test->expects($this->any())
  128. ->method('getFixtures')
  129. ->willReturn(['core.Articles']);
  130. $this->manager->fixturize($test);
  131. $this->manager->load($test);
  132. $db->enableQueryLogging($restore);
  133. $this->assertStringContainsString('DROP TABLE', implode('', $buffer->messages()));
  134. }
  135. /**
  136. * Test loading fixtures with constraints.
  137. */
  138. public function testFixturizeCoreConstraint(): void
  139. {
  140. $driver = ConnectionManager::get('test')->getDriver();
  141. $this->skipIf($driver instanceof Sqlserver, 'This fails in SQLServer');
  142. $this->cleanup = ['authors', 'authors_tags'];
  143. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  144. $test->expects($this->any())
  145. ->method('getFixtures')
  146. ->willReturn(['core.Authors', 'core.AuthorsTags']);
  147. $this->manager->fixturize($test);
  148. $this->manager->load($test);
  149. $table = $this->getTableLocator()->get('AuthorsTags');
  150. $schema = $table->getSchema();
  151. $expectedConstraint = [
  152. 'type' => 'foreign',
  153. 'columns' => ['author_id'],
  154. 'references' => ['authors', 'id'],
  155. 'update' => 'cascade',
  156. 'delete' => 'cascade',
  157. 'length' => [],
  158. ];
  159. $this->assertSame($expectedConstraint, $schema->getConstraint('author_id_fk'));
  160. $this->manager->unload($test);
  161. }
  162. /**
  163. * Test loading plugin fixtures.
  164. */
  165. public function testFixturizePlugin(): void
  166. {
  167. $this->loadPlugins(['TestPlugin']);
  168. $this->cleanup = ['articles'];
  169. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  170. $test->expects($this->any())
  171. ->method('getFixtures')
  172. ->willReturn(['plugin.TestPlugin.Articles']);
  173. $this->manager->fixturize($test);
  174. $fixtures = $this->manager->loaded();
  175. $this->assertCount(1, $fixtures);
  176. $this->assertArrayHasKey('plugin.TestPlugin.Articles', $fixtures);
  177. $this->assertInstanceOf(
  178. 'TestPlugin\Test\Fixture\ArticlesFixture',
  179. $fixtures['plugin.TestPlugin.Articles']
  180. );
  181. }
  182. /**
  183. * Test loading plugin fixtures.
  184. */
  185. public function testFixturizePluginSubdirectory(): void
  186. {
  187. $this->loadPlugins(['TestPlugin']);
  188. $this->cleanup = ['comments'];
  189. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  190. $test->expects($this->any())
  191. ->method('getFixtures')
  192. ->willReturn(['plugin.TestPlugin.Blog/Comments']);
  193. $this->manager->fixturize($test);
  194. $fixtures = $this->manager->loaded();
  195. $this->assertCount(1, $fixtures);
  196. $this->assertArrayHasKey('plugin.TestPlugin.Blog/Comments', $fixtures);
  197. $this->assertInstanceOf(
  198. 'TestPlugin\Test\Fixture\Blog\CommentsFixture',
  199. $fixtures['plugin.TestPlugin.Blog/Comments']
  200. );
  201. }
  202. /**
  203. * Test loading plugin fixtures from a vendor namespaced plugin
  204. */
  205. public function testFixturizeVendorPlugin(): void
  206. {
  207. $this->cleanup = ['articles'];
  208. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  209. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  210. $test->expects($this->any())
  211. ->method('getFixtures')
  212. ->willReturn(['plugin.Company/TestPluginThree.Articles']);
  213. $this->manager->fixturize($test);
  214. $fixtures = $this->manager->loaded();
  215. $this->assertCount(1, $fixtures);
  216. $this->assertArrayHasKey('plugin.Company/TestPluginThree.Articles', $fixtures);
  217. $this->assertInstanceOf(
  218. 'Company\TestPluginThree\Test\Fixture\ArticlesFixture',
  219. $fixtures['plugin.Company/TestPluginThree.Articles']
  220. );
  221. }
  222. /**
  223. * Test loading fixtures with fully-qualified namespaces.
  224. */
  225. public function testFixturizeClassName(): void
  226. {
  227. $this->cleanup = ['articles'];
  228. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  229. $test->expects($this->any())
  230. ->method('getFixtures')
  231. ->willReturn(['Company\TestPluginThree\Test\Fixture\ArticlesFixture']);
  232. $this->manager->fixturize($test);
  233. $fixtures = $this->manager->loaded();
  234. $this->assertCount(1, $fixtures);
  235. $this->assertArrayHasKey('Company\TestPluginThree\Test\Fixture\ArticlesFixture', $fixtures);
  236. $this->assertInstanceOf(
  237. 'Company\TestPluginThree\Test\Fixture\ArticlesFixture',
  238. $fixtures['Company\TestPluginThree\Test\Fixture\ArticlesFixture']
  239. );
  240. }
  241. /**
  242. * Test that unknown types are handled gracefully.
  243. */
  244. public function testFixturizeInvalidType(): void
  245. {
  246. $this->expectException(\UnexpectedValueException::class);
  247. $this->expectExceptionMessage('Referenced fixture class "Test\Fixture\Derp.DerpFixture" not found. Fixture "Derp.Derp" was referenced');
  248. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  249. $test->expects($this->any())
  250. ->method('getFixtures')
  251. ->willReturn(['Derp.Derp']);
  252. $this->manager->fixturize($test);
  253. }
  254. /**
  255. * Test load uses aliased connections via a mock.
  256. *
  257. * Ensure that FixtureManager uses connection aliases
  258. * protecting 'live' tables from being wiped by mistakes in
  259. * fixture connection names.
  260. */
  261. public function testLoadConnectionAliasUsage(): void
  262. {
  263. $connection = ConnectionManager::get('test');
  264. $statement = $this->getMockBuilder('Cake\Database\StatementInterface')
  265. ->getMock();
  266. // This connection should _not_ be used.
  267. $other = $this->getMockBuilder('Cake\Database\Connection')
  268. ->onlyMethods(['execute'])
  269. ->setConstructorArgs([['driver' => $connection->getDriver()]])
  270. ->getMock();
  271. $other->expects($this->never())
  272. ->method('execute')
  273. ->will($this->returnValue($statement));
  274. // This connection should be used instead of
  275. // the 'other' connection as the alias should not be ignored.
  276. $testOther = $this->getMockBuilder('Cake\Database\Connection')
  277. ->onlyMethods(['execute'])
  278. ->setConstructorArgs([[
  279. 'database' => $connection->config()['database'],
  280. 'driver' => $connection->getDriver(),
  281. ]])
  282. ->getMock();
  283. $testOther->expects($this->atLeastOnce())
  284. ->method('execute')
  285. ->will($this->returnValue($statement));
  286. ConnectionManager::setConfig('other', $other);
  287. ConnectionManager::setConfig('test_other', $testOther);
  288. // Connect the alias making test_other an alias of other.
  289. ConnectionManager::alias('test_other', 'other');
  290. $this->cleanup = ['articles'];
  291. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  292. $test->expects($this->any())
  293. ->method('getFixtures')
  294. ->willReturn(['core.OtherArticles']);
  295. $this->manager->fixturize($test);
  296. $this->manager->load($test);
  297. ConnectionManager::drop('other');
  298. ConnectionManager::drop('test_other');
  299. }
  300. /**
  301. * Test loading fixtures using loadSingle()
  302. */
  303. public function testLoadSingle(): void
  304. {
  305. $this->cleanup = ['comments', 'users'];
  306. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')
  307. ->onlyMethods(['getFixtures'])
  308. ->getMock();
  309. $test->autoFixtures = false;
  310. $test->expects($this->any())
  311. ->method('getFixtures')
  312. ->willReturn(['core.Comments', 'core.Users']);
  313. $this->manager->fixturize($test);
  314. $this->assertEquals([], $this->manager->getInserted());
  315. $this->manager->loadSingle('Comments');
  316. $this->manager->loadSingle('Users');
  317. $this->assertEquals(['comments', 'users'], $this->manager->getInserted());
  318. $table = $this->getTableLocator()->get('Users');
  319. $results = $table->find('all')->toArray();
  320. $schema = $table->getSchema();
  321. $expectedConstraint = [
  322. 'type' => 'primary',
  323. 'columns' => [
  324. 'id',
  325. ],
  326. 'length' => [],
  327. ];
  328. $this->assertSame($expectedConstraint, $schema->getConstraint('primary'));
  329. $this->assertCount(4, $results);
  330. $this->manager->unload($test);
  331. }
  332. /**
  333. * Test exception on load
  334. */
  335. public function testExceptionOnLoad(): void
  336. {
  337. $this->cleanup = ['products'];
  338. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  339. $test->expects($this->any())
  340. ->method('getFixtures')
  341. ->willReturn(['core.Products']);
  342. $manager = $this->getMockBuilder(FixtureManager::class)
  343. ->onlyMethods(['_runOperation'])
  344. ->getMock();
  345. $manager->expects($this->any())
  346. ->method('_runOperation')
  347. ->will($this->returnCallback(function (): void {
  348. throw new PDOException('message');
  349. }));
  350. $manager->fixturize($test);
  351. $e = null;
  352. try {
  353. $manager->load($test);
  354. } catch (\Exception $e) {
  355. }
  356. $this->assertNotNull($e);
  357. $this->assertMatchesRegularExpression('/^Unable to insert fixtures for "Mock_TestCase_\w+" test case. message$/D', $e->getMessage());
  358. $this->assertInstanceOf('PDOException', $e->getPrevious());
  359. }
  360. /**
  361. * Test exception on load fixture
  362. *
  363. * @dataProvider loadErrorMessageProvider
  364. */
  365. public function testExceptionOnLoadFixture(string $method, string $expectedMessage): void
  366. {
  367. $fixture = $this->getMockBuilder('Cake\Test\Fixture\ProductsFixture')
  368. ->onlyMethods(['drop', 'create', $method])
  369. ->getMock();
  370. $fixture->expects($this->once())
  371. ->method($method)
  372. ->will($this->returnCallback(function (): void {
  373. throw new PDOException('message');
  374. }));
  375. $fixtures = [
  376. 'core.Products' => $fixture,
  377. ];
  378. $test = $this->getMockBuilder('Cake\TestSuite\TestCase')->getMock();
  379. $test->expects($this->any())
  380. ->method('getFixtures')
  381. ->willReturn(array_keys($fixtures));
  382. /** @var \Cake\TestSuite\Fixture\FixtureManager|\PHPUnit\Framework\MockObject\MockObject $manager */
  383. $manager = $this->getMockBuilder(FixtureManager::class)
  384. ->onlyMethods(['_fixtureConnections'])
  385. ->getMock();
  386. $manager->expects($this->any())
  387. ->method('_fixtureConnections')
  388. ->will($this->returnValue([
  389. 'test' => $fixtures,
  390. ]));
  391. $manager->fixturize($test);
  392. $e = null;
  393. try {
  394. $manager->load($test);
  395. } catch (CakeException $e) {
  396. }
  397. $this->assertNotNull($e);
  398. $this->assertMatchesRegularExpression($expectedMessage, $e->getMessage());
  399. $this->assertInstanceOf('PDOException', $e->getPrevious());
  400. }
  401. /**
  402. * Data provider for testExceptionOnLoadFixture
  403. *
  404. * @return array
  405. */
  406. public function loadErrorMessageProvider(): array
  407. {
  408. return [
  409. [
  410. 'createConstraints',
  411. '/^Unable to create constraints for fixture "Mock_ProductsFixture_\w+" in "Mock_TestCase_\w+" test case: \nmessage$/D',
  412. ],
  413. [
  414. 'dropConstraints',
  415. '/^Unable to drop constraints for fixture "Mock_ProductsFixture_\w+" in "Mock_TestCase_\w+" test case: \nmessage$/D',
  416. ],
  417. [
  418. 'insert',
  419. '/^Unable to insert fixture "Mock_ProductsFixture_\w+" in "Mock_TestCase_\w+" test case: \nmessage$/D',
  420. ],
  421. ];
  422. }
  423. }