AndExpression_test.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. "use strict";
  2. if (!module.parent) return require.cache[__filename] = 0, (new(require("mocha"))()).addFile(__filename).ui("exports").run(process.exit);
  3. var assert = require("assert"),
  4. Expression = require("../../../../lib/pipeline/expressions/Expression"),
  5. AndExpression = require("../../../../lib/pipeline/expressions/AndExpression"),
  6. VariablesParseState = require("../../../../lib/pipeline/expressions/VariablesParseState"),
  7. VariablesIdGenerator = require("../../../../lib/pipeline/expressions/VariablesIdGenerator"),
  8. utils = require("./utils"),
  9. constify = utils.constify,
  10. expressionToJson = utils.expressionToJson;
  11. var TestBase = function TestBase(overrides) {
  12. //NOTE: DEVIATION FROM MONGO: using this base class to make things easier to initialize
  13. for (var key in overrides) //jshint ignore:line
  14. this[key] = overrides[key];
  15. },
  16. ExpectedResultBase = (function() {
  17. var klass = function ExpectedResultBase() {
  18. base.apply(this, arguments);
  19. }, base = TestBase, proto = klass.prototype = Object.create(base.prototype);
  20. proto.run = function() {
  21. var specElement = this.spec instanceof Function ? this.spec() : this.spec,
  22. idGenerator = new VariablesIdGenerator(),
  23. vps = new VariablesParseState(idGenerator),
  24. expr = Expression.parseOperand(specElement, vps);
  25. assert.deepEqual(constify(specElement), expressionToJson(expr));
  26. var expectedResult = this.expectedResult instanceof Function ? this.expectedResult() : this.expectedResult;
  27. assert.strictEqual(expectedResult, expr.evaluate({a:1}));
  28. var optimized = expr.optimize();
  29. assert.strictEqual(expectedResult, optimized.evaluate({a:1}));
  30. };
  31. return klass;
  32. })(),
  33. OptimizeBase = (function() {
  34. var klass = function OptimizeBase() {
  35. base.apply(this, arguments);
  36. }, base = TestBase, proto = klass.prototype = Object.create(base.prototype);
  37. proto.run = function() {
  38. var specElement = this.spec instanceof Function ? this.spec() : this.spec,
  39. idGenerator = new VariablesIdGenerator(),
  40. vps = new VariablesParseState(idGenerator),
  41. expr = Expression.parseOperand(specElement, vps);
  42. assert.deepEqual(constify(specElement), expressionToJson(expr));
  43. var optimized = expr.optimize(),
  44. expectedOptimized = this.expectedOptimized instanceof Function ? this.expectedOptimized() : this.expectedOptimized;
  45. assert.deepEqual(expectedOptimized, expressionToJson(optimized));
  46. };
  47. return klass;
  48. })(),
  49. NoOptimizeBase = (function() {
  50. var klass = function NoOptimizeBase() {
  51. base.apply(this, arguments);
  52. }, base = OptimizeBase, proto = klass.prototype = Object.create(base.prototype);
  53. proto.expectedOptimized = function() {
  54. return constify(this.spec instanceof Function ? this.spec() : this.spec);
  55. };
  56. return klass;
  57. })();
  58. exports.AndExpression = {
  59. "constructor()": {
  60. "should construct instance": function() {
  61. assert(new AndExpression() instanceof AndExpression);
  62. assert(new AndExpression() instanceof Expression);
  63. },
  64. "should error if given args": function() {
  65. assert.throws(function() {
  66. new AndExpression("bad stuff");
  67. });
  68. },
  69. },
  70. "#getOpName()": {
  71. "should return the correct op name; $and": function() {
  72. assert.equal(new AndExpression().getOpName(), "$and");
  73. }
  74. },
  75. "#evaluate()": {
  76. "should return true if no operands": function testNoOperands() {
  77. /** $and without operands. */
  78. new ExpectedResultBase({
  79. spec: {$and:[]},
  80. expectedResult: true,
  81. }).run();
  82. },
  83. "should return true if given true": function testTrue() {
  84. /** $and passed 'true'. */
  85. new ExpectedResultBase({
  86. spec: {$and:[true]},
  87. expectedResult: true,
  88. }).run();
  89. },
  90. "should return false if given false": function testFalse() {
  91. /** $and passed 'false'. */
  92. new ExpectedResultBase({
  93. spec: {$and:[false]},
  94. expectedResult: false,
  95. }).run();
  96. },
  97. "should return true if given true and true": function testTrueTrue() {
  98. /** $and passed 'true', 'true'. */
  99. new ExpectedResultBase({
  100. spec: {$and:[true, true]},
  101. expectedResult: true,
  102. }).run();
  103. },
  104. "should return false if given true and false": function testTrueFalse() {
  105. /** $and passed 'true', 'false'. */
  106. new ExpectedResultBase({
  107. spec: {$and:[true, false]},
  108. expectedResult: false,
  109. }).run();
  110. },
  111. "should return false if given false and true": function testFalseTrue() {
  112. /** $and passed 'false', 'true'. */
  113. new ExpectedResultBase({
  114. spec: {$and:[false, true]},
  115. expectedResult: false,
  116. }).run();
  117. },
  118. "should return false if given false and false": function testFalseFalse() {
  119. /** $and passed 'false', 'false'. */
  120. new ExpectedResultBase({
  121. spec: {$and:[false, false]},
  122. expectedResult: false,
  123. }).run();
  124. },
  125. "should return true if given true and true and true": function testTrueTrueTrue() {
  126. /** $and passed 'true', 'true', 'true'. */
  127. new ExpectedResultBase({
  128. spec: {$and:[true, true, true]},
  129. expectedResult: true,
  130. }).run();
  131. },
  132. "should return false if given true and true and false": function testTrueTrueFalse() {
  133. /** $and passed 'true', 'true', 'false'. */
  134. new ExpectedResultBase({
  135. spec: {$and:[true, true, false]},
  136. expectedResult: false,
  137. }).run();
  138. },
  139. "should return false if given 0 and 1": function testZeroOne() {
  140. /** $and passed '0', '1'. */
  141. new ExpectedResultBase({
  142. spec: {$and:[0, 1]},
  143. expectedResult: false,
  144. }).run();
  145. },
  146. "should return true if given 1 and 2": function testOneTwo() {
  147. /** $and passed '1', '2'. */
  148. new ExpectedResultBase({
  149. spec: {$and:[1, 2]},
  150. expectedResult: true,
  151. }).run();
  152. },
  153. "should return true if given a field path to a truthy value": function testFieldPath() {
  154. /** $and passed a field path. */
  155. new ExpectedResultBase({
  156. spec: {$and:["$a"]},
  157. expectedResult: true,
  158. }).run();
  159. },
  160. },
  161. "#optimize()": {
  162. "should optimize a constant expression": function testOptimizeConstantExpression() {
  163. /** A constant expression is optimized to a constant. */
  164. new OptimizeBase({
  165. spec: {$and:[1]},
  166. expectedOptimized: {$const:true},
  167. }).run();
  168. },
  169. "should not optimize a non constant": function testNonConstant() {
  170. /** A non constant expression is not optimized. */
  171. new NoOptimizeBase({
  172. spec: {$and:["$a"]},
  173. }).run();
  174. },
  175. "should optimize truthy constant and truthy expression": function testConstantNonConstantTrue() {
  176. /** An expression beginning with a single constant is optimized. */
  177. new OptimizeBase({
  178. spec: {$and:[1,"$a"]},
  179. expectedOptimized: {$and:["$a"]},
  180. }).run();
  181. // note: using $and as serialization of ExpressionCoerceToBool rather than ExpressionAnd
  182. },
  183. "should optimize falsy constant and truthy expression": function testConstantNonConstantFalse() {
  184. new OptimizeBase({
  185. spec: {$and:[0,"$a"]},
  186. expectedOptimized: {$const:false},
  187. }).run();
  188. },
  189. "should optimize truthy expression and truthy constant": function testNonConstantOne() {
  190. /** An expression with a field path and '1'. */
  191. new OptimizeBase({
  192. spec: {$and:["$a",1]},
  193. expectedOptimized: {$and:["$a"]}
  194. }).run();
  195. },
  196. "should optimize truthy expression and falsy constant": function testNonConstantZero() {
  197. /** An expression with a field path and '0'. */
  198. new OptimizeBase({
  199. spec: {$and:["$a",0]},
  200. expectedOptimized: {$const:false},
  201. }).run();
  202. },
  203. "should optimize truthy expression, falsy expression, and truthy constant": function testNonConstantNonConstantOne() {
  204. /** An expression with two field paths and '1'. */
  205. new OptimizeBase({
  206. spec: {$and:["$a","$b",1]},
  207. expectedOptimized: {$and:["$a","$b"]}
  208. }).run();
  209. },
  210. "should optimize truthy expression, falsy expression, and falsy constant": function testNonConstantNonConstantZero() {
  211. /** An expression with two field paths and '0'. */
  212. new OptimizeBase({
  213. spec: {$and:["$a","$b",0]},
  214. expectedOptimized: {$const:false},
  215. }).run();
  216. },
  217. "should optimize to false if [0,1,'$a']": function testZeroOneNonConstant() {
  218. /** An expression with '0', '1', and a field path. */
  219. new OptimizeBase({
  220. spec: {$and:[0,1,"$a"]},
  221. expectedOptimized: {$const:false},
  222. }).run();
  223. },
  224. "should optimize to {$and:'$a'} if [1,1,'$a']": function testOneOneNonConstant() {
  225. /** An expression with '1', '1', and a field path. */
  226. new OptimizeBase({
  227. spec: {$and:[1,1,"$a"]},
  228. expectedOptimized: {$and:["$a"]},
  229. }).run();
  230. },
  231. "should optimize away nested truthy $and expressions": function testNested() {
  232. /** Nested $and expressions. */
  233. new OptimizeBase({
  234. spec: {$and:[1, {$and:[1]}, "$a", "$b"]},
  235. expectedOptimized: {$and:["$a","$b"]},
  236. }).run();
  237. },
  238. "should optimize to false if nested falsey $and expressions": function testNestedZero() {
  239. /** Nested $and expressions containing a nested value evaluating to false. */
  240. new OptimizeBase({
  241. spec: {$and:[1, {$and:[ {$and:[0]} ]}, "$a", "$b"]},
  242. expectedOptimized: {$const:false},
  243. }).run();
  244. },
  245. },
  246. };