ModelTest.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  1. <?php
  2. /**
  3. * Lithium: the most rad php framework
  4. *
  5. * @copyright Copyright 2013, Union of RAD (http://union-of-rad.org)
  6. * @license http://opensource.org/licenses/bsd-license.php The BSD License
  7. */
  8. namespace lithium\tests\cases\data;
  9. use stdClass;
  10. use lithium\util\Inflector;
  11. use lithium\data\Model;
  12. use lithium\data\Entity;
  13. use lithium\data\Schema;
  14. use lithium\data\model\Query;
  15. use lithium\data\entity\Record;
  16. use lithium\tests\mocks\data\MockTag;
  17. use lithium\tests\mocks\data\MockPost;
  18. use lithium\tests\mocks\data\MockComment;
  19. use lithium\tests\mocks\data\MockTagging;
  20. use lithium\tests\mocks\data\MockCreator;
  21. use lithium\tests\mocks\data\MockPostForValidates;
  22. use lithium\tests\mocks\data\MockBadConnection;
  23. class ModelTest extends \lithium\test\Unit {
  24. protected $_model = 'lithium\tests\mocks\data\MockModelCompositePk';
  25. protected $_database = 'lithium\tests\mocks\data\MockSource';
  26. protected $_altSchema = null;
  27. public function setUp() {
  28. $database = $this->_database;
  29. $class = 'lithium\tests\mocks\data\MockPost';
  30. $connection = new $database();
  31. MockPost::$connection = $connection;
  32. MockTag::$connection = $connection;
  33. MockComment::$connection = $connection;
  34. MockCreator::$connection = $connection;
  35. $config = MockPost::$connection->configureClass($class);
  36. MockPost::config(array('meta' => array('connection' => true) + $config['meta']));
  37. MockTag::config(array('meta' => array('connection' => true)));
  38. MockComment::config(array('meta' => array('connection' => true)));
  39. MockCreator::config(array('meta' => array('connection' => true)));
  40. MockPostForValidates::config(array('meta' => array('locked' => true)));
  41. $this->_altSchema = new Schema(array(
  42. 'fields' => array(
  43. 'id' => array('type' => 'integer'),
  44. 'author_id' => array('type' => 'integer'),
  45. 'title' => array('type' => 'string'),
  46. 'body' => array('type' => 'text')
  47. )));
  48. }
  49. public function tearDown() {
  50. MockPost::$connection = null;
  51. MockTag::$connection = null;
  52. MockComment::$connection = null;
  53. MockCreator::$connection = null;
  54. MockPost::reset();
  55. MockTag::reset();
  56. MockComment::reset();
  57. MockPostForValidates::reset();
  58. MockCreator::reset();
  59. }
  60. public function testOverrideMeta() {
  61. MockTag::reset();
  62. MockTag::meta(array('id' => 'key'));
  63. $meta = MockTag::meta();
  64. $this->assertFalse($meta['connection']);
  65. $this->assertEqual('mock_tags', $meta['source']);
  66. $this->assertEqual('key', $meta['id']);
  67. }
  68. public function testClassInitialization() {
  69. $expected = MockPost::instances();
  70. MockPost::config();
  71. $this->assertEqual($expected, MockPost::instances());
  72. Model::config();
  73. $this->assertEqual($expected, MockPost::instances());
  74. $this->assertEqual('mock_posts', MockPost::meta('source'));
  75. MockPost::config(array('meta' => array('source' => 'post')));
  76. $this->assertEqual('post', MockPost::meta('source'));
  77. MockPost::config(array('meta' => array('source' => false)));
  78. $this->assertIdentical(false, MockPost::meta('source'));
  79. MockPost::config(array('meta' => array('source' => null)));
  80. $this->assertIdentical('mock_posts', MockPost::meta('source'));
  81. MockPost::config();
  82. $this->assertEqual('mock_posts', MockPost::meta('source'));
  83. $this->assertTrue(MockPost::meta('connection'));
  84. MockPost::config(array('meta' => array('source' => 'toreset')));
  85. MockPost::reset();
  86. $this->assertEqual('mock_posts', MockPost::meta('source'));
  87. $this->assertFalse(MockPost::meta('connection'));
  88. MockPost::config(array('query' => array('with' => array('MockComment'), 'limit' => 10)));
  89. $expected = array(
  90. 'with' => array('MockComment'),
  91. 'limit' => 10,
  92. 'conditions' => null,
  93. 'fields' => null,
  94. 'order' => null,
  95. 'page' => null
  96. );
  97. $this->assertEqual($expected, MockPost::query());
  98. $finder = array(
  99. 'fields' => array('title', 'body')
  100. );
  101. MockPost::finder('myFinder', $finder);
  102. $result = MockPost::find('myFinder');
  103. $expected = $finder + array(
  104. 'order' => null,
  105. 'limit' => 10,
  106. 'conditions' => null,
  107. 'page' => null,
  108. 'with' => array('MockComment'),
  109. 'type' => 'read',
  110. 'model' => 'lithium\tests\mocks\data\MockPost'
  111. );
  112. $this->assertEqual($expected, $result['options']);
  113. $finder = array(
  114. 'fields' => array('id', 'title')
  115. );
  116. MockPost::reset();
  117. $result = MockPost::finder('myFinder');
  118. $this->assertNull($result);
  119. }
  120. public function testInstanceMethods() {
  121. MockPost::instanceMethods(array());
  122. $methods = MockPost::instanceMethods();
  123. $this->assertTrue(empty($methods));
  124. MockPost::instanceMethods(array(
  125. 'first' => array(
  126. 'lithium\tests\mocks\data\source\MockMongoPost',
  127. 'testInstanceMethods'
  128. ),
  129. 'second' => function($entity) {}
  130. ));
  131. $methods = MockPost::instanceMethods();
  132. $this->assertEqual(2, count($methods));
  133. MockPost::instanceMethods(array(
  134. 'third' => function($entity) {}
  135. ));
  136. $methods = MockPost::instanceMethods();
  137. $this->assertEqual(3, count($methods));
  138. }
  139. public function testMetaInformation() {
  140. $class = 'lithium\tests\mocks\data\MockPost';
  141. $config = MockPost::$connection->configureClass($class);
  142. $expected = compact('class') + array(
  143. 'name' => 'MockPost',
  144. 'key' => 'id',
  145. 'title' => 'title',
  146. 'source' => 'mock_posts',
  147. 'connection' => true,
  148. 'locked' => true
  149. );
  150. MockPost::config(array('meta' => array('connection' => true) + $config['meta']));
  151. $this->assertEqual($expected, MockPost::meta());
  152. $class = 'lithium\tests\mocks\data\MockComment';
  153. $config = MockComment::$connection->configureClass($class);
  154. $expected = compact('class') + array(
  155. 'name' => 'MockComment',
  156. 'key' => 'comment_id',
  157. 'title' => 'comment_id',
  158. 'source' => 'mock_comments',
  159. 'connection' => true,
  160. 'locked' => true
  161. );
  162. unset($config['meta']['key']);
  163. MockComment::config(array('meta' => array('connection' => true) + $config['meta']));
  164. $this->assertEqual($expected, MockComment::meta());
  165. $expected += array('foo' => 'bar');
  166. MockComment::meta('foo', 'bar');
  167. $this->assertEqual($expected, MockComment::meta());
  168. $expected += array('bar' => true, 'baz' => false);
  169. MockComment::meta(array('bar' => true, 'baz' => false));
  170. $this->assertEqual($expected, MockComment::meta());
  171. }
  172. public function testSchemaLoading() {
  173. $result = MockPost::schema();
  174. $this->assertTrue($result);
  175. $this->assertEqual($result->fields(), MockPost::schema()->fields());
  176. MockPost::config(array('schema' => $this->_altSchema));
  177. $this->assertEqual($this->_altSchema->fields(), MockPost::schema()->fields());
  178. }
  179. public function testFieldIntrospection() {
  180. $this->assertTrue(MockComment::hasField('comment_id'));
  181. $this->assertFalse(MockComment::hasField('foo'));
  182. $this->assertEqual('comment_id', MockComment::hasField(array('comment_id')));
  183. }
  184. /**
  185. * Tests introspecting the relationship settings for the model as a whole, various relationship
  186. * types, and individual relationships.
  187. *
  188. * @todo Some tests will need to change when full relationship support is built out.
  189. * @return void
  190. */
  191. public function testRelationshipIntrospection() {
  192. MockPost::config(array('meta' => array('connection' => true)));
  193. MockComment::config(array('meta' => array('connection' => true)));
  194. MockTag::config(array('meta' => array('connection' => true)));
  195. $result = array_keys(MockPost::relations());
  196. $expected = array('MockComment');
  197. $this->assertEqual($expected, $result);
  198. $result = MockPost::relations('hasMany');
  199. $this->assertEqual($expected, $result);
  200. $result = array_keys(MockComment::relations());
  201. $expected = array('MockPost');
  202. $this->assertEqual($expected, $result);
  203. $result = MockComment::relations('belongsTo');
  204. $this->assertEqual($expected, $result);
  205. $this->assertFalse(MockComment::relations('hasMany'));
  206. $this->assertFalse(MockPost::relations('belongsTo'));
  207. $expected = array(
  208. 'name' => 'MockPost',
  209. 'type' => 'belongsTo',
  210. 'key' => array('mock_post_id' => 'id'),
  211. 'from' => 'lithium\tests\mocks\data\MockComment',
  212. 'to' => 'lithium\tests\mocks\data\MockPost',
  213. 'link' => 'key',
  214. 'fields' => true,
  215. 'fieldName' => 'mock_post',
  216. 'constraints' => array(),
  217. 'init' => true
  218. );
  219. $this->assertEqual($expected, MockComment::relations('MockPost')->data());
  220. $expected = array(
  221. 'name' => 'MockComment',
  222. 'type' => 'hasMany',
  223. 'from' => 'lithium\tests\mocks\data\MockPost',
  224. 'to' => 'lithium\tests\mocks\data\MockComment',
  225. 'fields' => true,
  226. 'key' => array('id' => 'mock_post_id'),
  227. 'link' => 'key',
  228. 'fieldName' => 'mock_comments',
  229. 'constraints' => array(),
  230. 'init' => true
  231. );
  232. $this->assertEqual($expected, MockPost::relations('MockComment')->data());
  233. MockPost::config(array('meta' => array('connection' => false)));
  234. MockComment::config(array('meta' => array('connection' => false)));
  235. MockTag::config(array('meta' => array('connection' => false)));
  236. }
  237. public function testSimpleRecordCreation() {
  238. $comment = MockComment::create(array(
  239. 'author_id' => 451,
  240. 'text' => 'Do you ever read any of the books you burn?'
  241. ));
  242. $this->assertFalse($comment->exists());
  243. $this->assertNull($comment->comment_id);
  244. $expected = 'Do you ever read any of the books you burn?';
  245. $this->assertEqual($expected, $comment->text);
  246. $comment = MockComment::create(
  247. array('author_id' => 111, 'text' => 'This comment should already exist'),
  248. array('exists' => true)
  249. );
  250. $this->assertTrue($comment->exists());
  251. }
  252. public function testSimpleFind() {
  253. $result = MockPost::find('all');
  254. $this->assertTrue($result['query'] instanceof Query);
  255. }
  256. public function testMagicFinders() {
  257. $result = MockPost::findById(5);
  258. $result2 = MockPost::findFirstById(5);
  259. $this->assertEqual($result2, $result);
  260. $expected = array('id' => 5);
  261. $this->assertEqual($expected, $result['query']->conditions());
  262. $this->assertEqual('read', $result['query']->type());
  263. $result = MockPost::findAllByFoo(13, array('order' => array('created_at' => 'desc')));
  264. $this->assertFalse($result['query']->data());
  265. $this->assertEqual(array('foo' => 13), $result['query']->conditions());
  266. $this->assertEqual(array('created_at' => 'desc'), $result['query']->order());
  267. $this->expectException('/Method `findFoo` not defined or handled in class/');
  268. MockPost::findFoo();
  269. }
  270. /**
  271. * Tests the find 'first' filter on a simple record set.
  272. *
  273. * @return void
  274. */
  275. public function testSimpleFindFirst() {
  276. $result = MockComment::first();
  277. $this->assertTrue($result instanceof Record);
  278. $expected = 'First comment';
  279. $this->assertEqual($expected, $result->text);
  280. }
  281. public function testSimpleFindList() {
  282. $result = MockComment::find('list');
  283. $this->assertTrue(!empty($result));
  284. $this->assertTrue(is_array($result));
  285. }
  286. public function testFilteredFind() {
  287. MockComment::applyFilter('find', function($self, $params, $chain) {
  288. $result = $chain->next($self, $params, $chain);
  289. if ($result !== null) {
  290. $result->filtered = true;
  291. }
  292. return $result;
  293. });
  294. $result = MockComment::first();
  295. $this->assertTrue($result->filtered);
  296. }
  297. public function testCustomFinder() {
  298. $finder = function() {};
  299. MockPost::finder('custom', $finder);
  300. $this->assertIdentical($finder, MockPost::finder('custom'));
  301. $finder = array(
  302. 'fields' => array('id', 'email'),
  303. 'conditions' => array('id' => 2)
  304. );
  305. MockPost::finder('arrayTest', $finder);
  306. $result = MockPost::find('arrayTest');
  307. $expected = $finder + array(
  308. 'order' => null,
  309. 'limit' => null,
  310. 'page' => null,
  311. 'with' => array(),
  312. 'type' => 'read',
  313. 'model' => 'lithium\tests\mocks\data\MockPost'
  314. );
  315. $this->assertEqual($expected, $result['options']);
  316. }
  317. public function testCustomFindMethods() {
  318. $result = MockPost::findFirstById(5);
  319. $query = $result['query'];
  320. $this->assertEqual(array('id' => 5), $query->conditions());
  321. $this->assertEqual(1, $query->limit());
  322. }
  323. public function testKeyGeneration() {
  324. $this->assertEqual('comment_id', MockComment::key());
  325. $this->assertEqual(array('post_id', 'tag_id'), MockTagging::key());
  326. $result = MockComment::key(array('comment_id' => 5, 'body' => 'This is a comment'));
  327. $this->assertEqual(array('comment_id' => 5), $result);
  328. $result = MockTagging::key(array(
  329. 'post_id' => 2,
  330. 'tag_id' => 5,
  331. 'created' => '2009-06-16 10:00:00'
  332. ));
  333. $this->assertEqual('id', MockPost::key());
  334. $this->assertEqual(array('id' => 5), MockPost::key(5));
  335. $this->assertEqual(array('post_id' => 2, 'tag_id' => 5), $result);
  336. $key = new stdClass();
  337. $key->foo = 'bar';
  338. $this->assertEqual(array('id' => $key), MockPost::key($key));
  339. $this->assertNull(MockPost::key(array()));
  340. $model = $this->_model;
  341. $this->assertNull($model::key(array('client_id' => 3)));
  342. $result = $model::key(array('invoice_id' => 5, 'payment' => '100'));
  343. $this->assertNull($result);
  344. $expected = array('client_id' => 3, 'invoice_id' => 5);
  345. $result = $model::key(array(
  346. 'client_id' => 3,
  347. 'invoice_id' => 5,
  348. 'payment' => '100'));
  349. $this->assertEqual($expected, $result);
  350. }
  351. public function testValidatesFalse() {
  352. $post = MockPostForValidates::create();
  353. $result = $post->validates();
  354. $this->assertTrue($result === false);
  355. $result = $post->errors();
  356. $this->assertTrue(!empty($result));
  357. $expected = array(
  358. 'title' => array('please enter a title'),
  359. 'email' => array('email is empty', 'email is not valid')
  360. );
  361. $result = $post->errors();
  362. $this->assertEqual($expected, $result);
  363. }
  364. public function testValidatesTitle() {
  365. $post = MockPostForValidates::create(array('title' => 'new post'));
  366. $result = $post->validates();
  367. $this->assertTrue($result === false);
  368. $result = $post->errors();
  369. $this->assertTrue(!empty($result));
  370. $expected = array(
  371. 'email' => array('email is empty', 'email is not valid')
  372. );
  373. $result = $post->errors();
  374. $this->assertEqual($expected, $result);
  375. }
  376. public function testValidatesEmailIsNotEmpty() {
  377. $post = MockPostForValidates::create(array('title' => 'new post', 'email' => 'something'));
  378. $result = $post->validates();
  379. $this->assertIdentical(false, $result);
  380. $result = $post->errors();
  381. $this->assertTrue($result);
  382. $expected = array('email' => array('email is not valid'));
  383. $result = $post->errors();
  384. $this->assertEqual($expected, $result);
  385. }
  386. public function testValidatesEmailIsValid() {
  387. $post = MockPostForValidates::create(array(
  388. 'title' => 'new post', 'email' => '[email protected]'
  389. ));
  390. $result = $post->validates();
  391. $this->assertTrue($result === true);
  392. $result = $post->errors();
  393. $this->assertTrue(empty($result));
  394. }
  395. public function testCustomValidationCriteria() {
  396. $validates = array(
  397. 'title' => 'A custom message here for empty titles.',
  398. 'email' => array(
  399. array('notEmpty', 'message' => 'email is empty.')
  400. )
  401. );
  402. $post = MockPostForValidates::create(array(
  403. 'title' => 'custom validation', 'email' => 'asdf'
  404. ));
  405. $result = $post->validates(array('rules' => $validates));
  406. $this->assertTrue($result === true);
  407. $this->assertIdentical(array(), $post->errors());
  408. }
  409. public function testValidatesCustomEventFalse() {
  410. $post = MockPostForValidates::create();
  411. $events = 'customEvent';
  412. $this->assertIdentical(false, $post->validates(compact('events')));
  413. $this->assertTrue($post->errors());
  414. $expected = array(
  415. 'title' => array('please enter a title'),
  416. 'email' => array(
  417. 'email is empty',
  418. 'email is not valid',
  419. 'email is not in 1st list'
  420. )
  421. );
  422. $result = $post->errors();
  423. $this->assertEqual($expected, $result);
  424. }
  425. public function testValidatesCustomEventValid() {
  426. $post = MockPostForValidates::create(array(
  427. 'title' => 'new post', 'email' => '[email protected]'
  428. ));
  429. $events = 'customEvent';
  430. $result = $post->validates(compact('events'));
  431. $this->assertTrue($result === true);
  432. $result = $post->errors();
  433. $this->assertTrue(empty($result));
  434. }
  435. public function testValidatesCustomEventsFalse() {
  436. $post = MockPostForValidates::create();
  437. $events = array('customEvent','anotherCustomEvent');
  438. $result = $post->validates(compact('events'));
  439. $this->assertTrue($result === false);
  440. $result = $post->errors();
  441. $this->assertTrue(!empty($result));
  442. $expected = array(
  443. 'title' => array('please enter a title'),
  444. 'email' => array(
  445. 'email is empty',
  446. 'email is not valid',
  447. 'email is not in 1st list',
  448. 'email is not in 2nd list'
  449. )
  450. );
  451. $result = $post->errors();
  452. $this->assertEqual($expected, $result);
  453. }
  454. public function testValidatesCustomEventsFirstValid() {
  455. $post = MockPostForValidates::create(array(
  456. 'title' => 'new post', 'email' => '[email protected]'
  457. ));
  458. $events = array('customEvent','anotherCustomEvent');
  459. $result = $post->validates(compact('events'));
  460. $this->assertTrue($result === false);
  461. $result = $post->errors();
  462. $this->assertTrue(!empty($result));
  463. $expected = array(
  464. 'email' => array('email is not in 2nd list')
  465. );
  466. $result = $post->errors();
  467. $this->assertEqual($expected, $result);
  468. }
  469. public function testValidatesCustomEventsValid() {
  470. $post = MockPostForValidates::create(array(
  471. 'title' => 'new post', 'email' => '[email protected]'
  472. ));
  473. $events = array('customEvent','anotherCustomEvent');
  474. $result = $post->validates(compact('events'));
  475. $this->assertTrue($result === true);
  476. $result = $post->errors();
  477. $this->assertTrue(empty($result));
  478. }
  479. public function testDefaultValuesFromSchema() {
  480. $creator = MockCreator::create();
  481. $expected = array(
  482. 'name' => 'Moe',
  483. 'sign' => 'bar',
  484. 'age' => 0
  485. );
  486. $result = $creator->data();
  487. $this->assertEqual($expected, $result);
  488. $creator = MockCreator::create(array('name' => 'Homer'));
  489. $expected = array(
  490. 'name' => 'Homer',
  491. 'sign' => 'bar',
  492. 'age' => 0
  493. );
  494. $result = $creator->data();
  495. $this->assertEqual($expected, $result);
  496. $creator = MockCreator::create(array(
  497. 'sign' => 'Beer', 'skin' => 'yellow', 'age' => 12, 'hair' => false
  498. ));
  499. $expected = array(
  500. 'name' => 'Moe',
  501. 'sign' => 'Beer',
  502. 'skin' => 'yellow',
  503. 'age' => 12,
  504. 'hair' => false
  505. );
  506. $result = $creator->data();
  507. $this->assertEqual($expected, $result);
  508. $expected = 'mock_creators';
  509. $result = MockCreator::meta('source');
  510. $this->assertEqual($expected, $result);
  511. }
  512. public function testModelWithNoBackend() {
  513. MockPost::reset();
  514. $this->assertFalse(MockPost::meta('connection'));
  515. MockPost::config(array('meta' => array('connection' => true)));
  516. $this->assertTrue(MockPost::meta('connection'));
  517. $schema = MockPost::schema();
  518. MockPost::config(array('schema' => $this->_altSchema));
  519. $this->assertEqual($this->_altSchema->fields(), MockPost::schema()->fields());
  520. $post = MockPost::create(array('title' => 'New post'));
  521. $this->assertTrue($post instanceof Entity);
  522. $this->assertEqual('New post', $post->title);
  523. }
  524. public function testSave() {
  525. MockPost::config(array('schema' => $this->_altSchema));
  526. MockPost::config(array('schema' => new Schema()));
  527. $data = array('title' => 'New post', 'author_id' => 13, 'foo' => 'bar');
  528. $record = MockPost::create($data);
  529. $result = $record->save();
  530. $this->assertEqual('create', $result['query']->type());
  531. $this->assertEqual($data, $result['query']->data());
  532. $this->assertEqual('lithium\tests\mocks\data\MockPost', $result['query']->model());
  533. MockPost::config(array('schema' => $this->_altSchema));
  534. $record->tags = array("baz", "qux");
  535. $otherData = array('body' => 'foobar');
  536. $result = $record->save($otherData);
  537. $data['body'] = 'foobar';
  538. $data['tags'] = array("baz", "qux");
  539. $expected = array('title' => 'New post', 'author_id' => 13, 'body' => 'foobar');
  540. $this->assertNotEqual($data, $result['query']->data());
  541. }
  542. public function testSaveWithNoCallbacks() {
  543. MockPost::config(array('schema' => $this->_altSchema));
  544. $data = array('title' => 'New post', 'author_id' => 13);
  545. $record = MockPost::create($data);
  546. $result = $record->save(null, array('callbacks' => false));
  547. $this->assertEqual('create', $result['query']->type());
  548. $this->assertEqual($data, $result['query']->data());
  549. $this->assertEqual('lithium\tests\mocks\data\MockPost', $result['query']->model());
  550. }
  551. public function testSaveWithFailedValidation() {
  552. $data = array('title' => '', 'author_id' => 13);
  553. $record = MockPost::create($data);
  554. $result = $record->save(null, array(
  555. 'validate' => array(
  556. 'title' => 'A title must be present'
  557. )
  558. ));
  559. $this->assertIdentical(false, $result);
  560. }
  561. public function testSaveFailedWithValidationByModelDefinition() {
  562. MockPostForValidates::config(array('locked' => true));
  563. $post = MockPostForValidates::create();
  564. $result = $post->save();
  565. $this->assertTrue($result === false);
  566. $result = $post->errors();
  567. $this->assertTrue(!empty($result));
  568. $expected = array(
  569. 'title' => array('please enter a title'),
  570. 'email' => array('email is empty', 'email is not valid')
  571. );
  572. $result = $post->errors();
  573. $this->assertEqual($expected, $result);
  574. }
  575. public function testSaveFailedWithValidationByModelDefinitionAndTriggeredCustomEvents() {
  576. $post = MockPostForValidates::create();
  577. $events = array('customEvent','anotherCustomEvent');
  578. $result = $post->save(null,compact('events'));
  579. $this->assertTrue($result === false);
  580. $result = $post->errors();
  581. $this->assertTrue(!empty($result));
  582. $expected = array(
  583. 'title' => array('please enter a title'),
  584. 'email' => array(
  585. 'email is empty',
  586. 'email is not valid',
  587. 'email is not in 1st list',
  588. 'email is not in 2nd list'
  589. )
  590. );
  591. $result = $post->errors();
  592. $this->assertEqual($expected, $result);
  593. }
  594. public function testImplicitKeyFind() {
  595. $result = MockPost::find(10);
  596. $this->assertEqual('read', $result['query']->type());
  597. $this->assertEqual('lithium\tests\mocks\data\MockPost', $result['query']->model());
  598. $this->assertEqual(array('id' => 10), $result['query']->conditions());
  599. }
  600. public function testDelete() {
  601. $record = MockPost::create(array('id' => 5), array('exists' => true));
  602. $result = $record->delete();
  603. $this->assertEqual('delete', $result['query']->type());
  604. $this->assertEqual('mock_posts', $result['query']->source());
  605. $this->assertEqual(array('id' => 5), $result['query']->conditions());
  606. }
  607. public function testMultiRecordUpdate() {
  608. $result = MockPost::update(
  609. array('published' => false),
  610. array('expires' => array('>=' => '2010-05-13'))
  611. );
  612. $query = $result['query'];
  613. $this->assertEqual('update', $query->type());
  614. $this->assertEqual(array('published' => false), $query->data());
  615. $this->assertEqual(array('expires' => array('>=' => '2010-05-13')), $query->conditions());
  616. }
  617. public function testMultiRecordDelete() {
  618. $result = MockPost::remove(array('published' => false));
  619. $query = $result['query'];
  620. $this->assertEqual('delete', $query->type());
  621. $this->assertEqual(array('published' => false), $query->conditions());
  622. $keys = array_keys(array_filter($query->export(MockPost::$connection)));
  623. $this->assertEqual(array('type', 'conditions', 'model', 'source', 'alias'), $keys);
  624. }
  625. public function testFindFirst() {
  626. MockTag::config(array('meta' => array('key' => 'id')));
  627. $tag = MockTag::find('first', array('conditions' => array('id' => 2)));
  628. $tag2 = MockTag::find(2);
  629. $tag3 = MockTag::first(2);
  630. $expected = $tag['query']->export(MockTag::$connection);
  631. $this->assertEqual($expected, $tag2['query']->export(MockTag::$connection));
  632. $this->assertEqual($expected, $tag3['query']->export(MockTag::$connection));
  633. }
  634. /**
  635. * Tests that varying `count` syntaxes all produce the same query operation (i.e.
  636. * `Model::count(...)`, `Model::find('count', ...)` etc).
  637. *
  638. * @return void
  639. */
  640. public function testCountSyntax() {
  641. $base = MockPost::count(array('email' => '[email protected]'));
  642. $query = $base['query'];
  643. $this->assertEqual('read', $query->type());
  644. $this->assertEqual('count', $query->calculate());
  645. $this->assertEqual(array('email' => '[email protected]'), $query->conditions());
  646. $result = MockPost::find('count', array('conditions' => array(
  647. 'email' => '[email protected]'
  648. )));
  649. $this->assertEqual($query, $result['query']);
  650. $result = MockPost::count(array('conditions' => array('email' => '[email protected]')));
  651. $this->assertEqual($query, $result['query']);
  652. }
  653. public function testSettingNestedObjectDefaults() {
  654. $schema = MockPost::schema()->append(array(
  655. 'nested.value' => array('type' => 'string', 'default' => 'foo')
  656. ));
  657. $this->assertEqual('foo', MockPost::create()->nested['value']);
  658. $data = array('nested' => array('value' => 'bar'));
  659. $this->assertEqual('bar', MockPost::create($data)->nested['value']);
  660. }
  661. /**
  662. * Tests that objects can be passed as keys to `Model::find()` and be properly translated to
  663. * query conditions.
  664. */
  665. public function testFindByObjectKey() {
  666. $key = (object) array('foo' => 'bar');
  667. $result = MockPost::find($key);
  668. $this->assertEqual(array('id' => $key), $result['query']->conditions());
  669. }
  670. public function testLiveConfiguration() {
  671. MockBadConnection::config(array('meta' => array('connection' => false)));
  672. $result = MockBadConnection::meta('connection');
  673. $this->assertFalse($result);
  674. }
  675. public function testLazyLoad() {
  676. $object = MockPost::invokeMethod('_object');
  677. $object->belongsTo = array('Unexisting');
  678. MockPost::config();
  679. MockPost::invokeMethod('_initialize', array('lithium\tests\mocks\data\MockPost'));
  680. $exception = 'Related model class \'lithium\tests\mocks\data\Unexisting\' not found.';
  681. $this->expectException($exception);
  682. MockPost::relations('Unexisting');
  683. }
  684. public function testLazyMetadataInit() {
  685. MockPost::config(array(
  686. 'schema' => new Schema(array(
  687. 'fields' => array(
  688. 'id' => array('type' => 'integer'),
  689. 'name' => array('type' => 'string'),
  690. 'label' => array('type' => 'string')
  691. )
  692. ))
  693. ));
  694. $this->assertIdentical('mock_posts', MockPost::meta('source'));
  695. $this->assertIdentical('name', MockPost::meta('title'));
  696. $this->assertIdentical(null, MockPost::meta('unexisting'));
  697. $config = array(
  698. 'schema' => new Schema(array(
  699. 'fields' => array(
  700. 'id' => array('type' => 'integer'),
  701. 'name' => array('type' => 'string'),
  702. 'label' => array('type' => 'string')
  703. )
  704. )
  705. ),
  706. 'initializers' => array(
  707. 'source' => function($self) {
  708. return Inflector::tableize($self::meta('name'));
  709. },
  710. 'name' => function($self) {
  711. return Inflector::singularize('CoolPosts');
  712. },
  713. 'title' => function($self) {
  714. static $i = 1;
  715. return 'label' . $i++;
  716. }
  717. )
  718. );
  719. MockPost::reset();
  720. MockPost::config($config);
  721. $this->assertIdentical('cool_posts', MockPost::meta('source'));
  722. $this->assertIdentical('label1', MockPost::meta('title'));
  723. $this->assertFalse('label2' === MockPost::meta('title'));
  724. $this->assertIdentical('label1', MockPost::meta('title'));
  725. $meta = MockPost::meta();
  726. $this->assertIdentical('label1', $meta['title']);
  727. $this->assertIdentical('CoolPost', MockPost::meta('name'));
  728. MockPost::reset();
  729. unset($config['initializers']['title']);
  730. $config['initializers']['source'] = function($self) {
  731. return Inflector::underscore($self::meta('name'));
  732. };
  733. MockPost::config($config);
  734. $this->assertIdentical('cool_post', MockPost::meta('source'));
  735. $this->assertIdentical('name', MockPost::meta('title'));
  736. $this->assertIdentical('CoolPost', MockPost::meta('name'));
  737. MockPost::reset();
  738. MockPost::config($config);
  739. $expected = array (
  740. 'class' => 'lithium\\tests\\mocks\\data\\MockPost',
  741. 'connection' => false,
  742. 'key' => 'id',
  743. 'name' => 'CoolPost',
  744. 'title' => 'name',
  745. 'source' => 'cool_post'
  746. );
  747. $this->assertEqual($expected, MockPost::meta());
  748. }
  749. public function testRespondsTo() {
  750. $this->assertTrue(MockPost::respondsTo('findByFoo'));
  751. $this->assertTrue(MockPost::respondsTo('findFooByBar'));
  752. $this->assertFalse(MockPost::respondsTo('fooBarBaz'));
  753. }
  754. public function testRespondsToParentCall() {
  755. $this->assertTrue(MockPost::respondsTo('applyFilter'));
  756. $this->assertFalse(MockPost::respondsTo('fooBarBaz'));
  757. }
  758. public function testRespondsToInstanceMethod() {
  759. $this->assertFalse(MockPost::respondsTo('foo_Bar_Baz'));
  760. MockPost::instanceMethods(array(
  761. 'foo_Bar_Baz' => function($entity) {}
  762. ));
  763. $this->assertTrue(MockPost::respondsTo('foo_Bar_Baz'));
  764. }
  765. }
  766. ?>