UnwindDocumentSource.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. var assert = require("assert"),
  2. DocumentSource = require("../../../../lib/pipeline/documentSources/DocumentSource"),
  3. UnwindDocumentSource = require("../../../../lib/pipeline/documentSources/UnwindDocumentSource"),
  4. CursorDocumentSource = require("../../../../lib/pipeline/documentSources/CursorDocumentSource"),
  5. Cursor = require("../../../../lib/Cursor");
  6. //HELPERS
  7. var assertExhausted = function assertExhausted(pds) {
  8. assert.ok(pds.eof());
  9. assert.ok(!pds.advance());
  10. };
  11. /**
  12. * Tests if the given rep is the same as what the pds resolves to as JSON.
  13. * MUST CALL WITH A PDS AS THIS (e.g. checkJsonRepresentation.call(this, rep) where this is a PDS)
  14. **/
  15. var checkJsonRepresentation = function checkJsonRepresentation(self, rep) {
  16. var pdsRep = {};
  17. self.sourceToJson(pdsRep, true);
  18. assert.deepEqual(pdsRep, rep);
  19. };
  20. var createUnwind = function createUnwind(unwind) {
  21. //let unwind be optional
  22. if(!unwind){
  23. unwind = "$a";
  24. }
  25. var spec = {"$unwind": unwind},
  26. specElement = unwind,
  27. unwindDs = UnwindDocumentSource.createFromJson(specElement);
  28. checkJsonRepresentation(unwindDs, spec);
  29. return unwindDs;
  30. };
  31. var addSource = function addSource(unwind, data) {
  32. var cwc = new CursorDocumentSource.CursorWithContext();
  33. cwc._cursor = new Cursor( data );
  34. var cds = new CursorDocumentSource(cwc);
  35. var pds = new UnwindDocumentSource();
  36. unwind.setSource(cds);
  37. };
  38. var checkResults = function checkResults(data, expectedResults, path){
  39. var unwind = createUnwind(path);
  40. addSource(unwind, data || []);
  41. expectedResults = expectedResults || [];
  42. var resultSet = [];
  43. while( !unwind.eof() ) {
  44. // If not eof, current is non null.
  45. assert.ok( unwind.getCurrent() );
  46. // Get the current result.
  47. resultSet.push( unwind.getCurrent() );
  48. // Advance.
  49. if ( unwind.advance() ) {
  50. // If advance succeeded, eof() is false.
  51. assert.equal( unwind.eof(), false );
  52. }
  53. }
  54. assert.deepEqual( resultSet, expectedResults );
  55. };
  56. var throwsException = function throwsException(data, path, expectedResults){
  57. assert.throws(function(){
  58. checkResults(data, path, expectedResults);
  59. });
  60. };
  61. //TESTS
  62. module.exports = {
  63. "UnwindDocumentSource": {
  64. "constructor()": {
  65. "should throw Error when constructing with args": function (){
  66. assert.throws(function(){
  67. new UnwindDocumentSource("$a");
  68. });
  69. }
  70. },
  71. "#getSourceName()": {
  72. "should return the correct source name; $unwind": function (){
  73. var pds = new UnwindDocumentSource();
  74. assert.strictEqual(pds.getSourceName(), "$unwind");
  75. }
  76. },
  77. "#eof()": {
  78. "should return true if source is empty": function (){
  79. var pds = createUnwind();
  80. addSource(pds, []);
  81. assert.strictEqual(pds.eof(), true);
  82. },
  83. "should return false if source documents exist": function (){
  84. var pds = createUnwind();
  85. addSource(pds, [{_id:0, a:[1]}]);
  86. assert.strictEqual(pds.eof(), false);
  87. }
  88. },
  89. "#advance()": {
  90. "should return false if there are no documents in the parent source": function () {
  91. var pds = createUnwind();
  92. addSource(pds, []);
  93. assert.strictEqual(pds.advance(), false);
  94. },
  95. "should return true if source documents exist and advance the source": function (){
  96. var pds = createUnwind();
  97. addSource(pds, [{_id:0, a:[1,2]}]);
  98. assert.strictEqual(pds.advance(), true);
  99. assert.strictEqual(pds.getCurrent().a, 2);
  100. }
  101. },
  102. "#getCurrent()": {
  103. "should return null if there are no documents in the parent source": function () {
  104. var pds = createUnwind();
  105. addSource(pds, []);
  106. assert.strictEqual(pds.getCurrent(), null);
  107. },
  108. "should return unwound documents": function (){
  109. var pds = createUnwind();
  110. addSource(pds, [{_id:0, a:[1,2]}]);
  111. assert.ok(pds.getCurrent());
  112. assert.strictEqual(pds.getCurrent().a, 1);
  113. },
  114. "A document without the unwind field produces no results.": function(){
  115. checkResults([{}]);
  116. },
  117. "A document with a null field produces no results.": function(){
  118. checkResults([{a:null}]);
  119. },
  120. "A document with an empty array produces no results.": function(){
  121. checkResults([{a:[]}]);
  122. },
  123. "A document with a number field produces a UserException.": function(){
  124. throwsException([{a:1}]);
  125. },
  126. "An additional document with a number field produces a UserException.": function(){
  127. throwsException([{a:[1]}, {a:1}]);
  128. },
  129. "A document with a string field produces a UserException.": function(){
  130. throwsException([{a:"foo"}]);
  131. },
  132. "A document with an object field produces a UserException.": function(){
  133. throwsException([{a:{}}]);
  134. },
  135. "Unwind an array with one value.": function(){
  136. checkResults(
  137. [{_id:0, a:[1]}],
  138. [{_id:0,a:1}]
  139. );
  140. },
  141. "Unwind an array with two values.": function(){
  142. checkResults(
  143. [{_id:0, a:[1, 2]}],
  144. [{_id:0,a:1}, {_id:0,a:2}]
  145. );
  146. },
  147. "Unwind an array with two values, one of which is null.": function(){
  148. checkResults(
  149. [{_id:0, a:[1, null]}],
  150. [{_id:0,a:1}, {_id:0,a:null}]
  151. );
  152. },
  153. "Unwind two documents with arrays.": function(){
  154. checkResults(
  155. [{_id:0, a:[1,2]}, {_id:0, a:[3,4]}],
  156. [{_id:0,a:1}, {_id:0,a:2}, {_id:0,a:3}, {_id:0,a:4}]
  157. );
  158. },
  159. "Unwind an array in a nested document.": function(){
  160. checkResults(
  161. [{_id:0,a:{b:[1,2],c:3}}],
  162. [{_id:0,a:{b:1,c:3}},{_id:0,a:{b:2,c:3}}],
  163. "$a.b"
  164. );
  165. },
  166. "A missing array (that cannot be nested below a non object field) produces no results.": function(){
  167. checkResults(
  168. [{_id:0,a:4}],
  169. [],
  170. "$a.b"
  171. );
  172. },
  173. "Unwind an array in a doubly nested document.": function(){
  174. checkResults(
  175. [{_id:0,a:{b:{d:[1,2],e:4},c:3}}],
  176. [{_id:0,a:{b:{d:1,e:4},c:3}},{_id:0,a:{b:{d:2,e:4},c:3}}],
  177. "$a.b.d"
  178. );
  179. },
  180. "Unwind several documents in a row.": function(){
  181. checkResults(
  182. [
  183. {_id:0,a:[1,2,3]},
  184. {_id:1},
  185. {_id:2},
  186. {_id:3,a:[10,20]},
  187. {_id:4,a:[30]}
  188. ],
  189. [
  190. {_id:0,a:1},
  191. {_id:0,a:2},
  192. {_id:0,a:3},
  193. {_id:3,a:10},
  194. {_id:3,a:20},
  195. {_id:4,a:30}
  196. ]
  197. );
  198. },
  199. "Unwind several more documents in a row.": function(){
  200. checkResults(
  201. [
  202. {_id:0,a:null},
  203. {_id:1},
  204. {_id:2,a:['a','b']},
  205. {_id:3},
  206. {_id:4,a:[1,2,3]},
  207. {_id:5,a:[4,5,6]},
  208. {_id:6,a:[7,8,9]},
  209. {_id:7,a:[]}
  210. ],
  211. [
  212. {_id:2,a:'a'},
  213. {_id:2,a:'b'},
  214. {_id:4,a:1},
  215. {_id:4,a:2},
  216. {_id:4,a:3},
  217. {_id:5,a:4},
  218. {_id:5,a:5},
  219. {_id:5,a:6},
  220. {_id:6,a:7},
  221. {_id:6,a:8},
  222. {_id:6,a:9}
  223. ]
  224. );
  225. }
  226. },
  227. "#createFromJson()": {
  228. "should error if called with non-string": function testNonObjectPassed() {
  229. //Date as arg
  230. assert.throws(function() {
  231. var pds = createUnwind(new Date());
  232. });
  233. //Array as arg
  234. assert.throws(function() {
  235. var pds = createUnwind([]);
  236. });
  237. //Empty args
  238. assert.throws(function() {
  239. var pds = UnwindDocumentSource.createFromJson();
  240. });
  241. //Top level operator
  242. assert.throws(function() {
  243. var pds = createUnwind({$add: []});
  244. });
  245. }
  246. },
  247. "#getDependencies": {
  248. "should Dependant field paths.": function () {
  249. var pds = createUnwind("$x.y.z"),
  250. deps = {};
  251. assert.strictEqual(pds.getDependencies(deps), DocumentSource.GetDepsReturn.SEE_NEXT);
  252. assert.deepEqual(deps, {"x.y.z":1});
  253. }
  254. }
  255. }
  256. };
  257. if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);