UnwindDocumentSource_test.js 7.9 KB

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