OrExpression_test.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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. OrExpression = require("../../../../lib/pipeline/expressions/OrExpression"),
  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.OrExpression = {
  59. "constructor()": {
  60. "should construct instance": function() {
  61. assert(new OrExpression() instanceof OrExpression);
  62. assert(new OrExpression() instanceof Expression);
  63. },
  64. "should error if given args": function() {
  65. assert.throws(function() {
  66. new OrExpression("bad stuff");
  67. });
  68. },
  69. },
  70. "#getOpName()": {
  71. "should return the correct op name; $or": function(){
  72. assert.equal(new OrExpression().getOpName(), "$or");
  73. }
  74. },
  75. "#evaluate()": {
  76. "should return false if no operands": function testNoOperands(){
  77. /** $or without operands. */
  78. new ExpectedResultBase({
  79. spec: {$or:[]},
  80. expectedResult: false,
  81. }).run();
  82. },
  83. "should return true if given true": function testTrue(){
  84. /** $or passed 'true'. */
  85. new ExpectedResultBase({
  86. spec: {$or:[true]},
  87. expectedResult: true,
  88. }).run();
  89. },
  90. "should return false if given false": function testFalse(){
  91. /** $or passed 'false'. */
  92. new ExpectedResultBase({
  93. spec: {$or:[false]},
  94. expectedResult: false,
  95. }).run();
  96. },
  97. "should return true if given true and true": function testTrueTrue(){
  98. /** $or passed 'true', 'true'. */
  99. new ExpectedResultBase({
  100. spec: {$or:[true, true]},
  101. expectedResult: true,
  102. }).run();
  103. },
  104. "should return true if given true and false": function testTrueFalse(){
  105. /** $or passed 'true', 'false'. */
  106. new ExpectedResultBase({
  107. spec: {$or:[true, false]},
  108. expectedResult: true,
  109. }).run();
  110. },
  111. "should return true if given false and true": function testFalseTrue(){
  112. /** $or passed 'false', 'true'. */
  113. new ExpectedResultBase({
  114. spec: {$or:[false, true]},
  115. expectedResult: true,
  116. }).run();
  117. },
  118. "should return false if given false and false": function testFalseFalse(){
  119. /** $or passed 'false', 'false'. */
  120. new ExpectedResultBase({
  121. spec: {$or:[false, false]},
  122. expectedResult: false,
  123. }).run();
  124. },
  125. "should return false if given false and false and false": function testFalseFalseFalse(){
  126. /** $or passed 'false', 'false', 'false'. */
  127. new ExpectedResultBase({
  128. spec: {$or:[false, false, false]},
  129. expectedResult: false,
  130. }).run();
  131. },
  132. "should return true if given false and false and true": function testFalseFalseTrue(){
  133. /** $or passed 'false', 'false', 'true'. */
  134. new ExpectedResultBase({
  135. spec: {$or:[false, false, true]},
  136. expectedResult: true,
  137. }).run();
  138. },
  139. "should return true if given 0 and 1": function testZeroOne(){
  140. /** $or passed '0', '1'. */
  141. new ExpectedResultBase({
  142. spec: {$or:[0, 1]},
  143. expectedResult: true,
  144. }).run();
  145. },
  146. "should return false if given 0 and false": function testZeroFalse(){
  147. /** $or passed '0', 'false'. */
  148. new ExpectedResultBase({
  149. spec: {$or:[0, false]},
  150. expectedResult: false,
  151. }).run();
  152. },
  153. "should return true if given a field path to a truthy value": function testFieldPath(){
  154. /** $or passed a field path. */
  155. new ExpectedResultBase({
  156. spec: {$or:["$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: {$or:[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: {$or:["$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: {$or:[1,"$a"]},
  179. expectedOptimized: {$const:true},
  180. }).run();
  181. },
  182. "should optimize falsy constant and truthy expression": function testConstantNonConstantFalse() {
  183. /** An expression beginning with a single constant is optimized. */
  184. new OptimizeBase({
  185. spec: {$or:[0,"$a"]},
  186. expectedOptimized: {$and:["$a"]},
  187. }).run();
  188. // note: using $and as serialization of ExpressionCoerceToBool rather than ExpressionAnd
  189. },
  190. "should optimize truthy expression and truthy constant": function testNonConstantOne() {
  191. /** An expression with a field path and '1'. */
  192. new OptimizeBase({
  193. spec: {$or:["$a", 1]},
  194. expectedOptimized: {$const:true},
  195. }).run();
  196. },
  197. "should optimize truthy expression and falsy constant": function testNonConstantZero() {
  198. /** An expression with a field path and '0'. */
  199. new OptimizeBase({
  200. spec: {$or:["$a", 0]},
  201. expectedOptimized: {$and:["$a"]},
  202. }).run();
  203. },
  204. "should optimize truthy expression, falsy expression, and truthy constant": function testNonConstantNonConstantOne() {
  205. /** An expression with two field paths and '1'. */
  206. new OptimizeBase({
  207. spec: {$or:["$a","$b",1]},
  208. expectedOptimized: {$const:true},
  209. }).run();
  210. },
  211. "should optimize truthy expression, falsy expression, and falsy constant": function testNonConstantNonConstantZero() {
  212. /** An expression with two field paths and '0'. */
  213. new OptimizeBase({
  214. spec: {$or:["$a","$b",0]},
  215. expectedOptimized: {$or:["$a", "$b"]},
  216. }).run();
  217. },
  218. "should optimize to true if [0,1,'$a']": function testZeroOneNonConstant() {
  219. /** An expression with '0', '1', and a field path. */
  220. new OptimizeBase({
  221. spec: {$or:[0,1,"$a"]},
  222. expectedOptimized: {$const:true},
  223. }).run();
  224. },
  225. "should optimize to {$and:'$a'} if [0,0,'$a']": function testZeroZeroNonConstant() {
  226. /** An expression with '0', '0', and a field path. */
  227. new OptimizeBase({
  228. spec: {$or:[0,0,"$a"]},
  229. expectedOptimized: {$and:["$a"]},
  230. }).run();
  231. },
  232. "should optimize away nested falsey $or expressions": function testNested() {
  233. /** Nested $or expressions. */
  234. new OptimizeBase({
  235. spec: {$or:[0, {$or:[0]}, "$a", "$b"]},
  236. expectedOptimized: {$or: ["$a", "$b"]},
  237. }).run();
  238. },
  239. "should optimize to tru if nested truthy $or expressions": function testNestedOne() {
  240. /** Nested $or expressions containing a nested value evaluating to false. */
  241. new OptimizeBase({
  242. spec: {$or:[0, {$or:[ {$or:[1]} ]}, "$a", "$b"]},
  243. expectedOptimized: {$const:true},
  244. }).run();
  245. },
  246. },
  247. };