MatchDocumentSource.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. "use strict";
  2. var assert = require("assert"),
  3. async = require("async"),
  4. DocumentSource = require("../../../../lib/pipeline/documentSources/DocumentSource"),
  5. MatchDocumentSource = require("../../../../lib/pipeline/documentSources/MatchDocumentSource");
  6. var testRedactSafe = function testRedactSafe(input, safePortion) {
  7. var match = MatchDocumentSource.createFromJson(input);
  8. assert.deepEqual(match.redactSafePortion(), safePortion);
  9. };
  10. module.exports = {
  11. "MatchDocumentSource": {
  12. "constructor()": {
  13. "should throw Error when constructing without args": function testConstructor(){
  14. assert.throws(function(){
  15. new MatchDocumentSource();
  16. });
  17. }
  18. },
  19. "#getSourceName()": {
  20. "should return the correct source name; $match": function testSourceName(){
  21. var mds = new MatchDocumentSource({ packet :{ $exists : false } });
  22. assert.strictEqual(mds.getSourceName(), "$match");
  23. }
  24. },
  25. "#serialize()": {
  26. "should append the match query to the input builder": function sourceToJsonTest(){
  27. var mds = new MatchDocumentSource({ location : { $in : ['Kentucky'] } });
  28. var t = mds.serialize(false);
  29. assert.deepEqual(t, { "$match" : { location : { $in : ['Kentucky'] } }});
  30. }
  31. },
  32. "#createFromJson()": {
  33. "should return a new MatchDocumentSource object from an input object": function createTest(){
  34. var t = MatchDocumentSource.createFromJson({ someval:{$exists:true} });
  35. assert.strictEqual(t instanceof MatchDocumentSource, true);
  36. }
  37. },
  38. "#getNext()": {
  39. "should throw an error if no callback is given": function() {
  40. var mds = new MatchDocumentSource({item:1});
  41. assert.throws(mds.getNext.bind(mds));
  42. },
  43. "should return the current document source": function currSource(next){
  44. var mds = new MatchDocumentSource({item: 1});
  45. mds.source = {getNext:function(cb){cb(null,{ item:1 });}};
  46. mds.getNext(function(err,val) {
  47. assert.deepEqual(val, { item:1 });
  48. next();
  49. });
  50. },
  51. "should return matched sources remaining": function (next){
  52. var mds = new MatchDocumentSource({ item: {$lt: 5} }),
  53. items = [ 1,2,3,4,5,6,7,8,9 ];
  54. mds.source = {
  55. calls: 0,
  56. getNext:function(cb) {
  57. if (this.calls >= items.length)
  58. return cb(null,DocumentSource.EOF);
  59. return cb(null,{item: items[this.calls++]});
  60. },
  61. dispose:function() { return true; }
  62. };
  63. async.series([
  64. mds.getNext.bind(mds),
  65. mds.getNext.bind(mds),
  66. mds.getNext.bind(mds),
  67. mds.getNext.bind(mds),
  68. mds.getNext.bind(mds),
  69. ],
  70. function(err,res) {
  71. assert.deepEqual([{item:1},{item:2},{item:3},{item:4},DocumentSource.EOF], res);
  72. next();
  73. }
  74. );
  75. },
  76. "should not return matched out documents for sources remaining": function (next){
  77. var mds = new MatchDocumentSource({ item: {$gt: 5} }),
  78. items = [ 1,2,3,4,5,6,7,8,9 ];
  79. mds.source = {
  80. calls: 0,
  81. getNext:function(cb) {
  82. if (this.calls >= items.length)
  83. return cb(null,DocumentSource.EOF);
  84. return cb(null,{item: items[this.calls++]});
  85. },
  86. dispose:function() { return true; }
  87. };
  88. async.series([
  89. mds.getNext.bind(mds),
  90. mds.getNext.bind(mds),
  91. mds.getNext.bind(mds),
  92. mds.getNext.bind(mds),
  93. mds.getNext.bind(mds),
  94. ],
  95. function(err,res) {
  96. assert.deepEqual([{item:6},{item:7},{item:8},{item:9},DocumentSource.EOF], res);
  97. next();
  98. }
  99. );
  100. },
  101. "should return EOF for no sources remaining": function (next){
  102. var mds = new MatchDocumentSource({ item: {$gt: 5} }),
  103. items = [ ];
  104. mds.source = {
  105. calls: 0,
  106. getNext:function(cb) {
  107. if (this.calls >= items.length)
  108. return cb(null,DocumentSource.EOF);
  109. return cb(null,{item: items[this.calls++]});
  110. },
  111. dispose:function() { return true; }
  112. };
  113. async.series([
  114. mds.getNext.bind(mds),
  115. ],
  116. function(err,res) {
  117. assert.deepEqual([DocumentSource.EOF], res);
  118. next();
  119. }
  120. );
  121. },
  122. },
  123. "#coalesce()": {
  124. "should return false if nextSource is not $match": function dontSkip(){
  125. var mds = new MatchDocumentSource({item: {$lt:3}});
  126. assert.equal(mds.coalesce({}), false);
  127. },
  128. "should return true if nextSource is $limit": function changeLimit(){
  129. var mds = new MatchDocumentSource({item:{$gt:1}}),
  130. mds2 = new MatchDocumentSource({item:{$lt:3}}),
  131. expected = {$and: [{item:{$gt:1}}, {item:{$lt:3}}]};
  132. var actual = mds.coalesce(mds2);
  133. assert.equal(actual, true);
  134. assert.deepEqual(mds.getQuery(), expected);
  135. },
  136. "should merge two MatchDocumentSources together": function() {
  137. var match1 = MatchDocumentSource.createFromJson({ a: 1 }),
  138. match2 = MatchDocumentSource.createFromJson({ b: 1 }),
  139. match3 = MatchDocumentSource.createFromJson({ c: 1 });
  140. // check initial state
  141. assert.deepEqual(match1.getQuery(), {a:1});
  142. assert.deepEqual(match2.getQuery(), {b:1});
  143. assert.deepEqual(match3.getQuery(), {c:1});
  144. assert.doesNotThrow(function() {
  145. match1.coalesce(match2);
  146. });
  147. assert.deepEqual(match1.getQuery(), {$and: [{a:1}, {b:1}]});
  148. },
  149. "should merge three MatchDocumentSources together": function() {
  150. var match1 = MatchDocumentSource.createFromJson({ a: 1 }),
  151. match2 = MatchDocumentSource.createFromJson({ b: 1 }),
  152. match3 = MatchDocumentSource.createFromJson({ c: 1 });
  153. // check initial state
  154. assert.deepEqual(match1.getQuery(), {a:1});
  155. assert.deepEqual(match2.getQuery(), {b:1});
  156. assert.deepEqual(match3.getQuery(), {c:1});
  157. assert.doesNotThrow(function() {
  158. match1.coalesce(match2);
  159. });
  160. assert.deepEqual(match1.getQuery(), {$and: [{a:1}, {b:1}]});
  161. assert.doesNotThrow(function() {
  162. match1.coalesce(match3);
  163. });
  164. assert.deepEqual(match1.getQuery(), {$and: [{$and: [{a:1}, {b:1}]}, {c:1}]});
  165. }
  166. },
  167. "#getQuery()": {
  168. "should return current query": function () {
  169. var mds = new MatchDocumentSource({item: {$gt:1}});
  170. var actual = mds.getQuery();
  171. assert.deepEqual(actual, {item:{$gt:1}});
  172. }
  173. },
  174. "#redactSafePortion()": {
  175. "empty match": function() {
  176. testRedactSafe({}, {});
  177. },
  178. "basic allowed things": function () {
  179. testRedactSafe({a:1},
  180. {a:1});
  181. testRedactSafe({a:'asdf'},
  182. {a:'asdf'});
  183. testRedactSafe({a:/asdf/i},
  184. {a:/asdf/i});
  185. testRedactSafe({a: {$regex: 'adsf'}},
  186. {a: {$regex: 'adsf'}});
  187. testRedactSafe({a: {$regex: 'adsf', $options: 'i'}},
  188. {a: {$regex: 'adsf', $options: 'i'}});
  189. testRedactSafe({a: {$mod: [1, 0]}},
  190. {a: {$mod: [1, 0]}});
  191. testRedactSafe({a: {$type: 1}},
  192. {a: {$type: 1}});
  193. },
  194. "basic disallowed things": function() {
  195. testRedactSafe({a: null},
  196. {});
  197. testRedactSafe({a: {}},
  198. {});
  199. testRedactSafe({a: []},
  200. {});
  201. testRedactSafe({'a.0': 1},
  202. {});
  203. testRedactSafe({'a.0.b': 1},
  204. {});
  205. testRedactSafe({a: {$ne: 1}},
  206. {});
  207. testRedactSafe({a: {$nin: [1, 2, 3]}},
  208. {});
  209. testRedactSafe({a: {$exists: true}}, // could be allowed but currently isn't
  210. {});
  211. testRedactSafe({a: {$exists: false}}, // can never be allowed
  212. {});
  213. testRedactSafe({a: {$size: 1}},
  214. {});
  215. testRedactSafe({$nor: [{a:1}]},
  216. {});
  217. },
  218. "Combinations": function() {
  219. testRedactSafe({a:1, b: 'asdf'},
  220. {a:1, b: 'asdf'});
  221. testRedactSafe({a:1, b: null},
  222. {a:1});
  223. testRedactSafe({a:null, b: null},
  224. {});
  225. },
  226. "$elemMatch": function() {
  227. testRedactSafe({a: {$elemMatch: {b: 1}}},
  228. {a:{$elemMatch:{b: 1}}});
  229. testRedactSafe({a:{$elemMatch:{b:null}}},
  230. {});
  231. testRedactSafe({a:{$elemMatch:{b:null, c:1}}},
  232. {a:{$elemMatch:{c: 1}}});
  233. },
  234. "explicit $and": function(){
  235. testRedactSafe({$and:[{a: 1}]},
  236. {$and:[{a: 1}]});
  237. testRedactSafe({$and:[{a: 1},{b: null}]},
  238. {$and:[{a: 1}]});
  239. testRedactSafe({$and:[{a: 1},{b: null, c:1}]},
  240. {$and:[{a: 1},{c:1}]});
  241. testRedactSafe({$and:[{a: null},{b: null}]},
  242. {});
  243. },
  244. "explicit $or": function() {
  245. testRedactSafe({$or:[{a: 1}]},
  246. {$or:[{a: 1}]});
  247. testRedactSafe({$or:[{a: 1},{b: null}]},
  248. {});
  249. testRedactSafe({$or:[{a: 1},{b: null, c:1}]},
  250. {$or:[{a: 1}, {c:1}]});
  251. testRedactSafe({$or:[{a: null},{b: null}]},
  252. {});
  253. testRedactSafe({},
  254. {});
  255. },
  256. "$all and $in": function() {
  257. testRedactSafe({a:{$all: [1, 0]}},
  258. {a: {$all: [1, 0]}});
  259. testRedactSafe({a:{$all: [1, 0, null]}},
  260. {a: {$all: [1, 0]}});
  261. testRedactSafe({a:{$all: [{$elemMatch: {b:1}}]}}, // could be allowed but currently isn't
  262. {});
  263. testRedactSafe({a:{$all: [1, 0, null]}},
  264. {a: {$all: [1, 0]}});
  265. testRedactSafe({a:{$in: [1, 0]}},
  266. {a: {$in: [1, 0]}});
  267. testRedactSafe({a:{$in: [1, 0, null]}},
  268. {});
  269. }
  270. }
  271. }
  272. };
  273. if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);