|
| 1 | +// SERVER-32930: Basic integration tests for trigonometric aggregation expressions. |
| 2 | + |
| 3 | +(function() { |
| 4 | + "use strict"; |
| 5 | + // For assertErrorCode. |
| 6 | + load("jstests/aggregation/extras/utils.js"); |
| 7 | + |
| 8 | + const coll = db.expression_trigonometric; |
| 9 | + coll.drop(); |
| 10 | + // We need at least one document in the collection in order to test expressions, add it here. |
| 11 | + assert.commandWorked(coll.insert({})); |
| 12 | + |
| 13 | + // Helper for testing that op returns expResult. |
| 14 | + function testOp(op, expResult) { |
| 15 | + const pipeline = [{$project: {_id: 0, result: op}}]; |
| 16 | + assert.eq(coll.aggregate(pipeline).toArray(), [{result: expResult}]); |
| 17 | + } |
| 18 | + |
| 19 | + // Helper for testing that the aggregation expression 'op' returns expResult, approximately, |
| 20 | + // since NumberDecimal has so many representations for a given number (0 versus 0e-40 for |
| 21 | + // instance). |
| 22 | + function testOpApprox(op, expResult) { |
| 23 | + const pipeline = [{$project: {_id: 0, result: {$abs: {$subtract: [op, expResult]}}}}]; |
| 24 | + assert.lt(coll.aggregate(pipeline).toArray(), [{result: NumberDecimal("0.00000005")}]); |
| 25 | + } |
| 26 | + |
| 27 | + // Simple successful int input. |
| 28 | + testOp({$acos: NumberInt(1)}, 0); |
| 29 | + testOp({$acosh: NumberInt(1)}, 0); |
| 30 | + testOp({$asin: NumberInt(0)}, 0); |
| 31 | + testOp({$asinh: NumberInt(0)}, 0); |
| 32 | + testOp({$atan: NumberInt(0)}, 0); |
| 33 | + testOp({$atan2: [NumberInt(0), NumberInt(1)]}, 0); |
| 34 | + testOp({$atan2: [NumberInt(0), NumberInt(0)]}, 0); |
| 35 | + testOp({$atanh: NumberInt(0)}, 0); |
| 36 | + testOp({$cos: NumberInt(0)}, 1); |
| 37 | + testOp({$cosh: NumberInt(0)}, 1); |
| 38 | + testOp({$sin: NumberInt(0)}, 0); |
| 39 | + testOp({$sinh: NumberInt(0)}, 0); |
| 40 | + testOp({$tan: NumberInt(0)}, 0); |
| 41 | + testOp({$tanh: NumberInt(0)}, 0); |
| 42 | + testOp({$degreesToRadians: NumberInt(0)}, 0); |
| 43 | + testOp({$radiansToDegrees: NumberInt(0)}, 0); |
| 44 | + |
| 45 | + // Simple successful long input. |
| 46 | + testOp({$acos: NumberLong(1)}, 0); |
| 47 | + testOp({$acosh: NumberLong(1)}, 0); |
| 48 | + testOp({$asin: NumberLong(0)}, 0); |
| 49 | + testOp({$asinh: NumberLong(0)}, 0); |
| 50 | + testOp({$atan: NumberLong(0)}, 0); |
| 51 | + testOp({$atan2: [NumberLong(0), NumberLong(1)]}, 0); |
| 52 | + testOp({$atan2: [NumberLong(0), NumberLong(0)]}, 0); |
| 53 | + testOp({$atanh: NumberLong(0)}, 0); |
| 54 | + testOp({$cos: NumberLong(0)}, 1); |
| 55 | + testOp({$cosh: NumberLong(0)}, 1); |
| 56 | + testOp({$sin: NumberLong(0)}, 0); |
| 57 | + testOp({$sinh: NumberLong(0)}, 0); |
| 58 | + testOp({$tan: NumberLong(0)}, 0); |
| 59 | + testOp({$tanh: NumberLong(0)}, 0); |
| 60 | + testOp({$degreesToRadians: NumberLong(0)}, 0); |
| 61 | + testOp({$radiansToDegrees: NumberLong(0)}, 0); |
| 62 | + |
| 63 | + // Simple successful double input. |
| 64 | + testOp({$acos: 1}, 0); |
| 65 | + testOp({$acosh: 1}, 0); |
| 66 | + testOp({$asin: 0}, 0); |
| 67 | + testOp({$asinh: 0}, 0); |
| 68 | + testOp({$atan: 0}, 0); |
| 69 | + testOp({$atan2: [0, 1]}, 0); |
| 70 | + testOp({$atan2: [0, 0]}, 0); |
| 71 | + testOp({$atanh: 0}, 0); |
| 72 | + testOp({$cos: 0}, 1); |
| 73 | + testOp({$cosh: 0}, 1); |
| 74 | + testOp({$sin: 0}, 0); |
| 75 | + testOp({$sinh: 0}, 0); |
| 76 | + testOp({$tan: 0}, 0); |
| 77 | + testOp({$tanh: 0}, 0); |
| 78 | + testOp({$degreesToRadians: 0}, 0); |
| 79 | + testOp({$radiansToDegrees: 0}, 0); |
| 80 | + |
| 81 | + // Simple successful decimal input. |
| 82 | + testOpApprox({$acos: NumberDecimal(1)}, NumberDecimal(0)); |
| 83 | + testOpApprox({$acosh: NumberDecimal(1)}, NumberDecimal(0)); |
| 84 | + testOpApprox({$asin: NumberDecimal(0)}, NumberDecimal(0)); |
| 85 | + testOpApprox({$asinh: NumberDecimal(0)}, NumberDecimal(0)); |
| 86 | + testOpApprox({$atan: NumberDecimal(0)}, NumberDecimal(0)); |
| 87 | + testOpApprox({$atan2: [NumberDecimal(0), 1]}, NumberDecimal(0)); |
| 88 | + testOpApprox({$atan2: [NumberDecimal(0), 0]}, NumberDecimal(0)); |
| 89 | + testOpApprox({$atanh: NumberDecimal(0)}, NumberDecimal(0)); |
| 90 | + testOpApprox({$cos: NumberDecimal(0)}, NumberDecimal(1)); |
| 91 | + testOpApprox({$cosh: NumberDecimal(0)}, NumberDecimal(1)); |
| 92 | + testOpApprox({$sin: NumberDecimal(0)}, NumberDecimal(0)); |
| 93 | + testOpApprox({$sinh: NumberDecimal(0)}, NumberDecimal(0)); |
| 94 | + testOpApprox({$tan: NumberDecimal(0)}, NumberDecimal(0)); |
| 95 | + testOpApprox({$tanh: NumberDecimal(0)}, NumberDecimal(0)); |
| 96 | + testOpApprox({$degreesToRadians: NumberDecimal(0)}, NumberDecimal(0)); |
| 97 | + testOpApprox({$radiansToDegrees: NumberDecimal(0)}, NumberDecimal(0)); |
| 98 | + |
| 99 | + // Infinity input produces out of bounds error. |
| 100 | + assertErrorCode(coll, [{$project: {a: {$acos: -Infinity}}}], 50989); |
| 101 | + assertErrorCode(coll, [{$project: {a: {$acos: NumberDecimal('-Infinity')}}}], 50989); |
| 102 | + assertErrorCode(coll, [{$project: {a: {$acos: Infinity}}}], 50989); |
| 103 | + assertErrorCode(coll, [{$project: {a: {$acos: NumberDecimal('Infinity')}}}], 50989); |
| 104 | + |
| 105 | + assertErrorCode(coll, [{$project: {a: {$acosh: -Infinity}}}], 50989); |
| 106 | + assertErrorCode(coll, [{$project: {a: {$acosh: NumberDecimal('-Infinity')}}}], 50989); |
| 107 | + |
| 108 | + assertErrorCode(coll, [{$project: {a: {$asin: -Infinity}}}], 50989); |
| 109 | + assertErrorCode(coll, [{$project: {a: {$asin: NumberDecimal('-Infinity')}}}], 50989); |
| 110 | + assertErrorCode(coll, [{$project: {a: {$asin: Infinity}}}], 50989); |
| 111 | + assertErrorCode(coll, [{$project: {a: {$asin: NumberDecimal('Infinity')}}}], 50989); |
| 112 | + |
| 113 | + assertErrorCode(coll, [{$project: {a: {$atanh: -Infinity}}}], 50989); |
| 114 | + assertErrorCode(coll, [{$project: {a: {$atanh: NumberDecimal('-Infinity')}}}], 50989); |
| 115 | + assertErrorCode(coll, [{$project: {a: {$atanh: Infinity}}}], 50989); |
| 116 | + assertErrorCode(coll, [{$project: {a: {$atanh: NumberDecimal('Infinity')}}}], 50989); |
| 117 | + |
| 118 | + assertErrorCode(coll, [{$project: {a: {$cos: -Infinity}}}], 50989); |
| 119 | + assertErrorCode(coll, [{$project: {a: {$cos: NumberDecimal('-Infinity')}}}], 50989); |
| 120 | + assertErrorCode(coll, [{$project: {a: {$cos: Infinity}}}], 50989); |
| 121 | + assertErrorCode(coll, [{$project: {a: {$cos: NumberDecimal('Infinity')}}}], 50989); |
| 122 | + |
| 123 | + assertErrorCode(coll, [{$project: {a: {$sin: -Infinity}}}], 50989); |
| 124 | + assertErrorCode(coll, [{$project: {a: {$sin: NumberDecimal('-Infinity')}}}], 50989); |
| 125 | + assertErrorCode(coll, [{$project: {a: {$sin: Infinity}}}], 50989); |
| 126 | + assertErrorCode(coll, [{$project: {a: {$sin: NumberDecimal('Infinity')}}}], 50989); |
| 127 | + |
| 128 | + assertErrorCode(coll, [{$project: {a: {$tan: -Infinity}}}], 50989); |
| 129 | + assertErrorCode(coll, [{$project: {a: {$tan: NumberDecimal('-Infinity')}}}], 50989); |
| 130 | + assertErrorCode(coll, [{$project: {a: {$tan: Infinity}}}], 50989); |
| 131 | + assertErrorCode(coll, [{$project: {a: {$tan: NumberDecimal('Infinity')}}}], 50989); |
| 132 | + |
| 133 | + // Infinity input produces Infinity as output. |
| 134 | + testOp({$acosh: NumberDecimal('Infinity')}, NumberDecimal('Infinity')); |
| 135 | + testOp({$acosh: Infinity}, Infinity); |
| 136 | + |
| 137 | + testOp({$asinh: NumberDecimal('Infinity')}, NumberDecimal('Infinity')); |
| 138 | + testOp({$asinh: NumberDecimal('-Infinity')}, NumberDecimal('-Infinity')); |
| 139 | + testOp({$asinh: Infinity}, Infinity); |
| 140 | + testOp({$asinh: -Infinity}, -Infinity); |
| 141 | + testOp({$cosh: NumberDecimal('Infinity')}, NumberDecimal('Infinity')); |
| 142 | + testOp({$cosh: NumberDecimal('-Infinity')}, NumberDecimal('Infinity')); |
| 143 | + testOp({$cosh: Infinity}, Infinity); |
| 144 | + testOp({$cosh: -Infinity}, Infinity); |
| 145 | + testOp({$sinh: NumberDecimal('Infinity')}, NumberDecimal('Infinity')); |
| 146 | + testOp({$sinh: NumberDecimal('-Infinity')}, NumberDecimal('-Infinity')); |
| 147 | + testOp({$sinh: Infinity}, Infinity); |
| 148 | + testOp({$sinh: -Infinity}, -Infinity); |
| 149 | + |
| 150 | + // Infinity produces finite output (due to asymptotic bounds). |
| 151 | + testOpApprox({$atan: NumberDecimal('Infinity')}, NumberDecimal(Math.PI / 2)); |
| 152 | + testOpApprox({$atan: NumberDecimal('-Infinity')}, NumberDecimal(Math.Pi / 2)); |
| 153 | + testOpApprox({$atan: Infinity}, Math.PI / 2); |
| 154 | + testOpApprox({$atan: -Infinity}, -Math.PI / 2); |
| 155 | + |
| 156 | + testOpApprox({$atan2: [NumberDecimal('Infinity'), 0]}, NumberDecimal(Math.PI / 2)); |
| 157 | + testOpApprox({$atan2: [NumberDecimal('-Infinity'), 0]}, NumberDecimal(-Math.PI / 2)); |
| 158 | + testOpApprox({$atan2: [NumberDecimal('-Infinity'), NumberDecimal("Infinity")]}, |
| 159 | + NumberDecimal(-Math.PI / 4)); |
| 160 | + testOpApprox({$atan2: [NumberDecimal('-Infinity'), NumberDecimal("-Infinity")]}, |
| 161 | + NumberDecimal(-3 * Math.PI / 4)); |
| 162 | + testOpApprox({$atan2: [NumberDecimal('0'), NumberDecimal("-Infinity")]}, |
| 163 | + NumberDecimal(Math.PI)); |
| 164 | + testOpApprox({$atan2: [NumberDecimal('0'), NumberDecimal("Infinity")]}, NumberDecimal(0)); |
| 165 | + |
| 166 | + testOp({$tanh: NumberDecimal('Infinity')}, NumberDecimal('1')); |
| 167 | + testOp({$tanh: NumberDecimal('-Infinity')}, NumberDecimal('-1')); |
| 168 | + |
| 169 | + // Finite input produces infinite outputs. |
| 170 | + testOp({$atanh: NumberDecimal(1)}, NumberDecimal('Infinity')); |
| 171 | + testOp({$atanh: NumberDecimal(-1)}, NumberDecimal('-Infinity')); |
| 172 | + testOp({$atanh: 1}, Infinity); |
| 173 | + testOp({$atanh: -1}, -Infinity); |
| 174 | + |
| 175 | + testOp({$tanh: Infinity}, 1); |
| 176 | + testOp({$tanh: -Infinity}, -1); |
| 177 | + |
| 178 | + // Int argument out of bounds. |
| 179 | + assertErrorCode(coll, [{$project: {a: {$acos: NumberInt(-2)}}}], 50989); |
| 180 | + assertErrorCode(coll, [{$project: {a: {$acos: NumberInt(2)}}}], 50989); |
| 181 | + assertErrorCode(coll, [{$project: {a: {$asin: NumberInt(-2)}}}], 50989); |
| 182 | + assertErrorCode(coll, [{$project: {a: {$asin: NumberInt(2)}}}], 50989); |
| 183 | + assertErrorCode(coll, [{$project: {a: {$acosh: NumberInt(0)}}}], 50989); |
| 184 | + assertErrorCode(coll, [{$project: {a: {$atanh: NumberInt(2)}}}], 50989); |
| 185 | + assertErrorCode(coll, [{$project: {a: {$atanh: NumberInt(-2)}}}], 50989); |
| 186 | + |
| 187 | + // Long argument out of bounds. |
| 188 | + assertErrorCode(coll, [{$project: {a: {$acos: NumberLong(-2)}}}], 50989); |
| 189 | + assertErrorCode(coll, [{$project: {a: {$acos: NumberLong(2)}}}], 50989); |
| 190 | + assertErrorCode(coll, [{$project: {a: {$asin: NumberLong(-2)}}}], 50989); |
| 191 | + assertErrorCode(coll, [{$project: {a: {$asin: NumberLong(2)}}}], 50989); |
| 192 | + assertErrorCode(coll, [{$project: {a: {$acosh: NumberLong(0)}}}], 50989); |
| 193 | + assertErrorCode(coll, [{$project: {a: {$atanh: NumberLong(2)}}}], 50989); |
| 194 | + assertErrorCode(coll, [{$project: {a: {$atanh: NumberLong(-2)}}}], 50989); |
| 195 | + |
| 196 | + // Double argument out of bounds. |
| 197 | + assertErrorCode(coll, [{$project: {a: {$acos: -1.1}}}], 50989); |
| 198 | + assertErrorCode(coll, [{$project: {a: {$acos: 1.1}}}], 50989); |
| 199 | + assertErrorCode(coll, [{$project: {a: {$asin: -1.1}}}], 50989); |
| 200 | + assertErrorCode(coll, [{$project: {a: {$asin: 1.1}}}], 50989); |
| 201 | + assertErrorCode(coll, [{$project: {a: {$acosh: 0.9}}}], 50989); |
| 202 | + assertErrorCode(coll, [{$project: {a: {$atanh: -1.00001}}}], 50989); |
| 203 | + assertErrorCode(coll, [{$project: {a: {$atanh: 1.00001}}}], 50989); |
| 204 | + |
| 205 | + // Decimal argument out of bounds. |
| 206 | + assertErrorCode(coll, [{$project: {a: {$acos: NumberDecimal(-1.1)}}}], 50989); |
| 207 | + assertErrorCode(coll, [{$project: {a: {$acos: NumberDecimal(1.1)}}}], 50989); |
| 208 | + assertErrorCode(coll, [{$project: {a: {$asin: NumberDecimal(-1.1)}}}], 50989); |
| 209 | + assertErrorCode(coll, [{$project: {a: {$asin: NumberDecimal(1.1)}}}], 50989); |
| 210 | + assertErrorCode(coll, [{$project: {a: {$acosh: NumberDecimal(0.9)}}}], 50989); |
| 211 | + assertErrorCode(coll, [{$project: {a: {$atanh: NumberDecimal(-1.00001)}}}], 50989); |
| 212 | + assertErrorCode(coll, [{$project: {a: {$atanh: NumberDecimal(1.000001)}}}], 50989); |
| 213 | + |
| 214 | + // Check NaN is preserved. |
| 215 | + ["$acos", "$asin", "$atan", "$cos", "$sin", "$tan"].forEach(op => { |
| 216 | + testOp({[op]: NaN}, NaN); |
| 217 | + testOp({[op]: NumberDecimal(NaN)}, NumberDecimal(NaN)); |
| 218 | + // Check the hyperbolic version of each function. |
| 219 | + testOp({[op + 'h']: NaN}, NaN); |
| 220 | + testOp({[op + 'h']: NumberDecimal(NaN)}, NumberDecimal(NaN)); |
| 221 | + }); |
| 222 | + |
| 223 | + ["$radiansToDegrees", "$degreesToRadians"].forEach(op => { |
| 224 | + testOp({[op]: NaN}, NaN); |
| 225 | + testOp({[op]: NumberDecimal(NaN)}, NumberDecimal(NaN)); |
| 226 | + testOp({[op]: -Infinity}, -Infinity); |
| 227 | + testOp({[op]: NumberDecimal(-Infinity)}, NumberDecimal(-Infinity)); |
| 228 | + testOp({[op]: Infinity}, Infinity); |
| 229 | + testOp({[op]: NumberDecimal(Infinity)}, NumberDecimal(Infinity)); |
| 230 | + }); |
| 231 | + |
| 232 | + testOp({$atan2: [NumberDecimal('NaN'), NumberDecimal('NaN')]}, NumberDecimal('NaN')); |
| 233 | + testOp({$atan2: [NumberDecimal('NaN'), NumberDecimal('0')]}, NumberDecimal('NaN')); |
| 234 | + testOp({$atan2: [NumberDecimal('0'), NumberDecimal('NaN')]}, NumberDecimal('NaN')); |
| 235 | + |
| 236 | + // Non-numeric input. |
| 237 | + assertErrorCode(coll, [{$project: {a: {$acos: "string"}}}], 28765); |
| 238 | + assertErrorCode(coll, [{$project: {a: {$acosh: "string"}}}], 28765); |
| 239 | + assertErrorCode(coll, [{$project: {a: {$asin: "string"}}}], 28765); |
| 240 | + assertErrorCode(coll, [{$project: {a: {$asinh: "string"}}}], 28765); |
| 241 | + assertErrorCode(coll, [{$project: {a: {$atan: "string"}}}], 28765); |
| 242 | + assertErrorCode(coll, [{$project: {a: {$atan2: ["string", "string"]}}}], 51044); |
| 243 | + assertErrorCode(coll, [{$project: {a: {$atan2: ["string", 0.0]}}}], 51044); |
| 244 | + assertErrorCode(coll, [{$project: {a: {$atan2: [0.0, "string"]}}}], 51045); |
| 245 | + assertErrorCode(coll, [{$project: {a: {$atanh: "string"}}}], 28765); |
| 246 | + assertErrorCode(coll, [{$project: {a: {$cos: "string"}}}], 28765); |
| 247 | + assertErrorCode(coll, [{$project: {a: {$cosh: "string"}}}], 28765); |
| 248 | + assertErrorCode(coll, [{$project: {a: {$sin: "string"}}}], 28765); |
| 249 | + assertErrorCode(coll, [{$project: {a: {$sinh: "string"}}}], 28765); |
| 250 | + assertErrorCode(coll, [{$project: {a: {$tan: "string"}}}], 28765); |
| 251 | + assertErrorCode(coll, [{$project: {a: {$tanh: "string"}}}], 28765); |
| 252 | + assertErrorCode(coll, [{$project: {a: {$degreesToRadians: "string"}}}], 28765); |
| 253 | + assertErrorCode(coll, [{$project: {a: {$radiansToDegrees: "string"}}}], 28765); |
| 254 | +}()); |
0 commit comments