Просмотр исходного кода

EAGLESIX-2651: Strcasecmp: better sync w/ 2.6.5 code and tests

* make implementation and test match original 2.6.5 code more closely (to make things easier going forward)
* fix a couple of small issues with tests along the way
* misc formatting
Kyle P Davis 11 лет назад
Родитель
Сommit
f82a6f3da8

+ 21 - 27
lib/pipeline/expressions/StrcasecmpExpression.js

@@ -7,40 +7,34 @@
  * @namespace mungedb-aggregate.pipeline.expressions
  * @module mungedb-aggregate
  * @constructor
- **/
+ */
 var StrcasecmpExpression = module.exports = function StrcasecmpExpression() {
+	if (arguments.length !== 0) throw new Error(klass.name + ": no args expected");
 	base.call(this);
-}, klass = StrcasecmpExpression,
-	FixedArityExpression = require("./FixedArityExpressionT")(klass, 2),
-	base = FixedArityExpression,
-	proto = klass.prototype = Object.create(base.prototype, {
-		constructor: {
-			value: klass
-		}
-	});
+}, klass = StrcasecmpExpression, base = require("./FixedArityExpressionT")(StrcasecmpExpression, 2), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
-// DEPENDENCIES
 var Value = require("../Value"),
-	NaryExpression = require("./NaryExpression"),
 	Expression = require("./Expression");
 
-// PROTOTYPE MEMBERS
-proto.getOpName = function getOpName() {
-	return "$strcasecmp";
-};
-
-/**
- * Takes in two strings. Returns a number. $strcasecmp is positive if the first string is “greater than” the second and negative if the first string is “less than” the second. $strcasecmp returns 0 if the strings are identical.
- * @method evaluate
- **/
 proto.evaluateInternal = function evaluateInternal(vars) {
-	var val1 = this.operands[0].evaluateInternal(vars),
-		val2 = this.operands[1].evaluateInternal(vars),
-		str1 = Value.coerceToString(val1).toUpperCase(),
-		str2 = Value.coerceToString(val2).toUpperCase(),
-		cmp = Value.compare(str1, str2);
-	return cmp;
+	var string1 = this.operands[0].evaluateInternal(vars),
+		string2 = this.operands[1].evaluateInternal(vars);
+
+	var str1 = Value.coerceToString(string1).toUpperCase(),
+		str2 = Value.coerceToString(string2).toUpperCase(),
+		result = Value.compare(str1, str2);
+
+	if (result === 0) {
+	return 0;
+	} else if (result > 0) {
+		return 1;
+	} else {
+		return -1
+	}
 };
 
-/** Register Expression */
 Expression.registerExpression("$strcasecmp", base.parse);
+
+proto.getOpName = function getOpName() {
+	return "$strcasecmp";
+};

+ 87 - 72
test/lib/pipeline/expressions/StrcasecmpExpression.js

@@ -8,87 +8,102 @@ var assert = require("assert"),
 	constify = utils.constify,
 	expressionToJson = utils.expressionToJson;
 
-module.exports = {
+var TestBase = function TestBase(overrides) {
+		//NOTE: DEVIATION FROM MONGO: using this base class to make things easier to initialize
+		for (var key in overrides)
+			this[key] = overrides[key];
+	},
+	ExpectedResultBase = (function() {
+		var klass = function ExpectedResultBase() {
+			base.apply(this, arguments);
+		}, base = TestBase, proto = klass.prototype = Object.create(base.prototype);
+		proto.run = function(){
+			this.assertResult(this.expectedResult, this.spec());
+			this.assertResult(-this.expectedResult, this.reverseSpec());
+		};
+		proto.spec = function() { return {$strcasecmp:[this.a, this.b]}; };
+		proto.reverseSpec = function() { return {$strcasecmp:[this.b, this.a]}; };
+		proto.assertResult = function(expectedResult, spec) {
+			var specElement = spec,
+				idGenerator = new VariablesIdGenerator(),
+				vps = new VariablesParseState(idGenerator),
+				expression = Expression.parseOperand(specElement, vps);
+			assert.deepEqual(constify(spec), expressionToJson(expression));
+			assert.equal(expectedResult, expression.evaluate({}));
+		};
+		return klass;
+	})();
+
+// Mocha one-liner to make these tests self-hosted
+if(!module.parent)return(require.cache[__filename]=null,(new(require("mocha"))({ui:"exports",reporter:"spec",grep:process.env.TEST_GREP})).addFile(__filename).run(process.exit));
+
+exports.StrcasecmpExpression = {
+
+	"constructor()": {
+
+		"should construct instance": function() {
+			assert(new StrcasecmpExpression() instanceof StrcasecmpExpression);
+			assert(new StrcasecmpExpression() instanceof Expression);
+		},
 
-	"StrcasecmpExpression": {
+		"should error if given args": function() {
+			assert.throws(function() {
+				new StrcasecmpExpression("bad stuff");
+			});
+		},
 
-		"constructor()": {
+	},
 
-			"should not throw Error when constructing without args": function testConstructor(){
-				assert.doesNotThrow(function(){
-					new StrcasecmpExpression();
-				});
-			}
+	"#getOpName()": {
 
+		"should return the correct op name; $strcasecmp": function(){
+			assert.equal(new StrcasecmpExpression().getOpName(), "$strcasecmp");
 		},
 
-		"#getOpName()": {
+	},
 
-			"should return the correct op name; $strcasecmp": function testOpName(){
-				assert.equal(new StrcasecmpExpression().getOpName(), "$strcasecmp");
-			}
+	"#evaluate()": {
 
+		"should return 'ab' == 'AB' (w/ null begin)": function NullBegin() {
+			new ExpectedResultBase({
+				a: "\0ab",
+				b: "\0AB",
+				expectedResult: 0,
+			}).run();
 		},
 
-		"evaluate": {
-
-			"before": function before() {
-				this.run = function run() {
-					this.assertResult(this.expectedResult, this.spec());
-					this.assertResult(-this.expectedResult, this.reverseSpec());
-				};
-				this.a = undefined;
-				this.b = undefined;
-				this.expectedResult = undefined;
-				this.spec = function spec() {return {$strcasecmp:[this.a, this.b]}; };
-				this.reverseSpec = function reverseSpec() {return {$strcasecmp:[this.b, this.a]}; };
-				this.assertResult = function assertResult(expectedResult, spec) {
-					var idGenerator = new VariablesIdGenerator(),
-						vps = new VariablesParseState(idGenerator),
-						expression = Expression.parseOperand(spec, vps);
-					assert.deepEqual(constify(spec), expressionToJson(expression));
-					assert.equal(expectedResult, expression.evaluate({}));
-				};
-			},
-
-			"NullBegin": function NullBegin() {
-				this.a = "\0ab";
-				this.b = "\0AB";
-				this.expectedResult = 0;
-				this.run();
-			},
-
-			"NullEnd": function NullEnd() {
-				this.a = "ab\0";
-				this.b = "AB\0";
-				this.expectedResult = 0;
-				this.run();
-			},
-
-			"NullMiddleLt": function NullMiddleLt() {
-				this.a = "a\0a";
-				this.b = "A\0B";
-				this.expectedResult = -1;
-				this.run();
-			},
-
-			"NullMiddleEq": function NullMiddleEq() {
-				this.a = "a\0b";
-				this.b = "a\0B";
-				this.expectedResult = 0;
-				this.run();
-			},
-
-			"NullMiddleGt": function NullMiddleGt() {
-				this.a = "a\0c";
-				this.b = "a\0B";
-				this.expectedResult = 1;
-				this.run();
-			}
-		}
-
-	}
+		"should return 'ab' == 'aB' (w/ null end)": function NullEnd() {
+			new ExpectedResultBase({
+				a: "ab\0",
+				b: "aB\0",
+				expectedResult: 0,
+			}).run();
+		},
 
-};
+		"should return 'aa' < 'aB' (w/ null middle)": function NullMiddleLt() {
+			new ExpectedResultBase({
+				a: "a\0a",
+				b: "a\0B",
+				expectedResult: -1,
+			}).run();
+		},
 
-if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);
+		"should return 'ab' == 'aB' (w/ null middle)": function NullMiddleEq() {
+			new ExpectedResultBase({
+				a: "a\0b",
+				b: "a\0B",
+				expectedResult: 0,
+			}).run();
+		},
+
+		"should return 'ac' > 'aB' (w/ null middle)": function NullMiddleGt() {
+			new ExpectedResultBase({
+				a: "a\0c",
+				b: "a\0B",
+				expectedResult: 1,
+			}).run();
+		},
+
+	},
+
+};