MongoDbTest.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957
  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\source;
  9. use lithium\data\source\mongo_db\Schema;
  10. use lithium\data\source\MongoDb;
  11. use MongoId;
  12. use MongoCode;
  13. use MongoDate;
  14. use MongoRegex;
  15. use lithium\data\model\Query;
  16. use lithium\data\entity\Document;
  17. use lithium\tests\mocks\data\MockPost;
  18. use lithium\tests\mocks\data\MockComment;
  19. use lithium\data\collection\DocumentSet;
  20. use lithium\tests\mocks\core\MockCallable;
  21. use lithium\tests\mocks\data\source\MockMongoSource;
  22. use lithium\tests\mocks\data\source\MockMongoConnection;
  23. use lithium\tests\mocks\data\source\mongo_db\MockResult;
  24. class MongoDbTest extends \lithium\test\Unit {
  25. protected $_model = 'lithium\tests\mocks\data\source\MockMongoPost';
  26. protected $_testConfig = array(
  27. 'adapter' => false,
  28. 'database' => 'lithium_test',
  29. 'host' => 'localhost',
  30. 'port' => '27017',
  31. 'persistent' => null,
  32. 'autoConnect' => false
  33. );
  34. protected $_schema = array(
  35. '_id' => 'id',
  36. 'guid' => 'id',
  37. 'title' => 'string',
  38. 'tags' => array('type' => 'string', 'array' => true),
  39. 'comments' => 'MongoId',
  40. 'authors' => array('type' => 'MongoId', 'array' => true),
  41. 'created' => 'MongoDate',
  42. 'modified' => 'datetime',
  43. 'voters' => array('type' => 'id', 'array' => true),
  44. 'rank_count' => array('type' => 'integer', 'default' => 0),
  45. 'rank' => array('type' => 'float', 'default' => 0.0),
  46. 'notifications.foo' => 'boolean',
  47. 'notifications.bar' => 'boolean',
  48. 'notifications.baz' => 'boolean'
  49. );
  50. protected $_configs = array();
  51. public function skip() {
  52. $this->skipIf(!MongoDb::enabled(), 'The `MongoDb` class is not enabled.');
  53. }
  54. public function setUp() {
  55. $model = $this->_model;
  56. $this->db = new MongoDb($this->_testConfig);
  57. $this->db->server = new MockMongoConnection();
  58. $this->db->connection = new MockMongoConnection();
  59. $this->db->server->connected = true;
  60. $model::config(array('meta' => array('key' => '_id')));
  61. $model::$connection = $this->db;
  62. $type = 'create';
  63. $this->query = new Query(compact('model', 'type') + array(
  64. 'entity' => new Document(compact('model'))
  65. ));
  66. }
  67. public function tearDown() {
  68. unset($this->query);
  69. MockPost::reset();
  70. MockComment::reset();
  71. }
  72. public function testBadConnection() {
  73. $db = new MongoDb(array('host' => null, 'autoConnect' => false));
  74. $this->expectException('Could not connect to the database.');
  75. $this->assertFalse($db->connect());
  76. $this->assertTrue($db->disconnect());
  77. }
  78. public function testGoodConnectionBadDatabase() {
  79. $this->expectException('Could not connect to the database.');
  80. $db = new MongoDb(array('database' => null, 'autoConnnect' => false));
  81. }
  82. public function testSources() {
  83. $this->db->connection->results = array(array());
  84. $this->assertEqual(array(), $this->db->sources());
  85. }
  86. public function testDescribe() {
  87. $result = $this->db->describe('test')->fields();
  88. $expected = array('_id' => array('type' => 'id'));
  89. $this->assertEqual($expected, $result);
  90. }
  91. public function testName() {
  92. $result = $this->db->name('{(\'Li\':"∆")}');
  93. $expected = '{(\'Li\':"∆")}';
  94. $this->assertEqual($expected, $result);
  95. }
  96. public function testSchema() {
  97. $result = $this->db->schema($this->query);
  98. $expected = array();
  99. $this->assertEqual($expected, $result);
  100. }
  101. public function testCreateSuccess() {
  102. array_push($this->db->connection->results, true);
  103. $this->query->data(array('title' => 'Test Post'));
  104. $this->assertTrue($this->db->create($this->query));
  105. $query = array_pop($this->db->connection->queries);
  106. $this->assertFalse($this->db->connection->queries);
  107. $this->assertEqual('insert', $query['type']);
  108. $this->assertEqual('posts', $query['collection']);
  109. $this->assertEqual(array('title', '_id'), array_keys($query['data']));
  110. $this->assertTrue($query['data']['_id'] instanceof MongoId);
  111. }
  112. public function testConditions() {
  113. $result = $this->db->conditions(null, null);
  114. $this->assertEqual(array(), $result);
  115. $function = 'function() { return this.x < y;}';
  116. $conditions = new MongoCode($function);
  117. $result = $this->db->conditions($conditions, null);
  118. $this->assertTrue(is_array($result));
  119. $this->assertTrue(isset($result['$where']));
  120. $this->assertEqual($conditions, $result['$where']);
  121. $conditions = $function;
  122. $result = $this->db->conditions($conditions, null);
  123. $this->assertTrue(is_array($result));
  124. $this->assertTrue(isset($result['$where']));
  125. $this->assertEqual($conditions, $result['$where']);
  126. $conditions = array('key' => 'value', 'anotherkey' => 'some other value');
  127. $result = $this->db->conditions($conditions, null);
  128. $this->assertTrue(is_array($result));
  129. $this->assertEqual($conditions, $result);
  130. $conditions = array('key' => array('one', 'two', 'three'));
  131. $result = $this->db->conditions($conditions, null);
  132. $this->assertTrue(is_array($result));
  133. $this->assertTrue(isset($result['key']));
  134. $this->assertTrue(isset($result['key']['$in']));
  135. $this->assertEqual($conditions['key'], $result['key']['$in']);
  136. $conditions = array('$or' => array(
  137. array('key' => 'value'),
  138. array('other key' => 'another value')
  139. ));
  140. $result = $this->db->conditions($conditions, null);
  141. $this->assertTrue(isset($result['$or']));
  142. $this->assertEqual($conditions['$or'][0]['key'], $result['$or'][0]['key']);
  143. $conditions = array('$and' => array(
  144. array('key' => 'value'),
  145. array('other key' => 'another value')
  146. ));
  147. $result = $this->db->conditions($conditions, null);
  148. $this->assertTrue(isset($result['$and']));
  149. $this->assertEqual($conditions['$and'][0]['key'], $result['$and'][0]['key']);
  150. $conditions = array('$nor' => array(
  151. array('key' => 'value'),
  152. array('other key' => 'another value')
  153. ));
  154. $result = $this->db->conditions($conditions, null);
  155. $this->assertTrue(isset($result['$nor']));
  156. $this->assertEqual($conditions['$nor'][0]['key'], $result['$nor'][0]['key']);
  157. $conditions = array('key' => array('or' => array(1, 2)));
  158. $result = $this->db->conditions($conditions, null);
  159. $this->assertEqual(array('key' => array('$or' => array(1, 2))), $result);
  160. }
  161. public function testMongoConditionalOperators() {
  162. $conditions = array('key' => array('<' => 10));
  163. $expected = array('key' => array('$lt' => 10));
  164. $result = $this->db->conditions($conditions, null);
  165. $this->assertEqual($expected, $result);
  166. $conditions = array('key' => array('<=' => 10));
  167. $expected = array('key' => array('$lte' => 10));
  168. $result = $this->db->conditions($conditions, null);
  169. $this->assertEqual($expected, $result);
  170. $conditions = array('key' => array('>' => 10));
  171. $expected = array('key' => array('$gt' => 10));
  172. $result = $this->db->conditions($conditions, null);
  173. $this->assertEqual($expected, $result);
  174. $conditions = array('key' => array('>=' => 10));
  175. $expected = array('key' => array('$gte' => 10));
  176. $result = $this->db->conditions($conditions, null);
  177. $this->assertEqual($expected, $result);
  178. $conditions = array('key' => array('!=' => 10));
  179. $expected = array('key' => array('$ne' => 10));
  180. $result = $this->db->conditions($conditions, null);
  181. $this->assertEqual($expected, $result);
  182. $conditions = array('key' => array('<>' => 10));
  183. $expected = array('key' => array('$ne' => 10));
  184. $result = $this->db->conditions($conditions, null);
  185. $this->assertEqual($expected, $result);
  186. $conditions = array('key' => array('!=' => array(10, 20, 30)));
  187. $expected = array('key' => array('$nin' => array(10, 20, 30)));
  188. $result = $this->db->conditions($conditions, null);
  189. $this->assertEqual($expected, $result);
  190. $conditions = array('key' => array('<>' => array(10, 20, 30)));
  191. $expected = array('key' => array('$nin' => array(10, 20, 30)));
  192. $result = $this->db->conditions($conditions, null);
  193. $this->assertEqual($expected, $result);
  194. $conditions = array('key' => array('like' => '/regex/i'));
  195. $result = $this->db->conditions($conditions, null);
  196. $expected = array('key' => new MongoRegex('/regex/i'));
  197. $this->assertEqual($expected, $result);
  198. }
  199. public function testReadNoConditions() {
  200. $this->db->connect();
  201. $connection = $this->db->connection;
  202. $this->db->connection = new MockMongoSource();
  203. $this->db->connection->resultSets = array(array('ok' => true));
  204. $data = array('title' => 'Test Post');
  205. $options = array('safe' => false, 'fsync' => false);
  206. $this->query->data($data);
  207. $this->assertIdentical(true, $this->db->create($this->query));
  208. $this->assertEqual(compact('data', 'options'), end($this->db->connection->queries));
  209. $this->db->connection->resultSets = array(array(array('_id' => new MongoId()) + $data));
  210. $result = $this->db->read($this->query);
  211. $this->assertTrue($result instanceof DocumentSet);
  212. $this->assertEqual(1, $result->count());
  213. $this->assertEqual('Test Post', $result->first()->title);
  214. $this->db->connection = $connection;
  215. }
  216. public function testReadWithConditions() {
  217. $this->db->connect();
  218. $connection = $this->db->connection;
  219. $this->db->connection = new MockMongoSource();
  220. $this->db->connection->resultSets = array(array('ok' => true));
  221. $data = array('title' => 'Test Post');
  222. $options = array('safe' => false, 'fsync' => false);
  223. $this->query->data($data);
  224. $this->assertTrue($this->db->create($this->query));
  225. $this->query->data(null);
  226. $this->db->connection->resultSets = array(array());
  227. $this->query->conditions(array('title' => 'Nonexistent Post'));
  228. $result = $this->db->read($this->query);
  229. $this->assertTrue($result);
  230. $this->assertEqual(0, $result->count());
  231. $this->db->connection->resultSets = array(array($data));
  232. $this->query->conditions($data);
  233. $result = $this->db->read($this->query);
  234. $this->assertTrue($result);
  235. $this->assertEqual(1, $result->count());
  236. $this->db->connection = $connection;
  237. }
  238. public function testUpdate() {
  239. $model = $this->_model;
  240. $data = array('title' => 'Test Post');
  241. $this->query->model($model);
  242. $this->query->data($data);
  243. $this->db->connection->results = array(true);
  244. $this->db->create($this->query);
  245. $result = array_pop($this->db->connection->queries);
  246. $data['_id'] = $result['data']['_id'];
  247. $expected = compact('data') + array(
  248. 'collection' => 'posts',
  249. 'type' => 'insert',
  250. 'options' => array('safe' => false, 'fsync' => false)
  251. );
  252. $this->assertEqual($expected, $result);
  253. $this->db->connection->results = array(
  254. new MockResult(array('data' => array($data))),
  255. new MockResult(array('data' => array($data)))
  256. );
  257. $this->db->connection->queries = array();
  258. $result = $this->db->read(new Query(compact('model')));
  259. $original = $result->first()->to('array');
  260. $this->assertEqual(array('title', '_id'), array_keys($original));
  261. $this->assertEqual('Test Post', $original['title']);
  262. $this->assertPattern('/^[0-9a-f]{24}$/', $original['_id']);
  263. $this->db->connection->results = array(true);
  264. $this->db->connection->queries = array();
  265. $update = array('title' => 'New Post Title');
  266. $this->query = new Query(compact('model') + array(
  267. 'data' => $update,
  268. 'conditions' => array('_id' => $original['_id'])
  269. ));
  270. $this->assertTrue($this->db->update($this->query));
  271. $result = array_pop($this->db->connection->queries);
  272. $expected = array(
  273. 'type' => 'update',
  274. 'collection' => 'posts',
  275. 'conditions' => array('_id' => '4f188fb17675ab167900010e'),
  276. 'update' => array('$set' => array('title' => 'New Post Title')),
  277. 'options' => array(
  278. 'upsert' => false, 'multiple' => true, 'safe' => false, 'fsync' => false
  279. )
  280. );
  281. array_push($this->db->connection->results, new MockResult(array(
  282. 'data' => array($update + $original)
  283. )));
  284. $this->db->connection->queries = array();
  285. $result = $this->db->read(new Query(compact('model') + array(
  286. 'conditions' => array('_id' => $original['_id'])
  287. )));
  288. $this->assertEqual(1, $result->count());
  289. $updated = $result->first();
  290. $updated = $updated ? $updated->to('array') : array();
  291. $this->assertEqual($original['_id'], $updated['_id']);
  292. $this->assertEqual('New Post Title', $updated['title']);
  293. $expected = array(
  294. 'type' => 'find',
  295. 'collection' => 'posts',
  296. 'fields' => array(),
  297. 'conditions' => array('_id' => $original['_id'])
  298. );
  299. $this->assertEqual($expected, array_pop($this->db->connection->queries));
  300. }
  301. public function testDelete() {
  302. $data = array('title' => 'Delete Me');
  303. array_push($this->db->connection->results, true);
  304. $this->query->data($data);
  305. $this->db->create($this->query);
  306. array_push($this->db->connection->results, new MockResult(array(
  307. 'data' => array()
  308. )));
  309. $this->assertFalse($this->db->read($this->query)->first());
  310. $result = array_pop($this->db->connection->queries);
  311. $conditions = array('_id' => $this->query->entity()->_id);
  312. $this->assertEqual($conditions, $result['conditions']);
  313. $this->assertTrue($this->query->entity()->exists());
  314. $model = $this->_model;
  315. $id = new MongoId();
  316. $this->query = new Query(compact('model') + array(
  317. 'entity' => new Document(compact('model') + array('data' => array('_id' => $id)))
  318. ));
  319. array_push($this->db->connection->results, true);
  320. $this->query->conditions($conditions);
  321. $this->assertTrue($this->db->delete($this->query));
  322. $this->assertFalse($this->query->entity()->exists());
  323. $expected = compact('conditions') + array(
  324. 'type' => 'remove',
  325. 'collection' => 'posts',
  326. 'options' => array('justOne' => false, 'safe' => false, 'fsync' => false)
  327. );
  328. $this->assertEqual($expected, array_pop($this->db->connection->queries));
  329. }
  330. public function testItem() {
  331. $model = $this->_model;
  332. $data = array('title' => 'New Item');
  333. $result = $this->db->item($model, $data);
  334. $this->assertTrue($result instanceof Document);
  335. $expected = $data;
  336. $result = $result->to('array');
  337. $this->assertEqual($expected, $result);
  338. }
  339. public function testCalculation() {
  340. $this->db->connection->results = array(new MockResult(array('data' => array(5))));
  341. $this->assertIdentical(5, $this->db->calculation('count', $this->query));
  342. }
  343. public function testEnabled() {
  344. $this->assertTrue(MongoDb::enabled());
  345. $this->assertTrue(MongoDb::enabled('arrays'));
  346. $this->assertTrue(MongoDb::enabled('booleans'));
  347. $this->assertTrue(MongoDb::enabled('relationships'));
  348. }
  349. public function testArbitraryMethodCalls() {
  350. $this->assertTrue(is_array($this->db->listDBs()));
  351. }
  352. public function testDocumentSorting() {
  353. $model = $this->_model;
  354. $model::config(array('meta' => array('source' => 'ordered_docs', 'locked' => false)));
  355. $first = array('title' => 'First document', 'position' => 1);
  356. $second = array('title' => 'Second document', 'position' => 2);
  357. $third = array('title' => 'Third document', 'position' => 3);
  358. $model::create($third)->save();
  359. $model::create($first)->save();
  360. $model::create($second)->save();
  361. $result = $this->db->connection->queries;
  362. $createOpts = array(
  363. 'validate' => true,
  364. 'events' => 'create',
  365. 'whitelist' => null,
  366. 'callbacks' => true,
  367. 'locked' => false,
  368. 'safe' => false,
  369. 'fsync' => false
  370. );
  371. $baseInsert = array(
  372. 'type' => 'insert',
  373. 'collection' => 'ordered_docs',
  374. 'options' => $createOpts
  375. );
  376. $expected = array(
  377. $baseInsert + array('data' => array('_id' => $result[0]['data']['_id']) + $third),
  378. $baseInsert + array('data' => array('_id' => $result[1]['data']['_id']) + $first),
  379. $baseInsert + array('data' => array('_id' => $result[2]['data']['_id']) + $second)
  380. );
  381. $this->assertEqual($expected, $result);
  382. array_push($this->db->connection->results, new MockResult(array(
  383. 'data' => array($first, $second, $third)
  384. )));
  385. $this->db->connection->queries = array();
  386. $documents = $model::all(array('order' => 'position'));
  387. $this->assertEqual($first['title'], $documents[0]->title);
  388. $this->assertEqual($second['title'], $documents[1]->title);
  389. $this->assertEqual($third['title'], $documents[2]->title);
  390. $expected = array(
  391. 'type' => 'find',
  392. 'collection' => 'ordered_docs',
  393. 'conditions' => array(),
  394. 'fields' => array()
  395. );
  396. $this->assertEqual($expected, array_pop($this->db->connection->queries));
  397. $result = $documents->result()->resource()->query['sort'];
  398. $this->assertEqual(array('position' => 1), $result);
  399. array_push($this->db->connection->results, new MockResult(array(
  400. 'data' => array($first, $second, $third)
  401. )));
  402. $documents = $model::all(array('order' => array('position' => 'asc')));
  403. $this->assertEqual($first['title'], $documents[0]->title);
  404. $this->assertEqual($second['title'], $documents[1]->title);
  405. $this->assertEqual($third['title'], $documents[2]->title);
  406. $this->assertEqual($expected, array_pop($this->db->connection->queries));
  407. $result = $documents->result()->resource()->query['sort'];
  408. $this->assertEqual(array('position' => 1), $result);
  409. array_push($this->db->connection->results, new MockResult(array(
  410. 'data' => array($third, $second, $first)
  411. )));
  412. $documents = $model::all(array('order' => array('position' => 'desc')));
  413. $this->assertEqual($third['title'], $documents[0]->title);
  414. $this->assertEqual($second['title'], $documents[1]->title);
  415. $this->assertEqual($first['title'], $documents[2]->title);
  416. $this->assertEqual($expected, array_pop($this->db->connection->queries));
  417. $result = $documents->result()->resource()->query['sort'];
  418. $this->assertEqual(array('position' => -1), $result);
  419. }
  420. public function testMongoIdPreservation() {
  421. $model = $this->_model;
  422. $model::config(array('meta' => array('locked' => false)));
  423. $post = $model::create(array('_id' => new MongoId(), 'title' => 'A post'));
  424. $post->save();
  425. $result = array_pop($this->db->connection->queries);
  426. $data = $result['data'];
  427. $this->assertEqual('A post', $data['title']);
  428. $this->assertTrue($data['_id'] instanceof MongoId);
  429. $post->sync();
  430. $post->title = 'An updated post';
  431. $post->save();
  432. $result = array_pop($this->db->connection->queries);
  433. $this->assertEqual(array('_id' => $post->_id), $result['conditions']);
  434. $this->assertEqual(array('$set' => array('title' => 'An updated post')), $result['update']);
  435. }
  436. public function testRelationshipGeneration() {
  437. $from = 'lithium\tests\mocks\data\MockComment';
  438. $to = 'lithium\tests\mocks\data\MockPost';
  439. $from::$connection = $this->db;
  440. $to::$connection = $this->db;
  441. $from::config(array(
  442. 'schema' => new Schema(array('fields' => array('comment_id')))
  443. ));
  444. $to::config(array('meta' => array('key' => '_id')));
  445. $result = $this->db->relationship($from, 'belongsTo', 'MockPost');
  446. $expected = array(
  447. 'name' => 'MockPost',
  448. 'type' => 'belongsTo',
  449. 'key' => array('mockComment' => '_id'),
  450. 'from' => $from,
  451. 'link' => 'contained',
  452. 'to' => $to,
  453. 'fields' => true,
  454. 'fieldName' => 'mockPost',
  455. 'constraints' => null,
  456. 'init' => true
  457. );
  458. $this->assertEqual($expected, $result->data());
  459. }
  460. public function testCreateNoConnectionException() {
  461. $db = new MongoDb(array('host' => '__invalid__', 'autoConnect' => false));
  462. $this->expectException('Could not connect to the database.');
  463. $result = $db->create(null);
  464. }
  465. public function testReadNoConnectionException() {
  466. $db = new MongoDb(array('host' => '__invalid__', 'autoConnect' => false));
  467. $this->expectException('Could not connect to the database.');
  468. $result = $db->read(null);
  469. }
  470. public function testUpdateNoConnectionException() {
  471. $db = new MongoDb(array('host' => '__invalid__', 'autoConnect' => false));
  472. $this->expectException('Could not connect to the database.');
  473. $result = $db->update(null);
  474. }
  475. public function testDeleteNoConnectionException() {
  476. $db = new MongoDb(array('host' => '__invalid__', 'autoConnect' => false));
  477. $this->expectException('Could not connect to the database.');
  478. $result = $db->delete(null);
  479. }
  480. public function testSourcesNoConnectionException() {
  481. $db = new MongoDb(array('host' => null, 'autoConnect' => false));
  482. $this->expectException('Could not connect to the database.');
  483. $result = $db->sources(null);
  484. }
  485. public function testAtomicUpdate() {
  486. $model = $this->_model;
  487. $model::config(array('meta' => array('source' => 'posts')));
  488. $data = array('initial' => 'one', 'values' => 'two');
  489. $this->db->connection = new MockMongoConnection();
  490. $this->db->connection->results = array(true, true);
  491. $document = $model::create($data);
  492. $this->assertTrue($document->save());
  493. $result = array_shift($this->db->connection->queries);
  494. $expected = array(
  495. 'type' => 'insert',
  496. 'collection' => 'posts',
  497. 'data' => array('initial' => 'one', 'values' => 'two', '_id' => $document->_id),
  498. 'options' => array(
  499. 'validate' => true, 'events' => 'create', 'whitelist' => null, 'callbacks' => true,
  500. 'locked' => false, 'safe' => false, 'fsync' => false
  501. )
  502. );
  503. $this->assertEqual($expected, $result);
  504. $duplicate = $model::create(array('_id' => $document->_id), array('exists' => true));
  505. $duplicate->values = 'new';
  506. $this->assertTrue($duplicate->save());
  507. $result = array_shift($this->db->connection->queries);
  508. $expected = array(
  509. 'type' => 'update',
  510. 'collection' => 'posts',
  511. 'conditions' => array('_id' => $document->_id),
  512. 'update' => array('$set' => array('values' => 'new')),
  513. 'options' => array(
  514. 'validate' => true, 'events' => 'update', 'whitelist' => null,
  515. 'callbacks' => true, 'locked' => false, 'upsert' => false, 'multiple' => true,
  516. 'safe' => false, 'fsync' => false
  517. )
  518. );
  519. $this->assertEqual($expected, $result);
  520. array_push($this->db->connection->results, new MockResult(array('data' => array(
  521. array('_id' => $duplicate->_id, 'initial' => 'one', 'values' => 'new')
  522. ))));
  523. $document = $model::find($duplicate->_id);
  524. $expected = array('_id' => (string) $duplicate->_id, 'initial' => 'one', 'values' => 'new');
  525. $this->assertEqual($expected, $document->data());
  526. $result = array_shift($this->db->connection->queries);
  527. $expected = array(
  528. 'type' => 'find', 'collection' => 'posts', 'fields' => array(), 'conditions' => array(
  529. '_id' => $duplicate->_id
  530. )
  531. );
  532. $this->assertEqual($expected, $result);
  533. }
  534. /**
  535. * Tests that the MongoDB adapter will not attempt to overwrite the _id field on document
  536. * update.
  537. */
  538. public function testPreserveId() {
  539. $model = $this->_model;
  540. $document = $model::create(array('_id' => 'custom'), array('exists' => true));
  541. array_push($this->db->connection->results, true);
  542. $this->assertTrue($document->save(array('_id' => 'custom2', 'foo' => 'bar')));
  543. $result = array_shift($this->db->connection->queries);
  544. $expected = array('$set' => array('foo' => 'bar'));
  545. $this->assertEqual($expected, $result['update']);
  546. }
  547. public function testCastingConditionsValues() {
  548. $query = new Query(array('schema' => new Schema(array('fields' => $this->_schema))));
  549. $conditions = array('_id' => new MongoId("4c8f86167675abfabdbe0300"));
  550. $result = $this->db->conditions($conditions, $query);
  551. $this->assertEqual($conditions, $result);
  552. $conditions = array('_id' => "4c8f86167675abfabdbe0300");
  553. $result = $this->db->conditions($conditions, $query);
  554. $this->assertEqual(array_keys($conditions), array_keys($result));
  555. $this->assertTrue($result['_id'] instanceof MongoId);
  556. $this->assertEqual($conditions['_id'], (string) $result['_id']);
  557. $conditions = array('_id' => array(
  558. "4c8f86167675abfabdbe0300", "4c8f86167675abfabdbf0300", "4c8f86167675abfabdc00300"
  559. ));
  560. $result = $this->db->conditions($conditions, $query);
  561. $this->assertEqual(3, count($result['_id']['$in']));
  562. foreach (array(0, 1, 2) as $i) {
  563. $this->assertTrue($result['_id']['$in'][$i] instanceof MongoId);
  564. }
  565. $conditions = array('voters' => array('$all' => array(
  566. "4c8f86167675abfabdbf0300", "4c8f86167675abfabdc00300"
  567. )));
  568. $result = $this->db->conditions($conditions, $query);
  569. $this->assertEqual(2, count($result['voters']['$all']));
  570. $result = $result['voters']['$all'];
  571. foreach (array(0, 1) as $i) {
  572. $this->assertTrue($result[$i] instanceof MongoId);
  573. $this->assertEqual($conditions['voters']['$all'][$i], (string) $result[$i]);
  574. }
  575. $conditions = array('$or' => array(
  576. array('_id' => "4c8f86167675abfabdbf0300"),
  577. array('guid' => "4c8f86167675abfabdbf0300")
  578. ));
  579. $result = $this->db->conditions($conditions, $query);
  580. $this->assertEqual(array('$or'), array_keys($result));
  581. $this->assertEqual(2, count($result['$or']));
  582. foreach (array('_id', 'guid') as $i => $key) {
  583. $this->assertTrue($result['$or'][$i][$key] instanceof MongoId);
  584. $this->assertEqual($conditions['$or'][$i][$key], (string) $result['$or'][$i][$key]);
  585. }
  586. }
  587. public function testMultiOperationConditions() {
  588. $conditions = array('loc' => array('$near' => array(50, 50), '$maxDistance' => 5));
  589. $result = $this->db->conditions($conditions, $this->query);
  590. $this->assertEqual($conditions, $result);
  591. }
  592. public function testCreateWithEmbeddedObjects() {
  593. $data = array(
  594. '_id' => new MongoId(),
  595. 'created' => new MongoDate(strtotime('-1 hour')),
  596. 'list' => array('foo', 'bar', 'baz')
  597. );
  598. $entity = new Document(compact('data') + array('exists' => false));
  599. $query = new Query(array('type' => 'create') + compact('entity'));
  600. $result = $query->export($this->db);
  601. $this->assertIdentical($data, $result['data']['data']);
  602. }
  603. public function testUpdateWithEmbeddedObjects() {
  604. $data = array(
  605. '_id' => new MongoId(),
  606. 'created' => new MongoDate(strtotime('-1 hour')),
  607. 'list' => array('foo', 'bar', 'baz')
  608. );
  609. $model = $this->_model;
  610. $fields = array('updated' => array('type' => 'MongoDate'));
  611. $schema = new Schema(compact('fields'));
  612. $entity = new Document(compact('data', 'schema', 'model') + array('exists' => true));
  613. $entity->updated = time();
  614. $entity->list[] = 'dib';
  615. $query = new Query(array('type' => 'update') + compact('entity'));
  616. $result = $query->export($this->db);
  617. $expected = array('_id', 'created', 'list', 'updated');
  618. $this->assertEqual($expected, array_keys($result['data']['update']));
  619. $this->assertTrue($result['data']['update']['updated'] instanceof MongoDate);
  620. }
  621. /**
  622. * Assert that Mongo and the Mongo Exporter don't mangle manual geospatial queries.
  623. */
  624. public function testGeoQueries() {
  625. $coords = array(84.13, 11.38);
  626. $coords2 = array_map(function($point) { return $point + 5; }, $coords);
  627. $conditions = array('location' => array('$near' => $coords));
  628. $query = new Query(compact('conditions') + array('model' => $this->_model));
  629. $result = $query->export($this->db);
  630. $this->assertEqual($result['conditions'], $conditions);
  631. $conditions = array('location' => array(
  632. '$within' => array('$box' => array($coords2, $coords))
  633. ));
  634. $query = new Query(compact('conditions') + array('model' => $this->_model));
  635. $result = $query->export($this->db);
  636. $this->assertEqual($conditions, $result['conditions']);
  637. }
  638. public function testSchemaCallback() {
  639. $schema = array('_id' => array('type' => 'id'), 'created' => array('type' => 'date'));
  640. $db = new MongoDb(array('autoConnect' => false, 'schema' => function() use ($schema) {
  641. return $schema;
  642. }));
  643. $this->assertEqual($schema, $db->describe(null)->fields());
  644. }
  645. public function testSetReadPreference() {
  646. $prefs = array(
  647. "SECONDARY",
  648. array('dc' => 'east', 'use' => 'reporting')
  649. );
  650. $db = new MongoDb(array(
  651. 'readPreference' => $prefs,
  652. 'classes' => array(
  653. 'server' => 'lithium\tests\mocks\core\MockCallable'
  654. )
  655. ));
  656. $result = $db->server->call;
  657. $this->assertEqual('setReadPreference', $result['method']);
  658. $this->assertEqual($prefs, $result['params']);
  659. }
  660. public function testDefaultSafeOptions() {
  661. $this->db = new MongoDb($this->_testConfig + array('safe' => true));
  662. $this->db->server = new MockMongoConnection();
  663. $this->db->connection = new MockCallable();
  664. $this->db->connection->custom = new MockCallable();
  665. $this->db->server->connected = true;
  666. $query = new Query(array('type' => 'read', 'source' => 'custom'));
  667. $this->db->create($query);
  668. $result = $this->db->connection->custom->call;
  669. $expected = array(null, array('safe' => true, 'fsync' => false));
  670. $this->assertEqual('insert', $result['method']);
  671. $this->assertEqual($expected, $result['params']);
  672. $query = new Query(array('type' => 'read', 'source' => 'custom'));
  673. $this->db->update($query);
  674. $result = $this->db->connection->custom->call;
  675. $expected = array('upsert' => false, 'multiple' => true, 'safe' => true, 'fsync' => false);
  676. $this->assertEqual('update', $result['method']);
  677. $this->assertEqual($expected, $result['params'][2]);
  678. $query = new Query(array('type' => 'read', 'source' => 'custom'));
  679. $this->db->delete($query);
  680. $result = $this->db->connection->custom->call;
  681. $expected = array('justOne' => false, 'safe' => true, 'fsync' => false);
  682. $this->assertEqual('remove', $result['method']);
  683. $this->assertEqual($expected, $result['params'][1]);
  684. $this->db = new MongoDb($this->_testConfig + array('safe' => false));
  685. $this->db->server = new MockMongoConnection();
  686. $this->db->connection = new MockCallable();
  687. $this->db->connection->custom = new MockCallable();
  688. $this->db->server->connected = true;
  689. $query = new Query(array('type' => 'read', 'source' => 'custom'));
  690. $this->db->create($query);
  691. $result = $this->db->connection->custom->call;
  692. $expected = array(null, array('safe' => false, 'fsync' => false));
  693. $this->assertEqual('insert', $result['method']);
  694. $this->assertEqual($expected, $result['params']);
  695. $query = new Query(array('type' => 'read', 'source' => 'custom'));
  696. $this->db->update($query);
  697. $result = $this->db->connection->custom->call;
  698. $expected = array('upsert' => false, 'multiple' => true, 'safe' => false, 'fsync' => false);
  699. $this->assertEqual('update', $result['method']);
  700. $this->assertEqual($expected, $result['params'][2]);
  701. $query = new Query(array('type' => 'read', 'source' => 'custom'));
  702. $this->db->delete($query);
  703. $result = $this->db->connection->custom->call;
  704. $expected = array('justOne' => false, 'safe' => false, 'fsync' => false);
  705. $this->assertEqual('remove', $result['method']);
  706. $this->assertEqual($expected, $result['params'][1]);
  707. }
  708. public function testGridFsCRUDWithDefaultPrefix() {
  709. $model = $this->_model;
  710. $source = 'fs.files';
  711. $data = array('filename' => 'lithium', 'file' => 'some_datas');
  712. $model::config(array('meta' => array('source' => $source, 'locked' => false)));
  713. $this->assertIdentical(true, $model::create()->save($data));
  714. $this->assertIdentical('fs', $this->db->connection->gridFsPrefix);
  715. $this->db->connection->gridFsPrefix = null;
  716. $model::config(array('meta' => array('source' => $source, 'locked' => false)));
  717. $this->db->connection->results = array(new MockResult(array('data' => $data)));
  718. $this->assertTrue($model::find('all'));
  719. $this->assertIdentical('fs', $this->db->connection->gridFsPrefix);
  720. $this->db->connection->gridFsPrefix = null;
  721. $model::create($data + array('_id' => new MongoId), array('exists' => true))->delete();
  722. $this->assertIdentical('fs', $this->db->connection->gridFsPrefix);
  723. $this->db->connection->gridFsPrefix = null;
  724. }
  725. public function testGridFsCreateWithCustomPrefix() {
  726. $model = $this->_model;
  727. $data = array('filename' => 'lithium', 'file' => 'some_datas');
  728. $db = new MongoDb($this->_testConfig + array('gridPrefix' => 'custom'));
  729. $db->server = new MockMongoConnection();
  730. $db->connection = new MockMongoConnection();
  731. $db->server->connected = true;
  732. $model::$connection = $db;
  733. $model::config(array('meta' => array('source' => 'fs.files', 'locked' => false)));
  734. $this->assertIdentical(false, $model::create()->save($data));
  735. $this->assertIdentical(null, $db->connection->gridFsPrefix);
  736. $model::config(array('meta' => array('source' => 'custom.files', 'locked' => false)));
  737. $this->assertIdentical(true, $model::create()->save($data));
  738. $this->assertIdentical('custom', $db->connection->gridFsPrefix);
  739. }
  740. public function testGridFsReadWithCustomPrefix() {
  741. $model = $this->_model;
  742. $data = array('filename' => 'lithium', 'file' => 'some_datas');
  743. $result = new MockResult(array('data' => array(
  744. array('filename' => 'lithium', 'file' => 'some_datas')
  745. )));
  746. $db = new MongoDb($this->_testConfig + array('gridPrefix' => 'custom'));
  747. $db->server = new MockMongoConnection();
  748. $db->connection = new MockMongoConnection();
  749. $db->server->connected = true;
  750. $model::$connection = $db;
  751. $model::config(array('meta' => array('source' => 'fs.files', 'locked' => false)));
  752. $db->connection->results = array($result);
  753. $this->assertTrue($model::find('all'));
  754. $this->assertIdentical(null, $db->connection->gridFsPrefix);
  755. $model::config(array('meta' => array('source' => 'custom.files', 'locked' => false)));
  756. $db->connection->results = array($result);
  757. $this->assertTrue($model::find('all'));
  758. $this->assertIdentical('custom', $db->connection->gridFsPrefix);
  759. }
  760. public function testGridFsDeleteWithCustomPrefix() {
  761. $model = $this->_model;
  762. $data = array('_id' => new MongoId);
  763. $db = new MongoDb($this->_testConfig + array('gridPrefix' => 'custom'));
  764. $db->server = new MockMongoConnection();
  765. $db->connection = new MockMongoConnection();
  766. $db->server->connected = true;
  767. $model::$connection = $db;
  768. $model::config(array('meta' => array('source' => 'fs.files', 'locked' => false)));
  769. $model::create($data, array('exists' => true))->delete();
  770. $this->assertIdentical(null, $db->connection->gridFsPrefix);
  771. $model::config(array('meta' => array('source' => 'custom.files', 'locked' => false)));
  772. $model::create($data, array('exists' => true))->delete();
  773. $this->assertIdentical('custom', $db->connection->gridFsPrefix);
  774. }
  775. public function testRespondsToParentCall() {
  776. $db = new MongoDb($this->_testConfig);
  777. $this->assertTrue($db->respondsTo('applyFilter'));
  778. $this->assertFalse($db->respondsTo('fooBarBaz'));
  779. }
  780. public function testRespondsToWithNoServer() {
  781. $db = new MongoDb($this->_testConfig);
  782. $this->assertFalse($db->respondsTo('listDBs'));
  783. $this->assertFalse($db->respondsTo('foobarbaz'));
  784. }
  785. public function testRespondsToWithServer() {
  786. $db = new MongoDb($this->_testConfig);
  787. $db->server = new MockMongoConnection();
  788. $this->assertTrue($db->respondsTo('listDBs'));
  789. $this->assertFalse($db->respondsTo('foobarbaz'));
  790. }
  791. }
  792. ?>