ConnectionTest.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. <?php
  2. /**
  3. * PHP Version 5.4
  4. *
  5. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  6. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  7. *
  8. * Licensed under The MIT License
  9. * For full copyright and license information, please see the LICENSE.txt
  10. * Redistributions of files must retain the above copyright notice.
  11. *
  12. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  13. * @link http://cakephp.org CakePHP(tm) Project
  14. * @since CakePHP(tm) v 3.0.0
  15. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  16. */
  17. namespace Cake\Test\TestCase\Database;
  18. use Cake\Core\Configure;
  19. use Cake\Database\Connection;
  20. use Cake\Database\ConnectionManager;
  21. use Cake\TestSuite\TestCase;
  22. /**
  23. * Tests Connection class
  24. */
  25. class ConnectionTest extends TestCase {
  26. public function setUp() {
  27. parent::setUp();
  28. $this->connection = ConnectionManager::get('test');
  29. }
  30. public function tearDown() {
  31. parent::tearDown();
  32. $this->connection->execute('DROP TABLE IF EXISTS things');
  33. $this->connection->useSavePoints(false);
  34. unset($this->connection);
  35. }
  36. /**
  37. * Auxiliary method to build a mock for a driver so it can be injected into
  38. * the connection object
  39. *
  40. * @return \Cake\Database\Driver
  41. */
  42. public function getMockFormDriver() {
  43. $driver = $this->getMock('Cake\Database\Driver');
  44. $driver->expects($this->once())
  45. ->method('enabled')
  46. ->will($this->returnValue(true));
  47. return $driver;
  48. }
  49. /**
  50. * Tests connecting to database
  51. *
  52. * @return void
  53. */
  54. public function testConnect() {
  55. $this->assertTrue($this->connection->connect());
  56. $this->assertTrue($this->connection->isConnected());
  57. }
  58. /**
  59. * Tests creating a connection using an invalid driver throws an exception
  60. *
  61. * @expectedException Cake\Database\Exception\MissingDriverException
  62. * @expectedExceptionMessage Database driver \Foo\InvalidDriver could not be found.
  63. * @return void
  64. */
  65. public function testMissingDriver() {
  66. $connection = new Connection(['datasource' => '\Foo\InvalidDriver']);
  67. }
  68. /**
  69. * Tests trying to use a disabled driver throws an exception
  70. *
  71. * @expectedException Cake\Database\Exception\MissingExtensionException
  72. * @expectedExceptionMessage Database driver DriverMock cannot be used due to a missing PHP extension or unmet dependency
  73. * @return void
  74. */
  75. public function testDisabledDriver() {
  76. $mock = $this->getMock('\Cake\Database\Connection\Driver', ['enabled'], [], 'DriverMock');
  77. $connection = new Connection(['datasource' => $mock]);
  78. }
  79. /**
  80. * Tests that connecting with invalid credentials or database name throws an exception
  81. *
  82. * @expectedException \Cake\Database\Exception\MissingConnectionException
  83. * @return void
  84. **/
  85. public function testWrongCredentials() {
  86. $config = ConnectionManager::config('test');
  87. $this->skipIf(isset($config['dsn']), 'Datasource has dsn, skipping.');
  88. $connection = new Connection(['database' => '_probably_not_there_'] + ConnectionManager::config('test'));
  89. $connection->connect();
  90. }
  91. /**
  92. * Tests disconnecting from database
  93. *
  94. * @return void
  95. **/
  96. public function testDisconnect() {
  97. $this->assertTrue($this->connection->connect());
  98. $this->assertTrue($this->connection->isConnected());
  99. $this->connection->disconnect();
  100. $this->assertFalse($this->connection->isConnected());
  101. }
  102. /**
  103. * Tests creation of prepared statements
  104. *
  105. * @return void
  106. **/
  107. public function testPrepare() {
  108. $sql = 'SELECT 1 + 1';
  109. $result = $this->connection->prepare($sql);
  110. $this->assertInstanceOf('Cake\Database\StatementInterface', $result);
  111. $this->assertEquals($sql, $result->queryString);
  112. }
  113. /**
  114. * Tests executing a simple query using bound values
  115. *
  116. * @return void
  117. **/
  118. public function testExecuteWithArguments() {
  119. $sql = 'SELECT 1 + ?';
  120. $statement = $this->connection->execute($sql, [1], array('integer'));
  121. $this->assertCount(1, $statement);
  122. $result = $statement->fetch();
  123. $this->assertEquals([2], $result);
  124. $sql = 'SELECT 1 + ? + ? AS total';
  125. $statement = $this->connection->execute($sql, [2, 3], array('integer', 'integer'));
  126. $this->assertCount(1, $statement);
  127. $result = $statement->fetch('assoc');
  128. $this->assertEquals(['total' => 6], $result);
  129. $sql = 'SELECT 1 + :one + :two AS total';
  130. $statement = $this->connection->execute($sql, ['one' => 2, 'two' => 3], array('one' => 'integer', 'two' => 'integer'));
  131. $this->assertCount(1, $statement);
  132. $result = $statement->fetch('assoc');
  133. $this->assertEquals(['total' => 6], $result);
  134. }
  135. /**
  136. * Tests executing a query with params and associated types
  137. *
  138. * @return void
  139. **/
  140. public function testExecuteWithArgumentsAndTypes() {
  141. $sql = "SELECT ? = '2012-01-01'";
  142. $statement = $this->connection->execute($sql, [new \DateTime('2012-01-01')], ['date']);
  143. $result = $statement->fetch();
  144. $this->assertTrue((bool)$result[0]);
  145. $sql = "SELECT ? = '2012-01-01', ? = '2000-01-01 10:10:10', ? = 2";
  146. $params = [new \DateTime('2012-01-01 10:10:10'), '2000-01-01 10:10:10', 2.1];
  147. $statement = $this->connection->execute($sql, $params, ['date', 'string', 'integer']);
  148. $result = $statement->fetch();
  149. $this->assertEquals($result, array_filter($result));
  150. }
  151. /**
  152. * Tests that passing a unknown value to a query throws an exception
  153. *
  154. * @expectedException \InvalidArgumentException
  155. * @return void
  156. **/
  157. public function testExecuteWithMissingType() {
  158. $sql = 'SELECT ?';
  159. $statement = $this->connection->execute($sql, [new \DateTime('2012-01-01')], ['bar']);
  160. }
  161. /**
  162. * Tests executing a query with no params also works
  163. *
  164. * @return void
  165. **/
  166. public function testExecuteWithNoParams() {
  167. $sql = 'SELECT 1';
  168. $statement = $this->connection->execute($sql);
  169. $result = $statement->fetch();
  170. $this->assertCount(1, $result);
  171. $this->assertEquals([1], $result);
  172. }
  173. /**
  174. * Tests it is possible to insert data into a table using matching types by key name
  175. *
  176. * @return void
  177. **/
  178. public function testInsertWithMatchingTypes() {
  179. $table = 'CREATE TEMPORARY TABLE things(id int, title varchar(20), body varchar(50))';
  180. $this->connection->execute($table);
  181. $data = ['id' => '1', 'title' => 'a title', 'body' => 'a body'];
  182. $result = $this->connection->insert(
  183. 'things',
  184. $data,
  185. ['id' => 'integer', 'title' => 'string', 'body' => 'string']
  186. );
  187. $this->assertInstanceOf('Cake\Database\StatementInterface', $result);
  188. $result = $this->connection->execute('SELECT * from things');
  189. $this->assertCount(1, $result);
  190. $row = $result->fetch('assoc');
  191. $this->assertEquals($data, $row);
  192. }
  193. /**
  194. * Tests it is possible to insert data into a table using matching types by array position
  195. *
  196. * @return void
  197. **/
  198. public function testInsertWithPositionalTypes() {
  199. $table = 'CREATE TEMPORARY TABLE things(id int, title varchar(20), body varchar(50))';
  200. $this->connection->execute($table);
  201. $data = ['id' => '1', 'title' => 'a title', 'body' => 'a body'];
  202. $result = $this->connection->insert(
  203. 'things',
  204. $data,
  205. ['integer', 'string', 'string']
  206. );
  207. $this->assertInstanceOf('Cake\Database\StatementInterface', $result);
  208. $result = $this->connection->execute('SELECT * from things');
  209. $this->assertCount(1, $result);
  210. $row = $result->fetch('assoc');
  211. $this->assertEquals($data, $row);
  212. }
  213. /**
  214. * Auxiliary function to insert a couple rows in a newly created table
  215. *
  216. * @return void
  217. **/
  218. protected function _insertTwoRecords() {
  219. $table = 'CREATE TEMPORARY TABLE things(id int, title varchar(20), body varchar(50))';
  220. $this->connection->execute($table);
  221. $data = ['id' => '1', 'title' => 'a title', 'body' => 'a body'];
  222. $result = $this->connection->insert(
  223. 'things',
  224. $data,
  225. ['id' => 'integer', 'title' => 'string', 'body' => 'string']
  226. );
  227. $result->bindValue(1, '2', 'integer');
  228. $result->bindValue(2, 'another title');
  229. $result->bindValue(3, 'another body');
  230. $result->execute();
  231. }
  232. /**
  233. * Tests an statement class can be reused for multiple executions
  234. *
  235. * @return void
  236. **/
  237. public function testStatementReusing() {
  238. $this->_insertTwoRecords();
  239. $total = $this->connection->execute('SELECT COUNT(*) AS total FROM things');
  240. $total = $total->fetch('assoc');
  241. $this->assertEquals(2, $total['total']);
  242. $result = $this->connection->execute('SELECT title, body FROM things');
  243. $row = $result->fetch('assoc');
  244. $this->assertEquals('a title', $row['title']);
  245. $this->assertEquals('a body', $row['body']);
  246. $row = $result->fetch('assoc');
  247. $this->assertEquals('another title', $row['title']);
  248. $this->assertEquals('another body', $row['body']);
  249. }
  250. /**
  251. * Tests rows can be updated without specifying any conditions nor types
  252. *
  253. * @return void
  254. **/
  255. public function testUpdateWithoutConditionsNorTypes() {
  256. $this->_insertTwoRecords();
  257. $title = 'changed the title!';
  258. $body = 'changed the body!';
  259. $this->connection->update('things', ['title' => $title, 'body' => $body]);
  260. $result = $this->connection->execute('SELECT * FROM things WHERE title = ? AND body = ?', [$title, $body]);
  261. $this->assertCount(2, $result);
  262. }
  263. /**
  264. * Tests it is possible to use key => value conditions for update
  265. *
  266. * @return void
  267. **/
  268. public function testUpdateWithConditionsNoTypes() {
  269. $this->_insertTwoRecords();
  270. $title = 'changed the title!';
  271. $body = 'changed the body!';
  272. $this->connection->update('things', ['title' => $title, 'body' => $body], ['id' => 2]);
  273. $result = $this->connection->execute('SELECT * FROM things WHERE title = ? AND body = ?', [$title, $body]);
  274. $this->assertCount(1, $result);
  275. }
  276. /**
  277. * Tests it is possible to use key => value and string conditions for update
  278. *
  279. * @return void
  280. **/
  281. public function testUpdateWithConditionsCombinedNoTypes() {
  282. $this->_insertTwoRecords();
  283. $title = 'changed the title!';
  284. $body = 'changed the body!';
  285. $this->connection->update('things', ['title' => $title, 'body' => $body], ['id' => 2, 'body is not null']);
  286. $result = $this->connection->execute('SELECT * FROM things WHERE title = ? AND body = ?', [$title, $body]);
  287. $this->assertCount(1, $result);
  288. }
  289. /**
  290. * Tests you can bind types to update values
  291. *
  292. * @return void
  293. **/
  294. public function testUpdateWithTypes() {
  295. $this->_insertTwoRecords();
  296. $title = 'changed the title!';
  297. $body = new \DateTime('2012-01-01');
  298. $values = compact('title', 'body');
  299. $this->connection->update('things', $values, [], ['body' => 'date']);
  300. $result = $this->connection->execute('SELECT * FROM things WHERE title = :title AND body = :body', $values, ['body' => 'date']);
  301. $this->assertCount(2, $result);
  302. $row = $result->fetch('assoc');
  303. $this->assertEquals('2012-01-01', $row['body']);
  304. $row = $result->fetch('assoc');
  305. $this->assertEquals('2012-01-01', $row['body']);
  306. }
  307. /**
  308. * Tests you can bind types to update values
  309. *
  310. * @return void
  311. **/
  312. public function testUpdateWithConditionsAndTypes() {
  313. $this->_insertTwoRecords();
  314. $title = 'changed the title!';
  315. $body = new \DateTime('2012-01-01');
  316. $values = compact('title', 'body');
  317. $this->connection->update('things', $values, ['id' => '1-string-parsed-as-int'], ['body' => 'date', 'id' => 'integer']);
  318. $result = $this->connection->execute('SELECT * FROM things WHERE title = :title AND body = :body', $values, ['body' => 'date']);
  319. $this->assertCount(1, $result);
  320. $row = $result->fetch('assoc');
  321. $this->assertEquals('2012-01-01', $row['body']);
  322. }
  323. /**
  324. * Tests delete from table with no conditions
  325. *
  326. * @return void
  327. **/
  328. public function testDeleteNoConditions() {
  329. $this->_insertTwoRecords();
  330. $this->connection->delete('things');
  331. $result = $this->connection->execute('SELECT * FROM things');
  332. $this->assertCount(0, $result);
  333. }
  334. /**
  335. * Tests delete from table with conditions
  336. * @return void
  337. **/
  338. public function testDeleteWithConditions() {
  339. $this->_insertTwoRecords();
  340. $this->connection->delete('things', ['id' => '1-rest-is-ommited'], ['id' => 'integer']);
  341. $result = $this->connection->execute('SELECT * FROM things');
  342. $this->assertCount(1, $result);
  343. $this->connection->delete('things', ['id' => '1-rest-is-ommited'], ['id' => 'integer']);
  344. $result = $this->connection->execute('SELECT * FROM things');
  345. $this->assertCount(1, $result);
  346. $this->connection->delete('things', ['id' => '2-rest-is-ommited'], ['id' => 'integer']);
  347. $result = $this->connection->execute('SELECT * FROM things');
  348. $this->assertCount(0, $result);
  349. }
  350. /**
  351. * Tests that it is possible to use simple database transactions
  352. *
  353. * @return void
  354. **/
  355. public function testSimpleTransactions() {
  356. $this->_insertTwoRecords();
  357. $this->connection->begin();
  358. $this->connection->delete('things', ['id' => 1]);
  359. $this->connection->rollback();
  360. $result = $this->connection->execute('SELECT * FROM things');
  361. $this->assertCount(2, $result);
  362. $this->connection->begin();
  363. $this->connection->delete('things', ['id' => 1]);
  364. $this->connection->commit();
  365. $result = $this->connection->execute('SELECT * FROM things');
  366. $this->assertCount(1, $result);
  367. }
  368. /**
  369. * Tests that it is possible to use virtualized nested transaction
  370. * with early rollback algorithm
  371. *
  372. * @return void
  373. **/
  374. public function testVirtualNestedTrasanction() {
  375. $this->_insertTwoRecords();
  376. //starting 3 virtual transaction
  377. $this->connection->begin();
  378. $this->connection->begin();
  379. $this->connection->begin();
  380. $this->connection->delete('things', ['id' => 1]);
  381. $result = $this->connection->execute('SELECT * FROM things');
  382. $this->assertCount(1, $result);
  383. $this->connection->commit();
  384. $this->connection->rollback();
  385. $result = $this->connection->execute('SELECT * FROM things');
  386. $this->assertCount(2, $result);
  387. }
  388. /**
  389. * Tests that it is possible to use virtualized nested transaction
  390. * with early rollback algorithm
  391. *
  392. * @return void
  393. **/
  394. public function testVirtualNestedTrasanction2() {
  395. $this->_insertTwoRecords();
  396. //starting 3 virtual transaction
  397. $this->connection->begin();
  398. $this->connection->begin();
  399. $this->connection->begin();
  400. $this->connection->delete('things', ['id' => 1]);
  401. $result = $this->connection->execute('SELECT * FROM things');
  402. $this->assertCount(1, $result);
  403. $this->connection->rollback();
  404. $result = $this->connection->execute('SELECT * FROM things');
  405. $this->assertCount(2, $result);
  406. }
  407. /**
  408. * Tests that it is possible to use virtualized nested transaction
  409. * with early rollback algorithm
  410. *
  411. * @return void
  412. **/
  413. public function testVirtualNestedTrasanction3() {
  414. $this->_insertTwoRecords();
  415. //starting 3 virtual transaction
  416. $this->connection->begin();
  417. $this->connection->begin();
  418. $this->connection->begin();
  419. $this->connection->delete('things', ['id' => 1]);
  420. $result = $this->connection->execute('SELECT * FROM things');
  421. $this->assertCount(1, $result);
  422. $this->connection->commit();
  423. $this->connection->commit();
  424. $this->connection->commit();
  425. $result = $this->connection->execute('SELECT * FROM things');
  426. $this->assertCount(1, $result);
  427. }
  428. /**
  429. * Tests that it is possible to real use nested transactions
  430. *
  431. * @return void
  432. **/
  433. public function testSavePoints() {
  434. $this->skipIf(!$this->connection->useSavePoints(true));
  435. $this->_insertTwoRecords();
  436. $this->connection->begin();
  437. $this->connection->delete('things', ['id' => 1]);
  438. $result = $this->connection->execute('SELECT * FROM things');
  439. $this->assertCount(1, $result);
  440. $this->connection->begin();
  441. $this->connection->delete('things', ['id' => 2]);
  442. $result = $this->connection->execute('SELECT * FROM things');
  443. $this->assertCount(0, $result);
  444. $this->connection->rollback();
  445. $result = $this->connection->execute('SELECT * FROM things');
  446. $this->assertCount(1, $result);
  447. $this->connection->rollback();
  448. $result = $this->connection->execute('SELECT * FROM things');
  449. $this->assertCount(2, $result);
  450. }
  451. /**
  452. * Tests that it is possible to real use nested transactions
  453. *
  454. * @return void
  455. **/
  456. public function testSavePoints2() {
  457. $this->skipIf(!$this->connection->useSavePoints(true));
  458. $this->_insertTwoRecords();
  459. $this->connection->begin();
  460. $this->connection->delete('things', ['id' => 1]);
  461. $result = $this->connection->execute('SELECT * FROM things');
  462. $this->assertCount(1, $result);
  463. $this->connection->begin();
  464. $this->connection->delete('things', ['id' => 2]);
  465. $result = $this->connection->execute('SELECT * FROM things');
  466. $this->assertCount(0, $result);
  467. $this->connection->rollback();
  468. $result = $this->connection->execute('SELECT * FROM things');
  469. $this->assertCount(1, $result);
  470. $this->connection->commit();
  471. $result = $this->connection->execute('SELECT * FROM things');
  472. $this->assertCount(1, $result);
  473. }
  474. /**
  475. * Tests connection can quote values to be safely used in query strings
  476. *
  477. * @return void
  478. **/
  479. public function testQuote() {
  480. $this->skipIf(!$this->connection->supportsQuoting());
  481. $expected = "'2012-01-01'";
  482. $result = $this->connection->quote(new \DateTime('2012-01-01'), 'date');
  483. $this->assertEquals($expected, $result);
  484. $expected = "'1'";
  485. $result = $this->connection->quote(1, 'string');
  486. $this->assertEquals($expected, $result);
  487. $expected = "'hello'";
  488. $result = $this->connection->quote('hello', 'string');
  489. $this->assertEquals($expected, $result);
  490. }
  491. /**
  492. * Tests identifier quoting
  493. *
  494. * @return void
  495. */
  496. public function testQuoteIdentifier() {
  497. $driver = $this->getMock('Cake\Database\Driver\Sqlite', ['enabled']);
  498. $driver->expects($this->once())
  499. ->method('enabled')
  500. ->will($this->returnValue(true));
  501. $connection = new Connection(['datasource' => $driver]);
  502. $result = $connection->quoteIdentifier('name');
  503. $expected = '"name"';
  504. $this->assertEquals($expected, $result);
  505. $result = $connection->quoteIdentifier('Model.*');
  506. $expected = '"Model".*';
  507. $this->assertEquals($expected, $result);
  508. $result = $connection->quoteIdentifier('MTD()');
  509. $expected = 'MTD()';
  510. $this->assertEquals($expected, $result);
  511. $result = $connection->quoteIdentifier('(sm)');
  512. $expected = '(sm)';
  513. $this->assertEquals($expected, $result);
  514. $result = $connection->quoteIdentifier('name AS x');
  515. $expected = '"name" AS "x"';
  516. $this->assertEquals($expected, $result);
  517. $result = $connection->quoteIdentifier('Model.name AS x');
  518. $expected = '"Model"."name" AS "x"';
  519. $this->assertEquals($expected, $result);
  520. $result = $connection->quoteIdentifier('Function(Something.foo)');
  521. $expected = 'Function("Something"."foo")';
  522. $this->assertEquals($expected, $result);
  523. $result = $connection->quoteIdentifier('Function(SubFunction(Something.foo))');
  524. $expected = 'Function(SubFunction("Something"."foo"))';
  525. $this->assertEquals($expected, $result);
  526. $result = $connection->quoteIdentifier('Function(Something.foo) AS x');
  527. $expected = 'Function("Something"."foo") AS "x"';
  528. $this->assertEquals($expected, $result);
  529. $result = $connection->quoteIdentifier('name-with-minus');
  530. $expected = '"name-with-minus"';
  531. $this->assertEquals($expected, $result);
  532. $result = $connection->quoteIdentifier('my-name');
  533. $expected = '"my-name"';
  534. $this->assertEquals($expected, $result);
  535. $result = $connection->quoteIdentifier('Foo-Model.*');
  536. $expected = '"Foo-Model".*';
  537. $this->assertEquals($expected, $result);
  538. $result = $connection->quoteIdentifier('Team.P%');
  539. $expected = '"Team"."P%"';
  540. $this->assertEquals($expected, $result);
  541. $result = $connection->quoteIdentifier('Team.G/G');
  542. $expected = '"Team"."G/G"';
  543. $result = $connection->quoteIdentifier('Model.name as y');
  544. $expected = '"Model"."name" AS "y"';
  545. $this->assertEquals($expected, $result);
  546. }
  547. /**
  548. * Tests default return vale for logger() function
  549. *
  550. * @return void
  551. */
  552. public function testLoggerDefault() {
  553. $logger = $this->connection->logger();
  554. $this->assertInstanceOf('\Cake\Database\Log\QueryLogger', $logger);
  555. $this->assertSame($logger, $this->connection->logger());
  556. }
  557. /**
  558. * Tests that a custom logger object can be set
  559. *
  560. * @return void
  561. */
  562. public function testSetLogger() {
  563. $logger = new \Cake\Database\Log\QueryLogger;
  564. $this->connection->logger($logger);
  565. $this->assertSame($logger, $this->connection->logger());
  566. }
  567. /**
  568. * Tests that statements are decorated with a logger when logQueries is set to true
  569. *
  570. * @return void
  571. */
  572. public function testLoggerDecorator() {
  573. $logger = new \Cake\Database\Log\QueryLogger;
  574. $this->connection->logQueries(true);
  575. $this->connection->logger($logger);
  576. $st = $this->connection->prepare('SELECT 1');
  577. $this->assertInstanceOf('\Cake\Database\Log\LoggingStatement', $st);
  578. $this->assertSame($logger, $st->logger());
  579. $this->connection->logQueries(false);
  580. $st = $this->connection->prepare('SELECT 1');
  581. $this->assertNotInstanceOf('\Cake\Database\Log\LoggingStatement', $st);
  582. }
  583. /**
  584. * Tests that log() function logs to the configured query logger
  585. *
  586. * @return void
  587. */
  588. public function testLogFunction() {
  589. $logger = $this->getMock('\Cake\Database\Log\QueryLogger');
  590. $this->connection->logger($logger);
  591. $logger->expects($this->once())->method('log')
  592. ->with($this->logicalAnd(
  593. $this->isInstanceOf('\Cake\Database\Log\LoggedQuery'),
  594. $this->attributeEqualTo('query', 'SELECT 1')
  595. ));
  596. $this->connection->log('SELECT 1');
  597. }
  598. /**
  599. * Tests that begin and rollback are also logged
  600. *
  601. * @return void
  602. */
  603. public function testLogBeginRollbackTransaction() {
  604. $connection = $this->getMock(
  605. '\Cake\Database\Connection',
  606. ['connect'],
  607. [['log' => true]]
  608. );
  609. $driver = $this->getMockFormDriver();
  610. $connection->driver($driver);
  611. $logger = $this->getMock('\Cake\Database\Log\QueryLogger');
  612. $connection->logger($logger);
  613. $logger->expects($this->at(0))->method('log')
  614. ->with($this->logicalAnd(
  615. $this->isInstanceOf('\Cake\Database\Log\LoggedQuery'),
  616. $this->attributeEqualTo('query', 'BEGIN')
  617. ));
  618. $logger->expects($this->at(1))->method('log')
  619. ->with($this->logicalAnd(
  620. $this->isInstanceOf('\Cake\Database\Log\LoggedQuery'),
  621. $this->attributeEqualTo('query', 'ROLLBACK')
  622. ));
  623. $connection->begin();
  624. $connection->begin(); //This one will not be logged
  625. $connection->rollback();
  626. }
  627. /**
  628. * Tests that commits are logged
  629. *
  630. * @return void
  631. */
  632. public function testLogCommitTransaction() {
  633. $driver = $this->getMockFormDriver();
  634. $connection = $this->getMock(
  635. '\Cake\Database\Connection',
  636. ['connect'],
  637. [['datasource' => $driver]]
  638. );
  639. $logger = $this->getMock('\Cake\Database\Log\QueryLogger');
  640. $connection->logger($logger);
  641. $logger->expects($this->at(1))->method('log')
  642. ->with($this->logicalAnd(
  643. $this->isInstanceOf('\Cake\Database\Log\LoggedQuery'),
  644. $this->attributeEqualTo('query', 'COMMIT')
  645. ));
  646. $connection->logQueries(true);
  647. $connection->begin();
  648. $connection->commit();
  649. }
  650. /**
  651. * Tests that the transactional method will start and commit a transaction
  652. * around some arbitrary function passed as argument
  653. *
  654. * @return void
  655. */
  656. public function testTransactionalSuccess() {
  657. $driver = $this->getMockFormDriver();
  658. $connection = $this->getMock(
  659. '\Cake\Database\Connection',
  660. ['connect', 'commit', 'begin'],
  661. [['datasource' => $driver]]
  662. );
  663. $connection->expects($this->at(0))->method('begin');
  664. $connection->expects($this->at(1))->method('commit');
  665. $result = $connection->transactional(function($conn) use ($connection) {
  666. $this->assertSame($connection, $conn);
  667. return 'thing';
  668. });
  669. $this->assertEquals('thing', $result);
  670. }
  671. /**
  672. * Tests that the transactional method will rollback the transaction if false
  673. * is returned from the callback
  674. *
  675. * @return void
  676. */
  677. public function testTransactionalFail() {
  678. $driver = $this->getMockFormDriver();
  679. $connection = $this->getMock(
  680. '\Cake\Database\Connection',
  681. ['connect', 'commit', 'begin', 'rollback'],
  682. [['datasource' => $driver]]
  683. );
  684. $connection->expects($this->at(0))->method('begin');
  685. $connection->expects($this->at(1))->method('rollback');
  686. $connection->expects($this->never())->method('commit');
  687. $result = $connection->transactional(function($conn) use ($connection) {
  688. $this->assertSame($connection, $conn);
  689. return false;
  690. });
  691. $this->assertFalse($result);
  692. }
  693. /**
  694. * Tests that the transactional method will rollback the transaction
  695. * and throw the same exception if the callback raises one
  696. *
  697. * @expectedException \InvalidArgumentException
  698. * @return void
  699. * @throws \InvalidArgumentException
  700. */
  701. public function testTransactionalWithException() {
  702. $driver = $this->getMockFormDriver();
  703. $connection = $this->getMock(
  704. '\Cake\Database\Connection',
  705. ['connect', 'commit', 'begin', 'rollback'],
  706. [['datasource' => $driver]]
  707. );
  708. $connection->expects($this->at(0))->method('begin');
  709. $connection->expects($this->at(1))->method('rollback');
  710. $connection->expects($this->never())->method('commit');
  711. $connection->transactional(function($conn) use ($connection) {
  712. $this->assertSame($connection, $conn);
  713. throw new \InvalidArgumentException;
  714. });
  715. }
  716. }