/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.data.mongodb.core.Person;
/**
* Unit tests for {@link SpelExpressionTransformer}.
*
* @see DATAMONGO-774
* @author Thomas Darimont
* @author Oliver Gierke
*/
public class SpelExpressionTransformerUnitTests {
SpelExpressionTransformer transformer = new SpelExpressionTransformer();
Data data;
@Before
public void setup() {
this.data = new Data();
this.data.primitiveLongValue = 42;
this.data.primitiveDoubleValue = 1.2345;
this.data.doubleValue = 23.0;
this.data.item = new DataItem();
this.data.item.primitiveIntValue = 21;
}
@Test
public void shouldRenderConstantExpression() {
assertThat(transform("1"), is("1"));
assertThat(transform("-1"), is("-1"));
assertThat(transform("1.0"), is("1.0"));
assertThat(transform("-1.0"), is("-1.0"));
assertThat(transform("null"), is(nullValue()));
}
@Test
public void shouldSupportKnownOperands() {
assertThat(transform("a + b"), is("{ \"$add\" : [ \"$a\" , \"$b\"]}"));
assertThat(transform("a - b"), is("{ \"$subtract\" : [ \"$a\" , \"$b\"]}"));
assertThat(transform("a * b"), is("{ \"$multiply\" : [ \"$a\" , \"$b\"]}"));
assertThat(transform("a / b"), is("{ \"$divide\" : [ \"$a\" , \"$b\"]}"));
assertThat(transform("a % b"), is("{ \"$mod\" : [ \"$a\" , \"$b\"]}"));
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionOnUnknownOperand() {
transform("a ^ 1");
}
@Test
public void shouldRenderSumExpression() {
assertThat(transform("a + 1"), is("{ \"$add\" : [ \"$a\" , 1]}"));
}
@Test
public void shouldRenderFormula() {
assertThat(
transform("(netPrice + surCharge) * taxrate + 42"),
is("{ \"$add\" : [ { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\"]} , 42]}"));
}
@Test
public void shouldRenderFormulaInCurlyBrackets() {
assertThat(
transform("{(netPrice + surCharge) * taxrate + 42}"),
is("{ \"$add\" : [ { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\"]} , 42]}"));
}
@Test
public void shouldRenderFieldReference() {
assertThat(transform("foo"), is("$foo"));
assertThat(transform("$foo"), is("$foo"));
}
@Test
public void shouldRenderNestedFieldReference() {
assertThat(transform("foo.bar"), is("$foo.bar"));
assertThat(transform("$foo.bar"), is("$foo.bar"));
}
@Test
@Ignore
public void shouldRenderNestedIndexedFieldReference() {
// TODO add support for rendering nested indexed field references
assertThat(transform("foo[3].bar"), is("$foo[3].bar"));
}
@Test
public void shouldRenderConsecutiveOperation() {
assertThat(transform("1 + 1 + 1"), is("{ \"$add\" : [ 1 , 1 , 1]}"));
}
@Test
public void shouldRenderComplexExpression0() {
assertThat(transform("-(1 + q)"), is("{ \"$multiply\" : [ -1 , { \"$add\" : [ 1 , \"$q\"]}]}"));
}
@Test
public void shouldRenderComplexExpression1() {
assertThat(transform("1 + (q + 1) / (q - 1)"),
is("{ \"$add\" : [ 1 , { \"$divide\" : [ { \"$add\" : [ \"$q\" , 1]} , { \"$subtract\" : [ \"$q\" , 1]}]}]}"));
}
@Test
public void shouldRenderComplexExpression2() {
assertThat(
transform("(q + 1 + 4 - 5) / (q + 1 + 3 + 4)"),
is("{ \"$divide\" : [ { \"$subtract\" : [ { \"$add\" : [ \"$q\" , 1 , 4]} , 5]} , { \"$add\" : [ \"$q\" , 1 , 3 , 4]}]}"));
}
@Test
public void shouldRenderBinaryExpressionWithMixedSignsCorrectly() {
assertThat(transform("-4 + 1"), is("{ \"$add\" : [ -4 , 1]}"));
assertThat(transform("1 + -4"), is("{ \"$add\" : [ 1 , -4]}"));
}
@Test
public void shouldRenderConsecutiveOperationsInComplexExpression() {
assertThat(transform("1 + 1 + (1 + 1 + 1) / q"),
is("{ \"$add\" : [ 1 , 1 , { \"$divide\" : [ { \"$add\" : [ 1 , 1 , 1]} , \"$q\"]}]}"));
}
@Test
public void shouldRenderParameterExpressionResults() {
assertThat(transform("[0] + [1] + [2]", 1, 2, 3), is("{ \"$add\" : [ 1 , 2 , 3]}"));
}
@Test
public void shouldRenderNestedParameterExpressionResults() {
assertThat(transform("[0].primitiveLongValue + [0].primitiveDoubleValue + [0].doubleValue.longValue()", data),
is("{ \"$add\" : [ 42 , 1.2345 , 23]}"));
}
@Test
public void shouldRenderNestedParameterExpressionResultsInNestedExpressions() {
assertThat(
transform("((1 + [0].primitiveLongValue) + [0].primitiveDoubleValue) * [0].doubleValue.longValue()", data),
is("{ \"$multiply\" : [ { \"$add\" : [ 1 , 42 , 1.2345]} , 23]}"));
}
/**
* @see DATAMONGO-840
*/
@Test
public void shouldRenderCompoundExpressionsWithIndexerAndFieldReference() {
Person person = new Person();
person.setAge(10);
assertThat(transform("[0].age + a.c", person), is("{ \"$add\" : [ 10 , \"$a.c\"]}"));
}
/**
* @see DATAMONGO-840
*/
@Test
public void shouldRenderCompoundExpressionsWithOnlyFieldReferences() {
assertThat(transform("a.b + a.c"), is("{ \"$add\" : [ \"$a.b\" , \"$a.c\"]}"));
}
@Test
public void shouldRenderStringFunctions() {
assertThat(transform("concat(a, b)"), is("{ \"$concat\" : [ \"$a\" , \"$b\"]}"));
assertThat(transform("substr(a, 1, 2)"), is("{ \"$substr\" : [ \"$a\" , 1 , 2]}"));
assertThat(transform("strcasecmp(a, b)"), is("{ \"$strcasecmp\" : [ \"$a\" , \"$b\"]}"));
assertThat(transform("toLower(a)"), is("{ \"$toLower\" : [ \"$a\"]}"));
assertThat(transform("toUpper(a)"), is("{ \"$toUpper\" : [ \"$a\"]}"));
assertThat(transform("toUpper(toLower(a))"), is("{ \"$toUpper\" : [ { \"$toLower\" : [ \"$a\"]}]}"));
}
private String transform(String expression, Object... params) {
Object result = transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
return result == null ? null : result.toString();
}
}