Script { public static AssertionResult matchJsonOrObject(MatchType matchType, ScriptValue actual, String path, String expression, ScenarioContext context) { DocumentContext actualDoc; switch (actual.getType()) { case JSON: case MAP: case LIST: actualDoc = actual.getAsJsonDocument(); break; case XML: actualDoc = XmlUtils.toJsonDoc(actual.getValue(Node.class)); break; case STRING: case INPUT_STREAM: String actualString = actual.getAsString(); ScriptValue expectedString = evalKarateExpression(expression, context); if (!expectedString.isStringOrStream()) { return matchFailed(matchType, path, actualString, expectedString.getValue(), "type of actual value is 'string' but that of expected is " + expectedString.getType()); } else { return matchStringOrPattern('.', path, matchType, null, null, actual, expectedString.getAsString(), context); } case PRIMITIVE: ScriptValue expected = evalKarateExpression(expression, context); if (expected.isStringOrStream()) { return matchStringOrPattern('.', path, matchType, null, null, actual, expected.getAsString(), context); } else { return matchPrimitive(matchType, path, actual.getValue(), expected.getValue()); } case NULL: ScriptValue expectedNull = evalKarateExpression(expression, context); if (expectedNull.isNull()) { if (matchType == MatchType.NOT_EQUALS) { return matchFailed(matchType, path, null, null, "actual and expected values are both null"); } return AssertionResult.PASS; } else if (!expectedNull.isStringOrStream()) { if (matchType == MatchType.NOT_EQUALS) { return AssertionResult.PASS; } return matchFailed(matchType, path, null, expectedNull.getValue(), "actual value is null but expected is " + expectedNull); } else { return matchStringOrPattern('.', path, matchType, null, null, actual, expectedNull.getAsString(), context); } case BYTE_ARRAY: ScriptValue expectedBytesValue = evalKarateExpression(expression, context); byte[] expectedBytes = expectedBytesValue.getAsByteArray(); byte[] actualBytes = actual.getAsByteArray(); if (Arrays.equals(expectedBytes, actualBytes)) { if (matchType == MatchType.NOT_EQUALS) { return matchFailed(matchType, path, actualBytes, expectedBytes, "actual and expected byte-arrays are not equal"); } return AssertionResult.PASS; } else { if (matchType == MatchType.NOT_EQUALS) { return AssertionResult.PASS; } return matchFailed(matchType, path, actualBytes, expectedBytes, "actual and expected byte-arrays are not equal"); } default: throw new RuntimeException("not json, cannot do json path for value: " + actual + ", path: " + path); } ScriptValue expected = evalKarateExpressionForMatch(expression, context); Object actObject; try { actObject = actualDoc.read(path); } catch (PathNotFoundException e) { if (expected.isString()) { String expString = expected.getAsString(); if (isOptionalMacro(expString) || "#notpresent".equals(expString) || "#ignore".equals(expString)) { return AssertionResult.PASS; } } return matchFailed(matchType, path, null, expected.getValue(), "actual json-path does not exist"); } Object expObject; switch (expected.getType()) { case JSON: expObject = expected.getValue(DocumentContext.class).read("$"); break; default: expObject = expected.getValue(); } switch (matchType) { case CONTAINS: case NOT_CONTAINS: case CONTAINS_ONLY: case CONTAINS_ANY: case CONTAINS_DEEP: if (actObject instanceof List && !(expObject instanceof List)) { expObject = Collections.singletonList(expObject); } case NOT_EQUALS: case EQUALS: return matchNestedObject('.', path, matchType, actualDoc, null, actObject, expObject, context); case EACH_CONTAINS: case EACH_NOT_CONTAINS: case EACH_CONTAINS_ONLY: case EACH_CONTAINS_ANY: case EACH_NOT_EQUALS: case EACH_EQUALS: if (actObject instanceof List) { List actList = (List) actObject; MatchType listMatchType = getInnerMatchType(matchType); int actSize = actList.size(); for (int i = 0; i < actSize; i++) { Object actListObject = actList.get(i); AssertionResult ar = matchNestedObject('.', "$[" + i + "]", listMatchType, actObject, actListObject, actListObject, expObject, context); if (!ar.pass) { if (matchType == MatchType.EACH_NOT_EQUALS) { return AssertionResult.PASS; } else { return ar; } } } if (matchType == MatchType.EACH_NOT_EQUALS) { return matchFailed(matchType, path, actual.getValue(), expected.getValue(), "all list items matched"); } return AssertionResult.PASS; } else { throw new RuntimeException("'match each' failed, not a json array: + " + actual + ", path: " + path); } default: throw new RuntimeException("unexpected match type: " + matchType); } } private  Script(); static final boolean isCallSyntax(String text); static final boolean isCallOnceSyntax(String text); static final boolean isGetSyntax(String text); static final boolean isJson(String text); static final boolean isJsonPath(String text); static final boolean isXml(String text); static final boolean isXmlPath(String text); static final boolean isXmlPathFunction(String text); static final boolean isEmbeddedExpression(String text); static final boolean isWithinParentheses(String text); static final boolean isContainsMacro(String text); static final boolean isContainsOnlyMacro(String text); static final boolean isContainsAnyMacro(String text); static final boolean isNotContainsMacro(String text); static final boolean isDollarPrefixedJsonPath(String text); static final boolean isDollarPrefixedParen(String text); static final boolean isDollarPrefixed(String text); static final boolean isVariable(String text); static final boolean isVariableAndSpaceAndPath(String text); static StringUtils.Pair parseVariableAndPath(String text); static ScriptValue evalKarateExpressionForMatch(String text, ScenarioContext context); static ScriptValue getIfVariableReference(String text, ScenarioContext context); static ScriptValue evalKarateExpression(String text, ScenarioContext context); static ScriptValue evalXmlPathOnVarByName(String name, String path, ScenarioContext context); static ScriptValue evalXmlPathOnXmlNode(Node doc, String path); static ScriptValue evalJsonPathOnVarByName(String name, String exp, ScenarioContext context); static ScriptValue evalJsExpression(String exp, ScenarioContext context); static ScriptValue evalJsExpression(String exp, ScenarioContext context, ScriptValue selfValue, Object root, Object parent); static boolean isValidVariableName(String name); static void evalJsonEmbeddedExpressions(DocumentContext doc, ScenarioContext context); static void evalXmlEmbeddedExpressions(Node node, ScenarioContext context); static ScriptValue copy(String name, String exp, ScenarioContext context, boolean validateName); static ScriptValue assign(String name, String exp, ScenarioContext context); static ScriptValue assign(AssignType assignType, String name, String exp, ScenarioContext context, boolean validateName); static DocumentContext toJsonDoc(ScriptValue sv, ScenarioContext context); static AssertionResult matchNamed(MatchType matchType, String expression, String path, String expected, ScenarioContext context); static AssertionResult matchScriptValue(MatchType matchType, ScriptValue actual, String path, String expected, ScenarioContext context); static AssertionResult matchString(MatchType matchType, ScriptValue actual, String expected, String path, ScenarioContext context); static boolean isMacro(String text); static boolean isOptionalMacro(String text); static AssertionResult matchStringOrPattern(char delimiter, String path, MatchType stringMatchType,
            Object actRoot, Object actParent, ScriptValue actValue, String expected, ScenarioContext context); static AssertionResult matchXml(MatchType matchType, ScriptValue actual, String path, String expression, ScenarioContext context); static AssertionResult matchJsonOrObject(MatchType matchType, ScriptValue actual, String path, String expression, ScenarioContext context); static AssertionResult matchFailed(MatchType matchType, String path,
            Object actObject, Object expObject, String reason); static AssertionResult matchNestedObject(char delimiter, String path, MatchType matchType,
            Object actRoot, Object actParent, Object actObject, Object expObject, ScenarioContext context); static boolean isPrimitive(Class clazz); static void removeValueByPath(String name, String path, ScenarioContext context); static void setValueByPath(String name, String path, ScriptValue value, ScenarioContext context); static void setValueByPath(String name, String path, String exp, ScenarioContext context); static void setValueByPath(String name, String path, String exp, boolean delete, ScenarioContext context, boolean viaTable); static void setValueByPath(String name, String path, ScriptValue value, boolean delete, ScenarioContext context, boolean viaTable); static ScriptValue call(ScriptValue called, String argString, ScenarioContext context, boolean reuseParentConfig); static ScriptValue evalJavaFunctionCall(Function function, Object callArg, ScenarioContext context); static ScriptValue evalJsFunctionCall(ScriptObjectMirror som, Object callArg, ScenarioContext context); static ScriptValue evalFeatureCall(Feature feature, Object callArg, ScenarioContext context, boolean reuseParentConfig); static StringUtils.Pair parseCallArgs(String line); static void callAndUpdateConfigAndAlsoVarsIfMapReturned(boolean callOnce, String name, String arg, ScenarioContext context); static AssertionResult assertBoolean(String expression, ScenarioContext context); static String replacePlaceholderText(String text, String token, String replaceWith, ScenarioContext context); static String replacePlaceholders(String text, List<Map<String, String>> list, ScenarioContext context); static List<Map<String, Object>> evalTable(List<Map<String, String>> list, ScenarioContext context); static void setByPathTable(String name, String path, List<Map<String, String>> list, ScenarioContext context); static final String VAR_SELF; static final String VAR_ROOT; static final String VAR_PARENT; static final String VAR_LOOP; static final String VAR_ARG; static final String VAR_HEADER; static final Map<String, Validator> VALIDATORS; }
@Test public void testMatchJsonPath() { DocumentContext doc = JsonPath.parse("{ foo: 'bar', baz: { ban: [1, 2, 3]} }"); ScenarioContext ctx = getContext(); ctx.vars.put("myJson", doc); ScriptValue myJson = ctx.vars.get("myJson"); assertTrue(Script.matchJsonOrObject(MatchType.EQUALS, myJson, "$.foo", "'bar'", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.EQUALS, myJson, "$.baz", "{ ban: [1, 2, 3]} }", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.EQUALS, myJson, "$.baz.ban[1]", "2", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.EQUALS, myJson, "$.baz", "{ ban: [1, '#ignore', 3]} }", ctx).pass); }
@Test public void testMatchAllJsonPath() { DocumentContext doc = JsonPath.parse("{ foo: [{bar: 1, baz: 'a'}, {bar: 2, baz: 'b'}, {bar:3, baz: 'c'}]}"); ScenarioContext ctx = getContext(); ctx.vars.put("myJson", doc); ScriptValue myJson = ctx.vars.get("myJson"); assertTrue(Script.matchJsonOrObject(MatchType.EQUALS, myJson, "$.foo", "[{bar: 1, baz: 'a'}, {bar: 2, baz: 'b'}, {bar:3, baz: 'c'}]", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS, myJson, "$.foo", "[{bar: 1, baz: 'a'}, {bar: 2, baz: 'b'}, {bar:3, baz: 'c'}]", ctx).pass); assertFalse(Script.matchJsonOrObject(MatchType.NOT_CONTAINS, myJson, "$.foo", "[{bar: 1, baz: 'a'}]", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.NOT_CONTAINS, myJson, "$.foo", "[{bar: 9, baz: 'z'}, {bar: 99, baz: 'zz'}]", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS_ONLY, myJson, "$.foo", "[{bar: 1, baz: 'a'}, {bar: 2, baz: 'b'}, {bar:3, baz: 'c'}]", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS_ANY, myJson, "$.foo", "[{bar: 9, baz: 'z'}, {bar: 2, baz: 'b'}]", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS_DEEP, myJson, "$.foo", "[{bar: 1}, {bar: 2}, {bar:3}]", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS_ONLY, myJson, "$.foo", "[{bar: 2, baz: 'b'}, {bar:3, baz: 'c'}, {bar: 1, baz: 'a'}]", ctx).pass); assertFalse(Script.matchJsonOrObject(MatchType.CONTAINS_ONLY, myJson, "$.foo", "[{bar: 1, baz: 'a'}, {bar: 2, baz: 'b'}]", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.EACH_EQUALS, myJson, "$.foo", "{bar:'#number', baz:'#string'}", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.EACH_CONTAINS, myJson, "$.foo", "{bar:'#number'}", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.EACH_CONTAINS, myJson, "$.foo", "{baz:'#string'}", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS_DEEP, myJson, "$.foo", "[{bar: 1}, {bar: 2}, {bar:3}]", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.EACH_NOT_CONTAINS, myJson, "$.foo", "{baz:'z'}", ctx).pass); assertFalse(Script.matchJsonOrObject(MatchType.EACH_NOT_CONTAINS, myJson, "$.foo", "{baz:'a'}", ctx).pass); assertFalse(Script.matchJsonOrObject(MatchType.EACH_EQUALS, myJson, "$.foo", "{bar:'#? _ < 3', baz:'#string'}", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS_DEEP, myJson, "$", "{foo: [{bar: 1}, {bar: 2}, {bar:3}]}", ctx).pass); }
@Test public void testMatchContainsDeep() { DocumentContext doc = JsonPath.parse("{ foo: [{bar: 1, baz: 'a'}, {bar: 2, baz: 'b'}, {bar:3, baz: 'c'}], eoo: { doo: { car: 1, caz: 'a'} } }"); ScenarioContext ctx = getContext(); ctx.vars.put("myJson", doc); ScriptValue myJson = ctx.vars.get("myJson"); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS_DEEP, myJson, "$.foo", "[{bar: 1}, {bar: 2}, {bar:3}]", ctx).pass); assertFalse(Script.matchJsonOrObject(MatchType.CONTAINS_DEEP, myJson, "$.foo", "[{bar: 1}, {bar: 4}, {bar:3}]", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS_DEEP, myJson, "$.foo", "[{bar: 1, baz: 'a'}]", ctx).pass); assertFalse(Script.matchJsonOrObject(MatchType.CONTAINS_DEEP, myJson, "$.foo", "[{bar: 1, baz: 'a', 'baq': 'b'}]", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS_DEEP, myJson, "$.foo", "[{baz: 'a'}, {bar: 2}, {bar:3, baz: 'c'}]", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS_DEEP, myJson, "$.foo", "[{baz: 'a'}, {bar:3, baz: 'c'}]", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS_DEEP, myJson, "$", "{eoo: '#ignore'}", ctx).pass); assertTrue(Script.matchJsonOrObject(MatchType.CONTAINS_DEEP, myJson, "$", "{eoo: { doo: {car: 1} } }", ctx).pass); assertFalse(Script.matchJsonOrObject(MatchType.CONTAINS_DEEP, myJson, "$", "{eoo: { doo: {car: 'a'} } }", ctx).pass); }
@Test public void testMatchTwoJsonDocsWithIgnores() { DocumentContext actual = JsonPath.parse(ACTUAL); DocumentContext expected = JsonPath.parse(EXPECTED); ScenarioContext ctx = getContext(); ctx.vars.put("actual", actual); ctx.vars.put("expected", expected); ScriptValue act = ctx.vars.get("actual"); assertTrue(Script.matchJsonOrObject(MatchType.EQUALS, act, "$", "expected", ctx).pass); }