PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/cases/data/source/mongo_db/ExporterTest.php

https://bitbucket.org/d1rk/lithium
PHP | 413 lines | 326 code | 63 blank | 24 comment | 2 complexity | cdcacd3db329c264b15a8bf41b5ac9bf MD5 | raw file
  1. <?php
  2. /**
  3. * Lithium: the most rad php framework
  4. *
  5. * @copyright Copyright 2012, 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\mongo_db;
  9. use MongoId;
  10. use MongoDate;
  11. use lithium\data\source\MongoDb;
  12. use lithium\data\entity\Document;
  13. use lithium\data\collection\DocumentArray;
  14. use lithium\data\source\mongo_db\Exporter;
  15. class ExporterTest extends \lithium\test\Unit {
  16. protected $_model = 'lithium\tests\mocks\data\source\MockMongoPost';
  17. protected $_schema = array(
  18. '_id' => array('type' => 'id'),
  19. 'guid' => array('type' => 'id'),
  20. 'title' => array('type' => 'string'),
  21. 'tags' => array('type' => 'string', 'array' => true),
  22. 'comments' => array('type' => 'MongoId'),
  23. 'authors' => array('type' => 'MongoId', 'array' => true),
  24. 'created' => array('type' => 'MongoDate'),
  25. 'modified' => array('type' => 'datetime'),
  26. 'voters' => array('type' => 'id', 'array' => true),
  27. 'rank_count' => array('type' => 'integer', 'default' => 0),
  28. 'rank' => array('type' => 'float', 'default' => 0.0),
  29. 'notifications.foo' => array('type' => 'boolean'),
  30. 'notifications.bar' => array('type' => 'boolean'),
  31. 'notifications.baz' => array('type' => 'boolean')
  32. );
  33. protected $_handlers = array();
  34. public function skip() {
  35. $this->skipIf(!MongoDb::enabled(), 'MongoDb is not enabled');
  36. }
  37. public function setUp() {
  38. $this->_handlers = array(
  39. 'id' => function($v) {
  40. return is_string($v) && preg_match('/^[0-9a-f]{24}$/', $v) ? new MongoId($v) : $v;
  41. },
  42. 'date' => function($v) {
  43. $v = is_numeric($v) ? intval($v) : strtotime($v);
  44. return (time() == $v) ? new MongoDate() : new MongoDate($v);
  45. },
  46. 'regex' => function($v) { return new MongoRegex($v); },
  47. 'integer' => function($v) { return (integer) $v; },
  48. 'float' => function($v) { return (float) $v; },
  49. 'boolean' => function($v) { return (boolean) $v; },
  50. 'code' => function($v) { return new MongoCode($v); },
  51. 'binary' => function($v) { return new MongoBinData($v); }
  52. );
  53. $model = $this->_model;
  54. $model::resetConnection(true);
  55. }
  56. public function testInvalid() {
  57. $this->assertNull(Exporter::get(null, null));
  58. }
  59. public function testCreateWithFixedData() {
  60. $doc = new Document(array('exists' => false, 'data' => array(
  61. '_id' => new MongoId(),
  62. 'created' => new MongoDate(),
  63. 'numbers' => new DocumentArray(array('data' => array(7, 8, 9))),
  64. 'objects' => new DocumentArray(array('data' => array(
  65. new Document(array('data' => array('foo' => 'bar'))),
  66. new Document(array('data' => array('baz' => 'dib')))
  67. ))),
  68. 'deeply' => new Document(array('data' => array('nested' => 'object')))
  69. )));
  70. $this->assertEqual('object', $doc->deeply->nested);
  71. $this->assertTrue($doc->_id instanceof MongoId);
  72. $result = Exporter::get('create', $doc->export());
  73. $data = $doc->export();
  74. $this->assertTrue($result['create']['_id'] instanceof MongoId);
  75. $this->assertTrue($result['create']['created'] instanceof MongoDate);
  76. $this->assertIdentical(time(), $result['create']['created']->sec);
  77. $this->assertIdentical(array(7, 8, 9), $result['create']['numbers']);
  78. $expected = array(array('foo' => 'bar'), array('baz' => 'dib'));
  79. $this->assertIdentical($expected, $result['create']['objects']);
  80. $this->assertIdentical(array('nested' => 'object'), $result['create']['deeply']);
  81. }
  82. public function testCreateWithChangedData() {
  83. $doc = new Document(array('exists' => false, 'data' => array(
  84. 'numbers' => new DocumentArray(array('data' => array(7, 8, 9))),
  85. 'objects' => new DocumentArray(array('data' => array(
  86. new Document(array('data' => array('foo' => 'bar'))),
  87. new Document(array('data' => array('baz' => 'dib')))
  88. ))),
  89. 'deeply' => new Document(array('data' => array('nested' => 'object')))
  90. )));
  91. $doc->numbers[] = 10;
  92. $doc->deeply->nested2 = 'object2';
  93. $doc->objects[1]->dib = 'gir';
  94. $expected = array(
  95. 'numbers' => array(7, 8, 9, 10),
  96. 'objects' => array(array('foo' => 'bar'), array('baz' => 'dib', 'dib' => 'gir')),
  97. 'deeply' => array('nested' => 'object', 'nested2' => 'object2')
  98. );
  99. $result = Exporter::get('create', $doc->export());
  100. $this->assertEqual(array('create'), array_keys($result));
  101. $this->assertEqual($expected, $result['create']);
  102. }
  103. public function testUpdateWithNoChanges() {
  104. $doc = new Document(array('exists' => true, 'data' => array(
  105. 'numbers' => new DocumentArray(array('exists' => true, 'data' => array(7, 8, 9))),
  106. 'objects' => new DocumentArray(array('exists' => true, 'data' => array(
  107. new Document(array('exists' => true, 'data' => array('foo' => 'bar'))),
  108. new Document(array('exists' => true, 'data' => array('baz' => 'dib')))
  109. ))),
  110. 'deeply' => new Document(array('exists' => true, 'data' => array('nested' => 'object')))
  111. )));
  112. $this->assertFalse(Exporter::get('update', $doc->export()));
  113. }
  114. public function testUpdateWithSubObjects() {
  115. $model = $this->_model;
  116. $exists = true;
  117. $model::config(array('key' => '_id'));
  118. $doc = new Document(compact('model', 'exists') + array('data' => array(
  119. 'numbers' => new DocumentArray(compact('model', 'exists') + array(
  120. 'data' => array(7, 8, 9), 'pathKey' => 'numbers'
  121. )),
  122. 'objects' => new DocumentArray(compact('model', 'exists') + array(
  123. 'data' => array(
  124. new Document(
  125. compact('model', 'exists') + array('data' => array('foo' => 'bar'))
  126. ),
  127. new Document(
  128. compact('model', 'exists') + array('data' => array('foo' => 'baz'))
  129. )
  130. ), 'pathKey' => 'numbers'
  131. )),
  132. 'deeply' => new Document(compact('model', 'exists') + array(
  133. 'pathKey' => 'deeply', 'data' => array('nested' => 'object')
  134. )),
  135. 'foo' => 'bar'
  136. )));
  137. $doc->field = 'value';
  138. $doc->objects[1]->foo = 'dib';
  139. $doc->deeply->nested = 'foo';
  140. $doc->newObject = new Document(array(
  141. 'exists' => false, 'data' => array('subField' => 'subValue')
  142. ));
  143. $this->assertEqual('foo', $doc->deeply->nested);
  144. $this->assertEqual('subValue', $doc->newObject->subField);
  145. $doc->numbers = array(8, 9);
  146. $result = Exporter::get('update', $doc->export());
  147. $expected = array(
  148. 'numbers' => array(8, 9),
  149. 'newObject' => array('subField' => 'subValue'),
  150. 'field' => 'value',
  151. 'deeply.nested' => 'foo',
  152. 'objects.1.foo' => 'dib'
  153. );
  154. $this->assertEqual($expected, $result['update']);
  155. }
  156. public function testFieldRemoval() {
  157. $doc = new Document(array('exists' => true, 'data' => array(
  158. 'numbers' => new DocumentArray(array('data' => array(7, 8, 9))),
  159. 'deeply' => new Document(array(
  160. 'pathKey' => 'deeply', 'exists' => true, 'data' => array('nested' => 'object')
  161. )),
  162. 'foo' => 'bar'
  163. )));
  164. unset($doc->numbers);
  165. $result = Exporter::get('update', $doc->export());
  166. $expected = array(
  167. 'numbers' => true
  168. );
  169. $this->assertEqual($expected, $result['remove']);
  170. $doc->set(array('flagged' => true, 'foo' => 'baz', 'bar' => 'dib'));
  171. unset($doc->foo, $doc->flagged, $doc->numbers, $doc->deeply->nested);
  172. $result = Exporter::get('update', $doc->export());
  173. $expected = array(
  174. 'foo' => true, 'deeply.nested' => true, 'numbers' => true
  175. );
  176. $this->assertEqual($expected, $result['remove']);
  177. $this->assertEqual(array('bar' => 'dib'), $result['update']);
  178. }
  179. /**
  180. * Tests that when an existing object is attached as a value of another existing object, the
  181. * whole sub-object is re-written to the new value.
  182. */
  183. public function testAppendExistingObjects() {
  184. $doc = new Document(array('exists' => true, 'data' => array(
  185. 'deeply' => new Document(array(
  186. 'pathKey' => 'deeply', 'exists' => true, 'data' => array('nested' => 'object')
  187. )),
  188. 'foo' => 'bar'
  189. )));
  190. $append = new Document(array('exists' => true, 'data' => array('foo' => 'bar')));
  191. $doc->deeply = $append;
  192. $result = Exporter::get('update', $doc->export());
  193. $expected = array('update' => array('deeply' => array('foo' => 'bar')));
  194. $this->assertEqual($expected, $result);
  195. $expected = array('$set' => array('deeply' => array('foo' => 'bar')));
  196. $this->assertEqual($expected, Exporter::toCommand($result));
  197. $doc->sync();
  198. $doc->append2 = new Document(array('exists' => false, 'data' => array('foo' => 'bar')));
  199. $expected = array('update' => array('append2' => array('foo' => 'bar')));
  200. $this->assertEqual($expected, Exporter::get('update', $doc->export()));
  201. $doc->sync();
  202. $this->assertFalse(Exporter::get('update', $doc->export()));
  203. $doc->append2->foo = 'baz';
  204. $doc->append2->bar = 'dib';
  205. $doc->deeply->nested = true;
  206. $expected = array('update' => array(
  207. 'append2.foo' => 'baz', 'append2.bar' => 'dib', 'deeply.nested' => true
  208. ));
  209. $this->assertEqual($expected, Exporter::get('update', $doc->export()));
  210. }
  211. public function testNestedObjectCasting() {
  212. $model = $this->_model;
  213. $data = array('notifications' => array('foo' => '', 'bar' => '1', 'baz' => 0, 'dib' => 42));
  214. $model::schema($this->_schema);
  215. $result = Exporter::cast($data, $this->_schema, $model::connection(), compact('model'));
  216. $this->assertIdentical(false, $result['notifications']->foo);
  217. $this->assertIdentical(true, $result['notifications']->bar);
  218. $this->assertIdentical(false, $result['notifications']->baz);
  219. $this->assertIdentical(42, $result['notifications']->dib);
  220. }
  221. /**
  222. * Tests handling type values based on specified schema settings.
  223. *
  224. * @return void
  225. */
  226. public function testTypeCasting() {
  227. $data = array(
  228. '_id' => '4c8f86167675abfabd970300',
  229. 'title' => 'Foo',
  230. 'tags' => 'test',
  231. 'comments' => array(
  232. "4c8f86167675abfabdbe0300", "4c8f86167675abfabdbf0300", "4c8f86167675abfabdc00300"
  233. ),
  234. 'authors' => '4c8f86167675abfabdb00300',
  235. 'created' => time(),
  236. 'modified' => date('Y-m-d H:i:s'),
  237. 'rank_count' => '45',
  238. 'rank' => '3.45688'
  239. );
  240. $time = time();
  241. $model = $this->_model;
  242. $handlers = $this->_handlers;
  243. $options = compact('model', 'handlers');
  244. $result = Exporter::cast($data, $this->_schema, $model::connection(), $options);
  245. $this->assertEqual(array_keys($data), array_keys($result));
  246. $this->assertTrue($result['_id'] instanceof MongoId);
  247. $this->assertEqual('4c8f86167675abfabd970300', (string) $result['_id']);
  248. $this->assertTrue($result['comments'] instanceof DocumentArray);
  249. $this->assertEqual(3, count($result['comments']));
  250. $this->assertTrue($result['comments'][0] instanceof MongoId);
  251. $this->assertTrue($result['comments'][1] instanceof MongoId);
  252. $this->assertTrue($result['comments'][2] instanceof MongoId);
  253. $this->assertEqual('4c8f86167675abfabdbe0300', (string) $result['comments'][0]);
  254. $this->assertEqual('4c8f86167675abfabdbf0300', (string) $result['comments'][1]);
  255. $this->assertEqual('4c8f86167675abfabdc00300', (string) $result['comments'][2]);
  256. $this->assertEqual($data['comments'], $result['comments']->data());
  257. $this->assertEqual(array('test'), $result['tags']->data());
  258. $this->assertEqual(array('4c8f86167675abfabdb00300'), $result['authors']->data());
  259. $this->assertTrue($result['authors'][0] instanceof MongoId);
  260. $this->assertTrue($result['modified'] instanceof MongoDate);
  261. $this->assertTrue($result['created'] instanceof MongoDate);
  262. $this->assertTrue($result['created']->sec > 0);
  263. $this->assertEqual($time, $result['modified']->sec);
  264. $this->assertEqual($time, $result['created']->sec);
  265. $this->assertIdentical(45, $result['rank_count']);
  266. $this->assertIdentical(3.45688, $result['rank']);
  267. }
  268. public function testWithArraySchema() {
  269. $model = $this->_model;
  270. $model::schema(array(
  271. '_id' => array('type' => 'id'),
  272. 'list' => array('type' => 'string', 'array' => true),
  273. 'obj.foo' => array('type' => 'string'),
  274. 'obj.bar' => array('type' => 'string')
  275. ));
  276. $doc = new Document(compact('model'));
  277. $doc->list[] = array('foo' => '!!', 'bar' => '??');
  278. $data = array('list' => array(array('foo' => '!!', 'bar' => '??')));
  279. $this->assertEqual($data, $doc->data());
  280. $result = Exporter::get('create', $doc->export());
  281. $this->assertEqual($data, $result['create']);
  282. $result = Exporter::get('update', $doc->export());
  283. $this->assertEqual($data, $result['update']);
  284. $doc = new Document(compact('model'));
  285. $doc->list = array();
  286. $doc->list[] = array('foo' => '!!', 'bar' => '??');
  287. $data = array('list' => array(array('foo' => '!!', 'bar' => '??')));
  288. $this->assertEqual($data, $doc->data());
  289. $result = Exporter::get('create', $doc->export());
  290. $this->assertEqual($result['create'], $data);
  291. $result = Exporter::get('update', $doc->export());
  292. $this->assertEqual($result['update'], $data);
  293. }
  294. /**
  295. * Test that sub-objects are properly casted on creating a new `Document`.
  296. */
  297. public function testSubObjectCastingOnSave() {
  298. $model = $this->_model;
  299. $model::schema(array(
  300. 'sub.foo' => array('type' => 'boolean'),
  301. 'bar' => array('type' => 'boolean')
  302. ));
  303. $data = array('sub' => array('foo' => 0), 'bar' => 1);
  304. $doc = new Document(compact('data', 'model'));
  305. $this->assertIdentical(true, $doc->bar);
  306. $this->assertIdentical(false, $doc->sub->foo);
  307. $data = array('sub.foo' => '1', 'bar' => '0');
  308. $doc = new Document(compact('data', 'model', 'schema'));
  309. $this->assertIdentical(false, $doc->bar);
  310. $this->assertIdentical(true, $doc->sub->foo);
  311. }
  312. /**
  313. * Tests that a nested key on a previously saved document gets updated properly.
  314. */
  315. public function testExistingNestedKeyOverwrite() {
  316. $doc = new Document(array('model' => $this->_model));
  317. $doc->{'this.that'} = 'value1';
  318. $this->assertEqual(array('this' => array('that' => 'value1')), $doc->data());
  319. $result = Exporter::get('create', $doc->export());
  320. $this->assertEqual(array('create' => array('this' => array('that' => 'value1'))), $result);
  321. $doc->sync();
  322. $doc->{'this.that'} = 'value2';
  323. $this->assertEqual(array('this' => array('that' => 'value2')), $doc->data());
  324. $result = Exporter::get('update', $doc->export());
  325. $this->assertEqual(array('update' => array('this.that' => 'value2')), $result);
  326. }
  327. public function testUpdatingArraysAndExporting() {
  328. $new = new Document(array('data' => array('name' => 'Acme, Inc.', 'active' => true)));
  329. $expected = array('name' => 'Acme, Inc.', 'active' => true);
  330. $result = $new->data();
  331. $this->assertEqual($expected, $result);
  332. $new->foo = new DocumentArray(array('data' => array('bar')));
  333. $expected = array('name' => 'Acme, Inc.', 'active' => true, 'foo' => array('bar'));
  334. $result = $new->data();
  335. $this->assertEqual($expected, $result);
  336. $expected = 'bar';
  337. $result = $new->foo[0];
  338. $this->assertEqual($expected, $result);
  339. $new->foo[1] = 'baz';
  340. $expected = 'baz';
  341. $result = $new->data();
  342. $this->assertEqual($expected, $result['foo'][1]);
  343. }
  344. /**
  345. * @todo Implement me.
  346. */
  347. public function testCreateWithWhitelist() {
  348. }
  349. }
  350. ?>