diff --git a/src-test/src/org/openbravo/test/StandaloneTestSuite.java b/src-test/src/org/openbravo/test/StandaloneTestSuite.java index 932a243992a600daa6bd91d33c71445f5be91eac..c0287ba08d0d74ff19527c5642dfefebd6df20fa 100644 --- a/src-test/src/org/openbravo/test/StandaloneTestSuite.java +++ b/src-test/src/org/openbravo/test/StandaloneTestSuite.java @@ -88,6 +88,7 @@ import org.openbravo.test.expression.EvaluationTest; import org.openbravo.test.expression.OBBindingsTest; import org.openbravo.test.generalsetup.enterprise.organization.ADOrgPersistInfoTestSuite; import org.openbravo.test.inventoryStatus.InventoryStatusTest; +import org.openbravo.test.matchers.json.JSONMatchersTest; import org.openbravo.test.materialMgmt.invoiceFromShipment.InvoiceFromShipmentTest; import org.openbravo.test.materialMgmt.iscompletelyinvoicedshipment.IsCompletelyInvoicedShipment; import org.openbravo.test.model.ClassLoaderTest; @@ -205,6 +206,9 @@ import org.openbravo.userinterface.selectors.test.ExpressionsTest; OBBindingsTest.class, // ExpressionsTest.class, + // matchers + JSONMatchersTest.class, + // model RuntimeModelTest.class, // OneToManyTest.class, // diff --git a/src-test/src/org/openbravo/test/matchers/json/HasItems.java b/src-test/src/org/openbravo/test/matchers/json/HasItems.java new file mode 100644 index 0000000000000000000000000000000000000000..847841613e006c697e661ee6f73f1c20fc86670b --- /dev/null +++ b/src-test/src/org/openbravo/test/matchers/json/HasItems.java @@ -0,0 +1,54 @@ +/* + ************************************************************************* + * The contents of this file are subject to the Openbravo Public License + * Version 1.1 (the "License"), being the Mozilla Public License + * Version 1.1 with a permitted attribution clause; you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at http://www.openbravo.com/legal/license.html + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * The Original Code is Openbravo ERP. + * The Initial Developer of the Original Code is Openbravo SLU + * All portions are Copyright (C) 2022 Openbravo SLU + * All Rights Reserved. + * Contributor(s): ______________________________________. + ************************************************************************ + */ +package org.openbravo.test.matchers.json; + +import java.util.List; +import java.util.stream.Collectors; + +import org.codehaus.jettison.json.JSONArray; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; + +/** + * A matcher to check whether a JSONArray contains all the objects of a list + */ +class HasItems extends TypeSafeMatcher { + + private List items; + + HasItems(List items) { + this.items = items; + } + + @Override + protected boolean matchesSafely(JSONArray item) { + return JSONComparator.arrayContains(item, items); + } + + @Override + public void describeTo(Description description) { + String itemsDesc = items.stream().map(Object::toString).collect(Collectors.joining(",")); + description.appendText("has items <[").appendText(itemsDesc).appendText("]>"); + } + + @Override + public void describeMismatchSafely(JSONArray item, Description description) { + description.appendText("missing items in ").appendValue(item); + } +} diff --git a/src-test/src/org/openbravo/test/matchers/json/HasMatchingItems.java b/src-test/src/org/openbravo/test/matchers/json/HasMatchingItems.java new file mode 100644 index 0000000000000000000000000000000000000000..9322191c2a0535e76a2c8bc63ba6dc431dbb13b7 --- /dev/null +++ b/src-test/src/org/openbravo/test/matchers/json/HasMatchingItems.java @@ -0,0 +1,79 @@ +/* + ************************************************************************* + * The contents of this file are subject to the Openbravo Public License + * Version 1.1 (the "License"), being the Mozilla Public License + * Version 1.1 with a permitted attribution clause; you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at http://www.openbravo.com/legal/license.html + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * The Original Code is Openbravo ERP. + * The Initial Developer of the Original Code is Openbravo SLU + * All portions are Copyright (C) 2022 Openbravo SLU + * All Rights Reserved. + * Contributor(s): ______________________________________. + ************************************************************************ + */ +package org.openbravo.test.matchers.json; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.codehaus.jettison.json.JSONArray; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +/** + * A matcher to check whether a JSONArray matches with a given list of matchers + */ +class HasMatchingItems extends TypeSafeMatcher { + + private List> matchers; + private List> nonMatching; + + HasMatchingItems(List> matchers) { + this.matchers = matchers; + } + + @Override + protected boolean matchesSafely(JSONArray item) { + nonMatching = matchers.stream() + .filter(matcher -> !anyMatch(matcher, item)) + .collect(Collectors.toList()); + return nonMatching.isEmpty(); + } + + private boolean anyMatch(Matcher matcher, JSONArray item) { + for (int i = 0; i < item.length(); i++) { + if (matcher.matches(item.opt(i))) { + return true; + } + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("expected items: "); + addToDescription(matchers, description); + } + + @Override + public void describeMismatchSafely(JSONArray item, Description description) { + description.appendText("missing items matching: "); + addToDescription(nonMatching, description); + } + + private void addToDescription(List> m, Description description) { + IntStream.range(0, m.size()).forEach(idx -> { + m.get(idx).describeTo(description); + if (idx < m.size() - 1) { + description.appendText(", "); + } + }); + } +} diff --git a/src-test/src/org/openbravo/test/matchers/json/IsEqualJSONObject.java b/src-test/src/org/openbravo/test/matchers/json/IsEqualJSONObject.java new file mode 100644 index 0000000000000000000000000000000000000000..1fef2e662e7c3ae26d3561246dbb20fccee18386 --- /dev/null +++ b/src-test/src/org/openbravo/test/matchers/json/IsEqualJSONObject.java @@ -0,0 +1,51 @@ +/* + ************************************************************************* + * The contents of this file are subject to the Openbravo Public License + * Version 1.1 (the "License"), being the Mozilla Public License + * Version 1.1 with a permitted attribution clause; you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at http://www.openbravo.com/legal/license.html + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * The Original Code is Openbravo ERP. + * The Initial Developer of the Original Code is Openbravo SLU + * All portions are Copyright (C) 2022 Openbravo SLU + * All Rights Reserved. + * Contributor(s): ______________________________________. + ************************************************************************ + */ +package org.openbravo.test.matchers.json; + +import org.codehaus.jettison.json.JSONObject; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; + +/** + * A matcher to check whether two JSONObjects are equal. Two JSONObjects will be considered equal if + * both have exactly the same number of properties with the same value for each of them. + */ +class IsEqualJSONObject extends TypeSafeMatcher { + + private JSONObject expected; + + IsEqualJSONObject(JSONObject expected) { + this.expected = expected; + } + + @Override + protected boolean matchesSafely(JSONObject item) { + return JSONComparator.objectsAreEquivalent(item, expected); + } + + @Override + public void describeTo(Description description) { + description.appendText("equal to ").appendValue(expected); + } + + @Override + public void describeMismatchSafely(JSONObject item, Description description) { + description.appendText("was ").appendValue(item); + } +} diff --git a/src-test/src/org/openbravo/test/matchers/json/IsMatchingJSONObject.java b/src-test/src/org/openbravo/test/matchers/json/IsMatchingJSONObject.java new file mode 100644 index 0000000000000000000000000000000000000000..67d8fe3f1c61e659348147e2a1756d668f4ab937 --- /dev/null +++ b/src-test/src/org/openbravo/test/matchers/json/IsMatchingJSONObject.java @@ -0,0 +1,51 @@ +/* + ************************************************************************* + * The contents of this file are subject to the Openbravo Public License + * Version 1.1 (the "License"), being the Mozilla Public License + * Version 1.1 with a permitted attribution clause; you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at http://www.openbravo.com/legal/license.html + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * The Original Code is Openbravo ERP. + * The Initial Developer of the Original Code is Openbravo SLU + * All portions are Copyright (C) 2022 Openbravo SLU + * All Rights Reserved. + * Contributor(s): ______________________________________. + ************************************************************************ + */ +package org.openbravo.test.matchers.json; + +import org.codehaus.jettison.json.JSONObject; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; + +/** + * A matcher to check whether a JSONObjects includes all the properties and with the same value of + * another JSONObject. + */ +class IsMatchingJSONObject extends TypeSafeMatcher { + + private JSONObject subset; + + IsMatchingJSONObject(JSONObject subset) { + this.subset = subset; + } + + @Override + protected boolean matchesSafely(JSONObject item) { + return JSONComparator.objectsMatch(item, subset); + } + + @Override + public void describeTo(Description description) { + description.appendText("matches with ").appendValue(subset); + } + + @Override + public void describeMismatchSafely(JSONObject item, Description description) { + description.appendText("was ").appendValue(item); + } +} diff --git a/src-test/src/org/openbravo/test/matchers/json/JSONComparator.java b/src-test/src/org/openbravo/test/matchers/json/JSONComparator.java new file mode 100644 index 0000000000000000000000000000000000000000..23fb0ce1dcef961b8c04748af4daa4b5b3447319 --- /dev/null +++ b/src-test/src/org/openbravo/test/matchers/json/JSONComparator.java @@ -0,0 +1,154 @@ +/* + ************************************************************************* + * The contents of this file are subject to the Openbravo Public License + * Version 1.1 (the "License"), being the Mozilla Public License + * Version 1.1 with a permitted attribution clause; you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at http://www.openbravo.com/legal/license.html + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * The Original Code is Openbravo ERP. + * The Initial Developer of the Original Code is Openbravo SLU + * All portions are Copyright (C) 2022 Openbravo SLU + * All Rights Reserved. + * Contributor(s): ______________________________________. + ************************************************************************ + */ +package org.openbravo.test.matchers.json; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import org.codehaus.jettison.json.JSONArray; +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; +import org.hamcrest.Matcher; + +/** + * Contains the base methods for comparing JSONObjects and JSONArrays + */ +class JSONComparator { + + private JSONComparator() { + } + + static boolean objectsAreEquivalent(Object obj1, Object obj2) { + if (!(obj1 instanceof JSONObject) || !(obj2 instanceof JSONObject)) { + return obj1.equals(obj2); + } + JSONObject json1 = (JSONObject) obj1; + JSONObject json2 = (JSONObject) obj2; + if (json1.length() != json2.length()) { + return false; + } + return allMatch(json1, key -> propertiesAreEqual(json1.opt(key), json2.opt(key))); + } + + static boolean objectsMatch(JSONObject json, JSONObject subset) { + if (subset.length() > json.length()) { + return false; + } + + JSONObject common = getCommonProperties(json, subset); + + if (common.length() == 0) { + // not any common property + return false; + } + + return allMatch(common, key -> propertiesAreEqual(common.opt(key), subset.opt(key))); + } + + static boolean arrayContains(JSONArray array, List objects) { + return objects.stream().allMatch(json -> hasEquivalentObject(array, json)); + } + + private static JSONObject getCommonProperties(JSONObject json1, JSONObject json2) { + @SuppressWarnings("unchecked") + Stream stream = asStream(json1.keys()); + return stream.filter(json2::has).collect(Collector.of(JSONObject::new, (result, key) -> { + try { + result.put(key, json1.get(key)); + } catch (JSONException ignore) { + // should not fail + } + }, (object1, object2) -> { + throw new UnsupportedOperationException( + "This JSONObject collector does not support combine operation"); + })); + } + + private static boolean allMatch(JSONObject json, Predicate keyPredicate) { + @SuppressWarnings("unchecked") + Stream stream = asStream(json.keys()); + return stream.allMatch(keyPredicate::test); + } + + private static boolean propertiesAreEqual(Object prop1, Object prop2) { + if (prop1 == null || prop2 == null) { + return prop1 == null && prop2 == null; + } + if (prop1.getClass() != prop2.getClass()) { + if (prop1 instanceof Number && prop2 instanceof Number) { + return areEqualNumericValues((Number) prop1, (Number) prop2); + } else if (canCompareTimestampValues(prop1, prop2)) { + return areEqualStringValues(prop1, prop2); + } else if (prop2 instanceof Matcher) { + return ((Matcher) prop2).matches(prop1); + } + return false; + } + if (prop1 instanceof JSONObject) { + return objectsAreEquivalent(prop1, prop2); + } + if (prop1 instanceof JSONArray) { + return arraysAreEquivalent((JSONArray) prop1, (JSONArray) prop2); + } + return prop1.equals(prop2); + } + + private static boolean areEqualNumericValues(Number number1, Number number2) { + return new BigDecimal(number1.toString()).compareTo(new BigDecimal(number2.toString())) == 0; + } + + private static boolean canCompareTimestampValues(Object object1, Object object2) { + if (object1 instanceof Timestamp) { + return object2 instanceof Timestamp || object2 instanceof String; + } + return object1 instanceof String && object2 instanceof Timestamp; + } + + private static boolean areEqualStringValues(Object object1, Object object2) { + return object1.toString().equals(object2.toString()); + } + + private static boolean hasEquivalentObject(JSONArray array, Object object) { + return asStream(array).anyMatch(json -> objectsAreEquivalent(json, object)); + } + + private static boolean arraysAreEquivalent(JSONArray array1, JSONArray array2) { + if (array1.length() != array2.length()) { + return false; + } + return arrayContains(array1, asStream(array2).collect(Collectors.toList())); + } + + private static Stream asStream(Iterator sourceIterator) { + Iterable iterable = () -> sourceIterator; + return StreamSupport.stream(iterable.spliterator(), false); + } + + private static Stream asStream(JSONArray array) { + return IntStream.range(0, array.length()).mapToObj(array::opt); + } +} diff --git a/src-test/src/org/openbravo/test/matchers/json/JSONMatchers.java b/src-test/src/org/openbravo/test/matchers/json/JSONMatchers.java new file mode 100644 index 0000000000000000000000000000000000000000..ede4c626e6dd2b04911a39fc011d43bd8041839a --- /dev/null +++ b/src-test/src/org/openbravo/test/matchers/json/JSONMatchers.java @@ -0,0 +1,122 @@ +/* + ************************************************************************* + * The contents of this file are subject to the Openbravo Public License + * Version 1.1 (the "License"), being the Mozilla Public License + * Version 1.1 with a permitted attribution clause; you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at http://www.openbravo.com/legal/license.html + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * The Original Code is Openbravo ERP. + * The Initial Developer of the Original Code is Openbravo SLU + * All portions are Copyright (C) 2022 Openbravo SLU + * All Rights Reserved. + * Contributor(s): ______________________________________. + ************************************************************************ + */ +package org.openbravo.test.matchers.json; + +import java.util.Arrays; + +import org.codehaus.jettison.json.JSONArray; +import org.codehaus.jettison.json.JSONObject; +import org.hamcrest.Matcher; + +/** + * Provides different matchers for asserting JSONObjects and JSONArrays + */ +public class JSONMatchers { + + private JSONMatchers() { + } + + /** + * Creates a matcher for a JSONObject matching when the examined JSONObject has exactly the same + * number of properties with the same values as the expected one. The order of the keys is not + * taken into account. It accepts matcher properties. + * + * For example assertThat(actual, equal(expected));: + * + * - Passes if actual = {p1: "hello", p2: 2} and expected = {p1: "hello", p2: 2}
+ * - Passes if actual = {p1: "hello", p2: 2} and expected = {p1: "hello", p2: greaterThan(1)}
+ * - Does not pass if actual = {p1: "hello", p2: 2} and expected = {p1: "hello", p2: 1} + * + * @param expected + * the JSONObject that is expected to be equal to the actual one + * + * @return the equal matcher + * + */ + public static Matcher equal(JSONObject expected) { + return new IsEqualJSONObject(expected); + } + + /** + * Creates a matcher for a JSONObject matching when the examined JSONObject contains the + * properties with the same values of the expected one. The order of the keys is not taken into + * account. It accepts matcher properties. + * + * For example assertThat(actual, matchesObject(expected));: + * + * - Passes if actual = {p1: "hello", p2: 2} and expected = {p1: "hello", p2: 2}
+ * - Passes if actual = {p1: "hello", p2: 2} and expected = {p1: "hello", p2: greaterThan(1)}
+ * - Passes if actual = {p1: "hello", p2: 2} and expected = {p1: "hello"}
+ * - Passes if actual = {p1: "hello", p2: 2} and expected = {p2: greaterThan(1)}
+ * - Does not pass if actual = {p1: "hello", p2: 2} and expected = {p1: "hello", p2: 1}
+ * - Does not pass if actual = {p1: "hello", p2: 2} and expected = {p1: "hello", p2: 2, p3: "bye"} + * + * @param expected + * the JSON object that is expected to match all its properties with the actual one + * + * @return the matchesObject matcher + */ + public static Matcher matchesObject(JSONObject expected) { + return new IsMatchingJSONObject(expected); + } + + /** + * Creates a matcher for a JSONArray matching when the examined JSONArray contains all the + * expected objects. The order of the objects is not taken into account. + * + * For example assertThat(actual, hasItems(item1, item2));: + * + * - Passes if actual = [{p1: "hello", p2: 2}, 1], item = {p1: "hello", p2: 2} and item2 = 1
+ * - Passes if actual = [{p1: "hello", p2: 2}, 1], item = {p1: "hello", p2: greaterThan(1)} and + * item2 = 1
+ * - Does not pass if actual = [{p1: "hello", p2: 2}, 1], item = {p1: "hello", p2: 2} and item2 = + * 3 + * + * @param expected + * the objects that are expected to be included in the actual JSONArray + * + * @return the hasItems for objects matcher + */ + public static Matcher hasItems(Object... expected) { + return new HasItems(Arrays.asList(expected)); + } + + /** + * Creates a matcher for a JSONArray matching when the examined JSONArray matches with all the + * expected matchers. + * + * For example assertThat(actual, hasItems(matcher1, matcher2));: + * + * - Passes if actual = [{p1: "hello", p2: 2}, 1], matcher1 = matchesObject{p1: "hello"} and + * matcher2 = greaterThan(1)
+ * - Passes if actual = [{p1: "hello", p2: 2}, 1], matcher1 = matchesObject{p1: startsWith("he")} + * and matcher2 = greaterThan(1)
+ * - Does not pass if actual = [{p1: "hello", p2: 2}, 1], matcher1 = matchesObject{p1: "hello"} + * and matcher2 = greaterThan(10) + * + * @param expected + * a list of matchers that are expected to match with the actual JSONArray + * + * @return the hasItems for matchers matcher + */ + @SafeVarargs + public static Matcher hasItems(Matcher... expected) { + return new HasMatchingItems(Arrays.asList(expected)); + } +} diff --git a/src-test/src/org/openbravo/test/matchers/json/JSONMatchersTest.java b/src-test/src/org/openbravo/test/matchers/json/JSONMatchersTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d6cf07b644d93d9f76501d4b27f2552f2805ce1a --- /dev/null +++ b/src-test/src/org/openbravo/test/matchers/json/JSONMatchersTest.java @@ -0,0 +1,418 @@ +/* + ************************************************************************* + * The contents of this file are subject to the Openbravo Public License + * Version 1.1 (the "License"), being the Mozilla Public License + * Version 1.1 with a permitted attribution clause; you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at http://www.openbravo.com/legal/license.html + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * The Original Code is Openbravo ERP. + * The Initial Developer of the Original Code is Openbravo SLU + * All portions are Copyright (C) 2022 Openbravo SLU + * All Rights Reserved. + * Contributor(s): ______________________________________. + ************************************************************************ + */ +package org.openbravo.test.matchers.json; + +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.core.StringStartsWith.startsWith; +import static org.junit.Assert.assertThat; +import static org.openbravo.test.matchers.json.JSONMatchers.equal; +import static org.openbravo.test.matchers.json.JSONMatchers.hasItems; +import static org.openbravo.test.matchers.json.JSONMatchers.matchesObject; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.codehaus.jettison.json.JSONArray; +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; +import org.junit.Test; + +/** + * Tests the correct behavior of the matchers exposed by {@link JSONMatchers} + */ +public class JSONMatchersTest { + + @Test + public void areEqual() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.base(); + + assertThat("JSON objects are equal", json1, equal(json2)); + } + + @Test + public void withPropsWithSameNumericalValueAreEqual() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.withProp("key2", BigDecimal.valueOf(2.5)); + + assertThat("JSON objects are equal", json1, equal(json2)); + } + + @Test + public void withPropsWithSameTimestampValueAreEqual() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.withProp("key5", "2015-08-24 00:00:00.0"); + + assertThat("JSON objects are equal", json1, equal(json2)); + } + + @Test + public void withMatherAreEqual() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.withProp("key2", greaterThan(2.4)); + + assertThat("JSON objects are equal", json1, equal(json2)); + } + + @Test + public void withAdditionalPropsAreNotEqual() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.withProp("key6", "1234"); + + assertThat("JSON objects are not equal", json1, not(equal(json2))); + } + + @Test + public void withMissingPropsAreNotEqual() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.withoutProp("key3"); + + assertThat("JSON objects are not equal", json1, not(equal(json2))); + } + + @Test + public void withDifferentPropValueAreNotEqual1() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.withProp("key2", BigDecimal.valueOf(1.5)); + + assertThat("JSON objects are not equal", json1, not(equal(json2))); + } + + @Test + public void withDifferentPropValueAreNotEqual2() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.withProp("key4", "efgh"); + + assertThat("JSON objects are not equal", json1, not(equal(json2))); + } + + @Test + public void withDifferentPropValueAreNotEqual3() throws JSONException { + JSONObject json1 = BaseJSON.base(); + JSONArray array = new JSONArray(); + JSONObject inner = new JSONObject(); + inner.put("key4a", Integer.valueOf(3)); + array.put(inner); + JSONObject json2 = BaseJSON.withProp("key4", array); + + assertThat("JSON objects are not equal", json1, not(equal(json2))); + } + + @Test + public void withMatherAreNotEqual() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.withProp("key2", greaterThan(2.6)); + + assertThat("JSON objects are equal", json1, not(equal(json2))); + } + + @Test + public void match() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.base(); + + assertThat("JSON objects are matching", json1, matchesObject(json2)); + } + + @Test + public void matchWithPropertyMatcher() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.withProp("key1", startsWith("abc")); + + assertThat("JSON objects are matching", json1, matchesObject(json2)); + } + + @Test + public void matchWithSubset() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.withoutProp("key3"); + + assertThat("JSON objects are matching", json1, matchesObject(json2)); + } + + @Test + public void withMissingPropsNoMatch() { + JSONObject json1 = BaseJSON.withoutProp("key3"); + JSONObject json2 = BaseJSON.base(); + + assertThat("JSON objects are not matching", json1, not(matchesObject(json2))); + } + + @Test + public void withDifferentPropValueNoMatch1() { + JSONObject json1 = BaseJSON.base(); + JSONObject json2 = BaseJSON.withProp("key4", Double.valueOf(3.5)); + + assertThat("JSON objects are not matching", json1, not(matchesObject(json2))); + } + + @Test + public void withDifferentPropValueNoMatch2() throws JSONException { + JSONObject json1 = BaseJSON.base(); + JSONArray array = new JSONArray(); + JSONObject inner = new JSONObject(); + inner.put("key4a", Integer.valueOf(3)); + array.put(inner); + JSONObject json2 = BaseJSON.withProp("key4", array); + + assertThat("JSON objects are not matching", json1, not(matchesObject(json2))); + } + + @Test + public void arrayHasItems() { + JSONArray array = Items.baseArray(); + List items = Items.base(); + JSONObject item1 = items.get(0); + JSONObject item2 = items.get(2); + + assertThat("JSON array has all the items", array, hasItems(item1, item2)); + } + + @Test + public void arrayHasItemsWithPropertyMatcher() throws JSONException { + JSONArray array = Items.baseArray(); + JSONObject inner = new JSONObject(); + inner.put("key2a", greaterThan(Double.valueOf(2))); + inner.put("key2b", startsWith("abc")); + + assertThat("JSON array has all the items", array, hasItems(inner)); + } + + @Test + public void arrayHasItemsWithDifferenTypes() { + JSONArray array = Items.withObject(1); + List items = Items.base(); + JSONObject item1 = items.get(0); + JSONObject item2 = items.get(2); + int item3 = 1; + + assertThat("JSON array has all the items", array, hasItems(item1, item2, item3)); + } + + @Test + public void arrayDoesNotHaveItems() { + JSONArray array = Items.withoutObjectAt(0); + List items = Items.base(); + JSONObject item1 = items.get(0); + JSONObject item2 = items.get(2); + + assertThat("JSON array has all the items", array, not(hasItems(item1, item2))); + } + + @Test + public void arrayDoesNotHaveItemsWithDifferenTypes() { + JSONArray array = Items.withObject(1); + List items = Items.base(); + JSONObject item1 = items.get(0); + JSONObject item2 = items.get(2); + int item3 = 5; + + assertThat("JSON array has all the items", array, not(hasItems(item1, item2, item3))); + } + + @Test + public void arrayDoesNotHaveItemsWithPropertyMatcher() throws JSONException { + JSONArray array = Items.baseArray(); + JSONObject inner = new JSONObject(); + inner.put("key2a", greaterThan(Double.valueOf(2))); + inner.put("key2b", startsWith("efg")); + + assertThat("JSON array has all the items", array, not(hasItems(inner))); + } + + @Test + public void matchItems() throws JSONException { + JSONArray array = Items.baseArray(); + JSONObject item1 = Items.base().get(0); + JSONObject item2 = new JSONObject(); + item2.put("key2a", greaterThan(Double.valueOf(2))); + item2.put("key2b", startsWith("abc")); + + assertThat("JSON array match all items", array, hasItems(equal(item1), matchesObject(item2))); + } + + @Test + public void matchItemsRegardlessTheOrder() throws JSONException { + JSONArray array = Items.withReverseOrder(); + JSONObject item1 = Items.base().get(0); + JSONObject item2 = new JSONObject(); + item2.put("key2a", greaterThan(Double.valueOf(2))); + item2.put("key2b", startsWith("abc")); + + assertThat("JSON array match all items", array, hasItems(equal(item1), matchesObject(item2))); + } + + @Test + public void matchItemsWithDifferentMatcherTypes() throws JSONException { + JSONArray array = Items.withObject(5); + JSONObject item1 = Items.base().get(0); + JSONObject item2 = new JSONObject(); + item2.put("key2a", greaterThan(Double.valueOf(2))); + item2.put("key2b", startsWith("abc")); + int item3 = 4; + + assertThat("JSON array match all items", array, + hasItems(equal(item1), matchesObject(item2), greaterThan(item3))); + } + + @Test + public void itemsDoNotMatch() throws JSONException { + JSONArray array = Items.baseArray(); + JSONObject item1 = Items.base().get(0); + JSONObject item2 = new JSONObject(); + item2.put("key2a", greaterThan(Double.valueOf(2))); + item2.put("key2b", startsWith("ccc")); + + assertThat("JSON array does not match all items", array, + not(hasItems(equal(item1), matchesObject(item2)))); + } + + @Test + public void itemsDoNotMatchWithDifferentMatcherTypes() throws JSONException { + JSONArray array = Items.withObject(5); + JSONObject item1 = Items.base().get(0); + JSONObject item2 = new JSONObject(); + item2.put("key2a", greaterThan(Double.valueOf(2))); + item2.put("key2b", startsWith("abc")); + int item3 = 7; + + assertThat("JSON array does not match all items", array, + not(hasItems(equal(item1), matchesObject(item2), greaterThan(item3)))); + } + + private static class BaseJSON { + private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private JSONObject json; + + private BaseJSON() { + json = new JSONObject(); + + try { + json.put("key1", "abcd"); + json.put("key2", Double.valueOf(2.5)); + json.put("key3", true); + json.put("key4", new JSONArray()); + JSONObject inner = new JSONObject(); + inner.put("key4a", Integer.valueOf(2)); + json.getJSONArray("key4").put(inner); + json.put("key5", new Timestamp(formatter.parse("2015-08-24 00:00:00.0").getTime())); + } catch (JSONException | ParseException ignore) { + // should not fail + } + } + + private JSONObject getJSON() { + return json; + } + + static JSONObject base() { + return new BaseJSON().getJSON(); + } + + static JSONObject withProp(String property, Object value) { + JSONObject json = base(); + try { + json.put(property, value); + } catch (JSONException ignore) { + // should not fail + } + return json; + } + + static JSONObject withoutProp(String property) { + JSONObject json = base(); + if (json.has(property)) { + json.remove(property); + } + return json; + } + } + + private static class Items { + List list; + JSONArray array; + + private Items() { + list = new ArrayList<>(); + array = new JSONArray(); + try { + JSONObject inner1 = new JSONObject(); + inner1.put("key1a", Integer.valueOf(2)); + inner1.put("key1b", true); + JSONObject inner2 = new JSONObject(); + inner2.put("key2a", Double.valueOf(2.5)); + inner2.put("key2b", "abcd"); + JSONObject inner3 = new JSONObject(); + inner3.put("key3a", new JSONObject()); + inner3.put("key3b", "abcd"); + + addItem(inner1); + addItem(inner2); + addItem(inner3); + } catch (JSONException ignore) { + // should not fail + } + } + + private void addItem(JSONObject item) { + list.add(item); + array.put(item); + } + + private List getList() { + return list; + } + + private JSONArray getArray() { + return array; + } + + static List base() { + return new Items().getList(); + } + + static JSONArray baseArray() { + return new Items().getArray(); + } + + static JSONArray withReverseOrder() { + List list = base(); + Collections.reverse(list); + return new JSONArray(list); + } + + static JSONArray withObject(Object object) { + JSONArray array = baseArray(); + array.put(object); + return array; + } + + static JSONArray withoutObjectAt(int index) { + List list = base(); + list.remove(index); + return new JSONArray(list); + } + } +}