package com.intuit.karate; import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.InvalidJsonException; import com.jayway.jsonpath.JsonPath; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.tuple.Pair; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.junit.Assert.*; import org.w3c.dom.Document; /** * * @author pthomas3 */ public class ScriptTest { private static final Logger logger = LoggerFactory.getLogger(ScriptTest.class); private ScriptContext getContext() { String featureDir = FileUtils.getDirContaining(getClass()).getPath(); ScriptEnv env = ScriptEnv.init("dev", new File(featureDir)); return new ScriptContext(env, null, null); } private AssertionResult matchJsonObject(Object act, Object exp, ScriptContext context) { return Script.matchNestedObject('.', "$", MatchType.EQUALS, null, act, exp, context); } @Test public void testParsingTextType() { assertTrue(Script.isVariableAndJsonPath("foo.bar")); assertFalse(Script.isVariableAndJsonPath("foo.bar()")); assertFalse(Script.isVariableAndXmlPath("foo.bar")); assertTrue(Script.isVariableAndXmlPath("foo/bar")); assertFalse(Script.isVariableAndJsonPath("foo/bar")); assertTrue(Script.isVariableAndXmlPath("foo/")); assertFalse(Script.isVariableAndXmlPath("foo")); assertTrue(Script.isVariable("foo")); assertFalse(Script.isVariableAndJsonPath("foo")); assertFalse(Script.isVariableAndXmlPath("foo")); assertTrue(Script.isJavaScriptFunction("function(){ return { bar: 'baz' } }")); assertFalse(Script.isVariableAndXmlPath("read('../syntax/for-demos.js')")); assertTrue(Script.isXmlPath("/foo")); assertTrue(Script.isXmlPath("//foo")); assertTrue(Script.isXmlPathFunction("lower-case('Foo')")); assertTrue(Script.isXmlPathFunction("count(/journal/article)")); assertTrue(Script.isVariableAndSpaceAndPath("foo count(/journal/article)")); assertTrue(Script.isVariableAndSpaceAndPath("foo $")); } @Test public void testEvalPrimitives() { ScriptContext ctx = getContext(); ctx.vars.put("foo", "bar"); ctx.vars.put("a", 1); ctx.vars.put("b", 2); String expression = "foo + 'baz'"; ScriptValue value = Script.evalInNashorn(expression, ctx); assertEquals(ScriptValue.Type.STRING, value.getType()); assertEquals("barbaz", value.getValue()); value = Script.evalInNashorn("a + b", ctx); assertEquals(ScriptValue.Type.PRIMITIVE, value.getType()); assertEquals(3.0, value.getValue()); } @Test public void testEvalMapsAndLists() { ScriptContext ctx = getContext(); Map<String, Object> testMap = new HashMap<>(); testMap.put("foo", "bar"); testMap.put("baz", 5); List<Integer> testList = new ArrayList<>(); testList.add(1); testList.add(2); testMap.put("myList", testList); ctx.vars.put("myMap", testMap); String expression = "myMap.foo + myMap.baz"; ScriptValue value = Script.evalInNashorn(expression, ctx); assertEquals(ScriptValue.Type.STRING, value.getType()); assertEquals("bar5", value.getValue()); value = Script.evalInNashorn("myMap.myList[0] + myMap.myList[1]", ctx); assertEquals(ScriptValue.Type.PRIMITIVE, value.getType()); assertEquals(3.0, value.getValue()); } @Test public void testEvalJsonDocuments() { ScriptContext ctx = getContext(); DocumentContext doc = JsonUtils.toJsonDoc("{ foo: 'bar', baz: [1, 2], ban: { hello: 'world' } }"); ctx.vars.put("myJson", doc); ScriptValue value = Script.evalInNashorn("myJson.foo", ctx); assertEquals("bar", value.getValue()); value = Script.evalInNashorn("myJson.baz[1]", ctx); assertEquals(2, value.getValue()); value = Script.evalInNashorn("myJson.ban.hello", ctx); assertEquals("world", value.getValue()); } @Test public void testEvalXmlDocuments() { ScriptContext ctx = getContext(); Document doc = XmlUtils.toXmlDoc("<root><foo>bar</foo><hello>world</hello></root>"); ctx.vars.put("myXml", doc); ScriptValue value = Script.evalInNashorn("myXml.root.foo", ctx); assertEquals("bar", value.getValue()); } @Test public void testAssignXmlWithLineBreaksAndMatchJson() { ScriptContext ctx = getContext(); Script.assign("foo", "<records>\n <record>a</record>\n <record>b</record>\n <record>c</record>\n</records>", ctx); Script.assign("bar", "foo.records", ctx); ScriptValue value = ctx.vars.get("bar"); assertTrue(value.getType() == ScriptValue.Type.MAP); assertTrue(Script.matchNamed(MatchType.EQUALS, "bar.record", null, "['a', 'b', 'c']", ctx).pass); assertTrue(Script.assertBoolean("foo.records.record.length == 3", ctx).pass); } @Test public void testAssignXmlWithLineBreaksAndNullElements() { ScriptContext ctx = getContext(); Script.assign("foo", "<records>\n <record>a</record>\n <record/>\n</records>", ctx); Script.assign("bar", "foo.records", ctx); ScriptValue value = ctx.vars.get("bar"); assertTrue(value.getType() == ScriptValue.Type.MAP); assertTrue(Script.matchNamed(MatchType.EQUALS, "bar.record", null, "['a', null]", ctx).pass); } @Test public void testJsonPathOnVarsByName() { ScriptContext ctx = getContext(); DocumentContext doc = JsonUtils.toJsonDoc("{ foo: 'bar', baz: [1, 2], ban: { hello: 'world' } }"); ctx.vars.put("myJson", doc); ScriptValue value = Script.evalJsonPathOnVarByName("myJson", "$.foo", ctx); assertEquals("bar", value.getValue()); value = Script.eval("myJson.foo", ctx); assertEquals("bar", value.getValue()); value = Script.evalJsonPathOnVarByName("myJson", "$.baz[1]", ctx); assertEquals(2, value.getValue()); value = Script.eval("myJson.baz[1]", ctx); assertEquals(2, value.getValue()); value = Script.evalJsonPathOnVarByName("myJson", "$.baz", ctx); assertEquals(ScriptValue.Type.LIST, value.getType()); value = Script.evalJsonPathOnVarByName("myJson", "$.ban", ctx); assertEquals(ScriptValue.Type.MAP, value.getType()); } @Test public void testXmlPathOnVarsByName() { ScriptContext ctx = getContext(); Document doc = XmlUtils.toXmlDoc("<root><foo>bar</foo></root>"); ctx.vars.put("myXml", doc); ScriptValue value = Script.evalXmlPathOnVarByName("myXml", "/root/foo", ctx); assertEquals(ScriptValue.Type.STRING, value.getType()); assertEquals("bar", value.getAsString()); value = Script.eval("myXml/root/foo", ctx); assertEquals("bar", value.getAsString()); } @Test public void testEvalXmlEmbeddedExpressions() { ScriptContext ctx = getContext(); ctx.vars.put("a", 1); ctx.vars.put("b", 2); Document doc = XmlUtils.toXmlDoc("<root><foo>#(a + b)</foo></root>"); Script.evalXmlEmbeddedExpressions(doc, ctx); ctx.vars.put("myXml", doc); ScriptValue value = Script.evalXmlPathOnVarByName("myXml", "/root/foo", ctx); assertEquals(ScriptValue.Type.STRING, value.getType()); assertEquals("3.0", value.getAsString()); } @Test public void testEvalXmlEmbeddedExpressionsInAttributes() { ScriptContext ctx = getContext(); ctx.vars.put("a", 5); String xml = "<foo bar=\"#(a)\">#(a)</foo>"; Document doc = XmlUtils.toXmlDoc(xml); Script.evalXmlEmbeddedExpressions(doc, ctx); String result = XmlUtils.toString(doc); logger.debug("result: {}", result); assertTrue(result.endsWith("<foo bar=\"5\">5</foo>")); } @Test public void testEvalJsonEmbeddedExpressions() { ScriptContext ctx = getContext(); ctx.vars.put("a", 1); ctx.vars.put("b", 2); DocumentContext doc = JsonUtils.toJsonDoc("{ foo: '#(a + b)' }"); Script.evalJsonEmbeddedExpressions(doc, ctx); ctx.vars.put("myJson", doc); ScriptValue value = Script.evalJsonPathOnVarByName("myJson", "$.foo", ctx); assertEquals(ScriptValue.Type.PRIMITIVE, value.getType()); assertEquals(3.0, value.getValue()); } @Test public void testEvalEmbeddedExpressionsWithJsonPath() { ScriptContext ctx = getContext(); String ticket = "{ ticket: 'my-ticket', userId: '12345' }"; ctx.vars.put("ticket", JsonUtils.toJsonDoc(ticket)); String json = "{ foo: '#(ticket.userId)' }"; DocumentContext doc = JsonUtils.toJsonDoc(json); Script.evalJsonEmbeddedExpressions(doc, ctx); String result = doc.jsonString(); logger.debug("result: {}", result); assertEquals("{\"foo\":\"12345\"}", result); } @Test public void testVariableNameValidation() { assertTrue(Script.isValidVariableName("foo")); assertTrue(Script.isValidVariableName("foo_bar")); assertTrue(Script.isValidVariableName("foo_")); assertTrue(Script.isValidVariableName("foo1")); assertTrue(Script.isValidVariableName("a")); assertTrue(Script.isValidVariableName("a1")); // bad assertFalse(Script.isValidVariableName("foo.bar")); assertFalse(Script.isValidVariableName("foo-bar")); assertFalse(Script.isValidVariableName("$foo")); assertFalse(Script.isValidVariableName("$foo/bar")); assertFalse(Script.isValidVariableName("_foo")); assertFalse(Script.isValidVariableName("_foo_")); assertFalse(Script.isValidVariableName("0")); assertFalse(Script.isValidVariableName("2foo")); } @Test public void testMatchMapObjects() { ScriptContext ctx = getContext(); Map<String, Object> left = new HashMap<>(); left.put("foo", "bar"); Map<String, Object> right = new HashMap<>(); right.put("foo", "bar"); assertTrue(matchJsonObject(left, right, ctx).pass); right.put("baz", "#ignore"); assertTrue(matchJsonObject(left, right, ctx).pass); left.put("baz", Arrays.asList(1, 2, 3)); right.put("baz", Arrays.asList(1, 2, 3)); assertTrue(matchJsonObject(left, right, ctx).pass); left.put("baz", Arrays.asList(1, 2)); assertFalse(matchJsonObject(left, right, ctx).pass); Map<String, Object> leftChild = new HashMap<>(); leftChild.put("a", 1); Map<String, Object> rightChild = new HashMap<>(); rightChild.put("a", 1); left.put("baz", leftChild); right.put("baz", rightChild); assertTrue(matchJsonObject(left, right, ctx).pass); List<Map> leftList = new ArrayList<>(); leftList.add(leftChild); List<Map> rightList = new ArrayList<>(); rightList.add(rightChild); left.put("baz", leftList); right.put("baz", rightList); assertTrue(matchJsonObject(left, right, ctx).pass); rightChild.put("a", 2); assertFalse(matchJsonObject(left, right, ctx).pass); rightChild.put("a", "#ignore"); assertTrue(matchJsonObject(left, right, ctx).pass); } @Test public void testMatchListObjects() { List left = new ArrayList(); List right = new ArrayList(); Map<String, Object> leftChild = new HashMap<>(); leftChild.put("a", 1); left.add(leftChild); Map<String, Object> rightChild = new HashMap<>(); rightChild.put("a", 1); right.add(rightChild); assertTrue(matchJsonObject(left, right, null).pass); } @Test public void testMatchJsonPath() { DocumentContext doc = JsonPath.parse("{ foo: 'bar', baz: { ban: [1, 2, 3]} }"); ScriptContext ctx = getContext(); ctx.vars.put("myJson", doc); ScriptValue myJson = ctx.vars.get("myJson"); assertTrue(Script.matchJsonPath(MatchType.EQUALS, myJson, "$.foo", "'bar'", ctx).pass); assertTrue(Script.matchJsonPath(MatchType.EQUALS, myJson, "$.baz", "{ ban: [1, 2, 3]} }", ctx).pass); assertTrue(Script.matchJsonPath(MatchType.EQUALS, myJson, "$.baz.ban[1]", "2", ctx).pass); assertTrue(Script.matchJsonPath(MatchType.EQUALS, myJson, "$.baz", "{ ban: [1, '#ignore', 3]} }", ctx).pass); } @Test public void testMatchJsonPathThatReturnsList() { DocumentContext doc = JsonPath.parse("{ foo: [{ bar: 1}, {bar: 2}, {bar: 3}]}"); ScriptContext ctx = getContext(); ctx.vars.put("json", doc); Script.assign("list", "json.foo", ctx); ScriptValue list = ctx.vars.get("list"); assertTrue(Script.matchJsonPath(MatchType.EQUALS, list, "$[0]", "{ bar: 1}", ctx).pass); assertTrue(Script.matchJsonPath(MatchType.EQUALS, list, "$[0].bar", "1", ctx).pass); } @Test public void testMatchAllJsonPath() { DocumentContext doc = JsonPath.parse("{ foo: [{bar: 1, baz: 'a'}, {bar: 2, baz: 'b'}, {bar:3, baz: 'c'}]}"); ScriptContext ctx = getContext(); ctx.vars.put("myJson", doc); ScriptValue myJson = ctx.vars.get("myJson"); assertTrue(Script.matchJsonPath(MatchType.EQUALS, myJson, "$.foo", "[{bar: 1, baz: 'a'}, {bar: 2, baz: 'b'}, {bar:3, baz: 'c'}]", ctx).pass); assertTrue(Script.matchJsonPath(MatchType.CONTAINS, myJson, "$.foo", "[{bar: 1, baz: 'a'}, {bar: 2, baz: 'b'}, {bar:3, baz: 'c'}]", ctx).pass); assertTrue(Script.matchJsonPath(MatchType.CONTAINS_ONLY, myJson, "$.foo", "[{bar: 1, baz: 'a'}, {bar: 2, baz: 'b'}, {bar:3, baz: 'c'}]", ctx).pass); // shuffle assertTrue(Script.matchJsonPath(MatchType.CONTAINS_ONLY, myJson, "$.foo", "[{bar: 2, baz: 'b'}, {bar:3, baz: 'c'}, {bar: 1, baz: 'a'}]", ctx).pass); assertFalse(Script.matchJsonPath(MatchType.CONTAINS_ONLY, myJson, "$.foo", "[{bar: 1, baz: 'a'}, {bar: 2, baz: 'b'}]", ctx).pass); assertTrue(Script.matchJsonPath(MatchType.EACH_EQUALS, myJson, "$.foo", "{bar:'#number', baz:'#string'}", ctx).pass); assertTrue(Script.matchJsonPath(MatchType.EACH_CONTAINS, myJson, "$.foo", "{bar:'#number'}", ctx).pass); assertTrue(Script.matchJsonPath(MatchType.EACH_CONTAINS, myJson, "$.foo", "{baz:'#string'}", ctx).pass); assertFalse(Script.matchJsonPath(MatchType.EACH_EQUALS, myJson, "$.foo", "{bar:'#? _ < 3', baz:'#string'}", ctx).pass); } @Test public void testMatchJsonObjectReturnedFromJs() { ScriptContext ctx = getContext(); Script.assign("fun", "function(){ return { foo: 'bar' } }", ctx); Script.assign("json", "{ foo: 'bar' }", ctx); Script.assign("expected", "fun()", ctx); assertTrue(Script.matchNamed("json", null, "expected", ctx).pass); assertTrue(Script.matchNamed("json", null, "fun()", ctx).pass); } @Test public void testMatchJsonArrayReturnedFromJs() { ScriptContext ctx = getContext(); Script.assign("fun", "function(){ return [ 'foo', 'bar', 'baz' ] }", ctx); Script.assign("json", "[ 'foo', 'bar', 'baz' ]", ctx); Script.assign("expected", "fun()", ctx); assertTrue(Script.matchNamed("json", null, "expected", ctx).pass); assertTrue(Script.matchNamed("json", null, "fun()", ctx).pass); } @Test public void testMatchJsonPathOnResponse() { DocumentContext doc = JsonPath.parse("{ foo: 'bar' }"); ScriptContext ctx = getContext(); ctx.vars.put("response", doc); assertTrue(Script.matchNamed("$", null, "{ foo: 'bar' }", ctx).pass); assertTrue(Script.matchNamed("$.foo", null, "'bar'", ctx).pass); } private final String ACTUAL = "{\"id\":{\"domain\":\"ACS\",\"type\":\"entityId\",\"value\":\"bef90f66-bb57-4fea-83aa-a0acc42b0426\"},\"primaryId\":\"bef90f66-bb57-4fea-83aa-a0acc42b0426\",\"created\":{\"on\":\"2016-02-28T05:56:48.485+0000\"},\"lastUpdated\":{\"on\":\"2016-02-28T05:56:49.038+0000\"},\"organization\":{\"id\":{\"domain\":\"ACS\",\"type\":\"entityId\",\"value\":\"631fafe9-8822-4c82-b4a4-8735b202c16c\"},\"created\":{\"on\":\"2016-02-28T05:56:48.486+0000\"},\"lastUpdated\":{\"on\":\"2016-02-28T05:56:49.038+0000\"}},\"clientState\":\"ACTIVE\"}"; private final String EXPECTED = "{\"id\":{\"domain\":\"ACS\",\"type\":\"entityId\",\"value\":\"#ignore\"},\"primaryId\":\"#ignore\",\"created\":{\"on\":\"#ignore\"},\"lastUpdated\":{\"on\":\"#ignore\"},\"organization\":{\"id\":{\"domain\":\"ACS\",\"type\":\"entityId\",\"value\":\"#ignore\"},\"created\":{\"on\":\"#ignore\"},\"lastUpdated\":{\"on\":\"#ignore\"}},\"clientState\":\"ACTIVE\"}"; @Test public void testMatchTwoJsonDocsWithIgnores() { DocumentContext actual = JsonPath.parse(ACTUAL); DocumentContext expected = JsonPath.parse(EXPECTED); ScriptContext ctx = getContext(); ctx.vars.put("actual", actual); ctx.vars.put("expected", expected); ScriptValue act = ctx.vars.get("actual"); assertTrue(Script.matchJsonPath(MatchType.EQUALS, act, "$", "expected", ctx).pass); } @Test public void testMatchXmlPath() { ScriptContext ctx = getContext(); Document doc = XmlUtils.toXmlDoc("<root><foo>bar</foo><hello>world</hello></root>"); ctx.vars.put("myXml", doc); ScriptValue myXml = ctx.vars.get("myXml"); assertTrue(Script.matchXmlPath(MatchType.EQUALS, myXml, "/root/foo", "'bar'", ctx).pass); assertTrue(Script.matchXmlPath(MatchType.EQUALS, myXml, "/root/hello", "'world'", ctx).pass); } @Test public void testAssignAndMatchXml() { ScriptContext ctx = getContext(); Script.assign("myXml", "<root><foo>bar</foo></root>", ctx); Script.assign("myStr", "myXml/root/foo", ctx); assertTrue(Script.assertBoolean("myStr == 'bar'", ctx).pass); } @Test public void testXmlShortCutsForResponse() { ScriptContext ctx = getContext(); Script.assign("response", "<root><foo>bar</foo></root>", ctx); assertTrue(Script.matchNamed("response", "/", "<root><foo>bar</foo></root>", ctx).pass); assertTrue(Script.matchNamed("response/", null, "<root><foo>bar</foo></root>", ctx).pass); assertTrue(Script.matchNamed("response", null, "<root><foo>bar</foo></root>", ctx).pass); assertTrue(Script.matchNamed("/", null, "<root><foo>bar</foo></root>", ctx).pass); } @Test public void testMatchXmlButUsingJsonPath() { ScriptContext ctx = getContext(); Document doc = XmlUtils.toXmlDoc("<cat><name>Billie</name><scores><score>2</score><score>5</score></scores></cat>"); ctx.vars.put("myXml", doc); assertTrue(Script.matchNamed("myXml/cat/scores/score[2]", null, "'5'", ctx).pass); // lenient primitive equality check assertTrue(Script.matchNamed("myXml/cat/scores/score[2]", null, "5", ctx).pass); // using json path for xml ! assertTrue(Script.matchNamed("myXml.cat.scores.score[1]", null, "'5'", ctx).pass); // lenient primitive equality check assertTrue(Script.matchNamed("myXml.cat.scores.score[1]", null, "5", ctx).pass); } @Test public void testMatchXmlRepeatedElements() { ScriptContext ctx = getContext(); String xml = "<foo><bar>baz1</bar><bar>baz2</bar></foo>"; Document doc = XmlUtils.toXmlDoc(xml); ctx.vars.put(ScriptValueMap.VAR_RESPONSE, doc); ScriptValue response = ctx.vars.get(ScriptValueMap.VAR_RESPONSE); assertTrue(Script.matchXmlPath(MatchType.EQUALS, response, "/", "<foo><bar>baz1</bar><bar>baz2</bar></foo>", ctx).pass); assertTrue(Script.matchXmlPath(MatchType.EQUALS, response, "/foo/bar[2]", "'baz2'", ctx).pass); assertTrue(Script.matchXmlPath(MatchType.EQUALS, response, "/foo/bar[1]", "'baz1'", ctx).pass); } @Test public void testMatchXmlAttributeErrorReporting() { ScriptContext ctx = getContext(); Script.assign("xml", "<hello foo=\"bar\">world</hello>", ctx); ScriptValue xml = ctx.vars.get("xml"); assertTrue(Script.matchXmlPath(MatchType.EQUALS, xml, "/", "<hello foo=\"bar\">world</hello>", ctx).pass); AssertionResult ar = Script.matchXmlPath(MatchType.EQUALS, xml, "/", "<hello foo=\"baz\">world</hello>", ctx); assertFalse(ar.pass); assertTrue(ar.message.contains("/hello/@foo")); } @Test public void testAssigningAndCallingFunctionThatUpdatesVars() { ScriptContext ctx = getContext(); Script.assign("foo", "function(){ return { bar: 'baz' } }", ctx); ScriptValue testFoo = ctx.vars.get("foo"); assertEquals(ScriptValue.Type.JS_FUNCTION, testFoo.getType()); Script.callAndUpdateVarsIfMapReturned("foo", null, ctx); ScriptValue testBar = ctx.vars.get("bar"); assertEquals("baz", testBar.getValue()); } @Test public void testAssigningAndCallingFunctionThatCanBeUsedToAssignVariable() { ScriptContext ctx = getContext(); Script.assign("foo", "function(){ return 'world' }", ctx); Script.assign("hello", "call foo", ctx); ScriptValue hello = ctx.vars.get("hello"); assertEquals("world", hello.getValue()); } @Test public void testAssigningAndCallingFunctionWithArgumentsThatCanBeUsedToAssignVariable() { ScriptContext ctx = getContext(); Script.assign("foo", "function(pre){ return pre + ' world' }", ctx); Script.assign("hello", "call foo 'hello'", ctx); ScriptValue hello = ctx.vars.get("hello"); assertEquals("hello world", hello.getValue()); } @Test public void testCallingFunctionThatTakesPrimitiveArgument() { ScriptContext ctx = getContext(); Script.assign("foo", "function(a){ return { bar: a } }", ctx); ScriptValue testFoo = ctx.vars.get("foo"); assertEquals(ScriptValue.Type.JS_FUNCTION, testFoo.getType()); Script.callAndUpdateVarsIfMapReturned("foo", "'hello'", ctx); ScriptValue testBar = ctx.vars.get("bar"); assertEquals("hello", testBar.getValue()); } @Test public void testCallingFunctionThatTakesJsonArgument() { ScriptContext ctx = getContext(); Script.assign("foo", "function(a){ return { bar: a.hello } }", ctx); ScriptValue testFoo = ctx.vars.get("foo"); assertEquals(ScriptValue.Type.JS_FUNCTION, testFoo.getType()); Script.callAndUpdateVarsIfMapReturned("foo", "{ hello: 'world' }", ctx); ScriptValue testBar = ctx.vars.get("bar"); assertEquals("world", testBar.getValue()); } @Test public void testCallingFunctionWithJsonArray() { ScriptContext ctx = getContext(); Script.assign("foo", "function(a){ return a[0] }", ctx); Script.assign("bar", "call foo ['hello']", ctx); ScriptValue bar = ctx.vars.get("bar"); assertEquals("hello", bar.getValue()); } @Test public void testCallingFunctionWithJavaList() { ScriptContext ctx = getContext(); Script.assign("foo", "function(a){ return a[0] }", ctx); Script.assign("bar", "['hello']", ctx); Script.assign("baz", "call foo bar", ctx); ScriptValue baz = ctx.vars.get("baz"); assertEquals("hello", baz.getValue()); } @Test public void testCallingFunctionThatUsesJsonPath() { ScriptContext ctx = getContext(); Script.assign("foo", "{ bar: [{baz: 1}, {baz: 2}, {baz: 3}]}", ctx); Script.assign("fun", "function(){ return karate.get('foo.bar[*].baz') }", ctx); Script.assign("res", "call fun", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res", null, "[1, 2, 3]", ctx).pass); // 'normal' variable name Script.assign("fun", "function(){ return karate.get('foo') }", ctx); Script.assign("res", "call fun", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res", null, "{ bar: [{baz: 1}, {baz: 2}, {baz: 3}]}", ctx).pass); } @Test public void testCallingFunctionWithJsonArrayReturnedFromAnotherFunction() { ScriptContext ctx = getContext(); Script.assign("fun1", "function(){ return [1, 2, 3] }", ctx); Script.assign("res1", "call fun1", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res1", null, "[1, 2, 3]", ctx).pass); Script.assign("fun2", "function(arg){ return arg.length }", ctx); Script.assign("res2", "call fun2 res1", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res2", null, "3", ctx).pass); } @Test public void testCallingFunctionWithJsonReturnedFromAnotherFunction() { ScriptContext ctx = getContext(); Script.assign("fun1", "function(){ return { foo: 'bar' } }", ctx); Script.assign("res1", "call fun1", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res1", null, "{ foo: 'bar' }", ctx).pass); Script.assign("fun2", "function(arg){ return arg.foo }", ctx); Script.assign("res2", "call fun2 res1", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res2", null, "'bar'", ctx).pass); } @Test public void testCallingFunctionWithStringReturnedFromAnotherFunction() { ScriptContext ctx = getContext(); Script.assign("fun1", "function(){ return 'foo' }", ctx); Script.assign("res1", "call fun1", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res1", null, "'foo'", ctx).pass); Script.assign("fun2", "function(arg){ return arg + 'bar' }", ctx); Script.assign("res2", "call fun2 res1", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res2", null, "'foobar'", ctx).pass); } @Test public void testParsingVariableAndJsonPath() { assertEquals(Pair.of("foo", "$"), Script.parseVariableAndPath("foo")); assertEquals(Pair.of("foo", "$.bar"), Script.parseVariableAndPath("foo.bar")); assertEquals(Pair.of("foo", "$[0]"), Script.parseVariableAndPath("foo[0]")); assertEquals(Pair.of("foo", "$[0].bar"), Script.parseVariableAndPath("foo[0].bar")); assertEquals(Pair.of("foo", "/bar"), Script.parseVariableAndPath("foo/bar")); assertEquals(Pair.of("foo", "/"), Script.parseVariableAndPath("foo/")); assertEquals(Pair.of("foo", "/bar/baz[1]/ban"), Script.parseVariableAndPath("foo/bar/baz[1]/ban")); } @Test public void testSettingPathOnVariable() { ScriptContext ctx = getContext(); Document xml = XmlUtils.toXmlDoc("<root><foo>bar</foo></root>"); ctx.vars.put("xml", xml); DocumentContext json = JsonUtils.toJsonDoc("{ foo: 'bar' }"); ctx.vars.put("json", json); Script.setValueByPath("xml", "/root/foo", "'hello'", ctx); assertEquals("hello", Script.evalXmlPathOnVarByName("xml", "/root/foo", ctx).getValue()); Script.setValueByPath("xml/root/foo", null, "'world'", ctx); assertEquals("world", Script.evalXmlPathOnVarByName("xml", "/root/foo", ctx).getValue()); Script.setValueByPath("json", "$.foo", "'hello'", ctx); assertEquals("hello", Script.evalJsonPathOnVarByName("json", "$.foo", ctx).getValue()); Script.setValueByPath("json.foo", null, "'world'", ctx); assertEquals("world", Script.evalJsonPathOnVarByName("json", "$.foo", ctx).getValue()); } @Test public void testDefaultValidators() { ScriptContext ctx = getContext(); DocumentContext doc = JsonUtils.toJsonDoc("{ foo: 'bar' }"); ctx.vars.put("json", doc); assertTrue(Script.matchNamed("json", "$", "{ foo: '#ignore' }", ctx).pass); assertTrue(Script.matchNamed("json", "$", "{ foo: '#notnull' }", ctx).pass); assertFalse(Script.matchNamed("json", "$", "{ foo: '#null' }", ctx).pass); assertTrue(Script.matchNamed("json", "$", "{ foo: '#regex^bar' }", ctx).pass); assertTrue(Script.matchNamed("json", "$", "{ foo: '#regex ^bar' }", ctx).pass); assertFalse(Script.matchNamed("json", "$", "{ foo: '#regex^baX' }", ctx).pass); assertFalse(Script.matchNamed("json", "$", "{ foo: '#regex ^baX' }", ctx).pass); doc = JsonUtils.toJsonDoc("{ foo: null }"); ctx.vars.put("json", doc); assertTrue(Script.matchNamed("json", "$", "{ foo: '#ignore' }", ctx).pass); assertTrue(Script.matchNamed("json", "$", "{ foo: '#null' }", ctx).pass); assertFalse(Script.matchNamed("json", "$", "{ foo: '#notnull' }", ctx).pass); doc = JsonUtils.toJsonDoc("{ foo: 'a9f7a56b-8d5c-455c-9d13-808461d17b91' }"); ctx.vars.put("json", doc); assertTrue(Script.matchNamed("json", "$", "{ foo: '#uuid' }", ctx).pass); doc = JsonUtils.toJsonDoc("{ foo: 'a9f7a56b-8d5c-455c-9d13' }"); ctx.vars.put("json", doc); assertFalse(Script.matchNamed("json", "$", "{ foo: '#uuid' }", ctx).pass); doc = JsonUtils.toJsonDoc("{ foo: 5 }"); ctx.vars.put("json", doc); ctx.vars.put("min", 4); ctx.vars.put("max", 6); assertTrue(Script.matchNamed("json", "$", "{ foo: '#number' }", ctx).pass); assertTrue(Script.matchNamed("json", "$", "{ foo: '#? _ == 5' }", ctx).pass); assertTrue(Script.matchNamed("json", "$", "{ foo: '#? _ < 6' }", ctx).pass); assertTrue(Script.matchNamed("json", "$", "{ foo: '#? _ > 4' }", ctx).pass); assertTrue(Script.matchNamed("json", "$", "{ foo: '#? _ > 4 && _ < 6' }", ctx).pass); assertTrue(Script.matchNamed("json", "$", "{ foo: '#? _ > min && _ < max' }", ctx).pass); } @Test public void testSimpleJsonMatch() { ScriptContext ctx = getContext(); DocumentContext doc = JsonUtils.toJsonDoc("{ foo: 'bar' }"); ctx.vars.put("json", doc); assertFalse(Script.matchNamed("json", "$", "{ }", ctx).pass); } @Test public void testAssignJsonChunkObjectAndUse() { ScriptContext ctx = getContext(); //=== Script.assign("parent", "{ foo: 'bar', 'ban': { a: 1 } }", ctx); Script.assign("child", "parent.ban", ctx); assertTrue(Script.matchNamed("child.a", null, "1", ctx).pass); //=== Script.assign("parent", "{ foo: 'bar', 'ban': { a: [1, 2, 3] } }", ctx); Script.assign("child", "parent.ban", ctx); assertTrue(Script.matchNamed("child.a[1]", null, "2", ctx).pass); } @Test public void testAssignJsonChunkListAndUse() { ScriptContext ctx = getContext(); //=== Script.assign("parent", "{ foo: { bar: [{ baz: 1}, {baz: 2}, {baz: 3}] }}", ctx); Script.assign("child", "parent.foo", ctx); assertTrue(Script.matchNamed("child", null, "{ bar: [{ baz: 1}, {baz: 2}, {baz: 3}]}", ctx).pass); assertTrue(Script.matchNamed("child.bar", null, "[{ baz: 1}, {baz: 2}, {baz: 3}]", ctx).pass); assertTrue(Script.matchNamed("child.bar[0]", null, "{ baz: 1}", ctx).pass); } @Test public void testEvalUrl() { ScriptContext ctx = getContext(); String url = "'http://localhost:8089/v1/cats'"; assertEquals("http://localhost:8089/v1/cats", Script.eval(url, ctx).getAsString()); } @Test public void testEvalParamWithDot() { ScriptContext ctx = getContext(); String param = "'ACS.Itself'"; assertEquals("ACS.Itself", Script.eval(param, ctx).getAsString()); } @Test public void testMatchJsonObjectContains() { ScriptContext ctx = getContext(); Script.assign("json", "{ foo: 'bar', baz: [1, 2, 3] }", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "json", null, "{ baz: [1, 2, 3], foo: 'bar' }", ctx).pass); assertTrue(Script.matchNamed(MatchType.CONTAINS, "json", null, "{ baz: [1, 2, 3] }", ctx).pass); assertTrue(Script.matchNamed(MatchType.CONTAINS, "json", null, "{ foo: 'bar' }", ctx).pass); } @Test public void testMatchJsonArrayContains() { ScriptContext ctx = getContext(); Script.assign("foo", "{ bar: [1, 2, 3] }", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "foo.bar", null, "[1 ,2, 3]", ctx).pass); assertTrue(Script.matchNamed(MatchType.CONTAINS, "foo.bar", null, "[1]", ctx).pass); } @Test public void testMatchContainsForSingleElements() { ScriptContext ctx = getContext(); Script.assign("foo", "{ bar: [1, 2, 3] }", ctx); assertTrue(Script.matchNamed(MatchType.CONTAINS, "foo.bar", null, "1", ctx).pass); Script.assign("json", "[{ foo: 1 }, { foo: 2 }, { foo: 3 }]", ctx); assertTrue(Script.matchNamed(MatchType.CONTAINS, "json", null, "{ foo: 1 }", ctx).pass); Script.assign("json", "[{ foo: 1 }]", ctx); assertTrue(Script.matchNamed(MatchType.CONTAINS_ONLY, "json", null, "{ foo: 1 }", ctx).pass); } @Test public void testMatchArrayErrorReporting() { ScriptContext ctx = getContext(); Script.assign("json", "[{ foo: 1 }, { foo: 2 }, { foo: 3 }]", ctx); AssertionResult ar = Script.matchNamed(MatchType.EQUALS, "json", null, "[{ foo: 1 }, { foo: 2 }, { foo: 4 }]", ctx); assertFalse(ar.pass); assertTrue(ar.message.contains("$[2].foo")); ar = Script.matchNamed(MatchType.CONTAINS, "json", null, "[{ foo: 1 }, { foo: 2 }, { foo: 4 }]", ctx); assertFalse(ar.pass); assertTrue(ar.message.contains("$[*]")); ar = Script.matchNamed(MatchType.CONTAINS_ONLY, "json", null, "[{ foo: 3 }, { foo: 2 }, { foo: 0 }]", ctx); assertFalse(ar.pass); assertTrue(ar.message.contains("$[*]")); ar = Script.matchNamed(MatchType.CONTAINS_ONLY, "json", null, "[{ foo: 3 }, { foo: 2 }, { foo: 1 }]", ctx); assertTrue(ar.pass); ar = Script.matchNamed(MatchType.CONTAINS_ONLY, "json", null, "[{ foo: 3 }, { foo: 2 }]", ctx); assertFalse(ar.pass); assertTrue(ar.message.contains("not the same size")); } @Test public void testMatchStringContains() { ScriptContext ctx = getContext(); Script.assign("foo", "'hello world'", ctx); assertTrue(Script.matchNamed(MatchType.CONTAINS, "foo", null, "'hello'", ctx).pass); assertFalse(Script.matchNamed(MatchType.CONTAINS, "foo", null, "'zoo'", ctx).pass); } @Test public void testKarateEnvAccessFromScript() { String featureDir = FileUtils.getDirContaining(getClass()).getPath(); ScriptEnv env = ScriptEnv.init("baz", new File(featureDir)); ScriptContext ctx = new ScriptContext(env, null, null); Script.assign("foo", "function(){ return karate.env }", ctx); Script.assign("bar", "call foo", ctx); ScriptValue bar = ctx.vars.get("bar"); assertEquals("baz", bar.getValue()); // null env = ScriptEnv.init(null, new File(featureDir)); ctx = new ScriptContext(env, null, null); Script.assign("foo", "function(){ return karate.env }", ctx); Script.assign("bar", "call foo", ctx); bar = ctx.vars.get("bar"); assertNull(bar.getValue()); } @Test public void testCallingFeatureWithNoArgument() { ScriptContext ctx = getContext(); Script.assign("foo", "call read('test.feature')", ctx); ScriptValue a = Script.evalJsonPathOnVarByName("foo", "$.a", ctx); assertEquals(1, a.getValue()); ScriptValue b = Script.evalJsonPathOnVarByName("foo", "$.b", ctx); assertEquals(2, b.getValue()); } @Test public void testCallingFeatureWithVarOverrides() { ScriptContext ctx = getContext(); Script.assign("foo", "call read('test.feature') { c: 3 }", ctx); ScriptValue a = Script.evalJsonPathOnVarByName("foo", "$.a", ctx); assertEquals(1, a.getValue()); ScriptValue b = Script.evalJsonPathOnVarByName("foo", "$.b", ctx); assertEquals(2, b.getValue()); ScriptValue c = Script.evalJsonPathOnVarByName("foo", "$.c", ctx); assertEquals(3, c.getValue()); } @Test public void testCallingFeatureWithVarOverrideFromVariable() { ScriptContext ctx = getContext(); Script.assign("bar", "{ c: 3 }", ctx); Script.assign("foo", "call read('test.feature') bar", ctx); ScriptValue a = Script.evalJsonPathOnVarByName("foo", "$.a", ctx); assertEquals(1, a.getValue()); ScriptValue b = Script.evalJsonPathOnVarByName("foo", "$.b", ctx); assertEquals(2, b.getValue()); ScriptValue c = Script.evalJsonPathOnVarByName("foo", "$.c", ctx); assertEquals(3, c.getValue()); } @Test public void testCallingFeatureWithList() { ScriptContext ctx = getContext(); Script.assign("foo", "call read('test.feature') [{c: 100}, {c: 200}, {c: 300}]", ctx); ScriptValue c0 = Script.evalJsonPathOnVarByName("foo", "$[0].c", ctx); assertEquals(100, c0.getValue()); ScriptValue c1 = Script.evalJsonPathOnVarByName("foo", "$[1].c", ctx); assertEquals(200, c1.getValue()); ScriptValue c2 = Script.evalJsonPathOnVarByName("foo", "$[2].c", ctx); assertEquals(300, c2.getValue()); } @Test public void testCallingFeatureWithJsonCreatedByJavaScript() { ScriptContext ctx = getContext(); Script.assign("fun", "function(){ return { c: 100} }", ctx); Script.assign("res", "call fun", ctx); Script.assign("foo", "call read('test.feature') res", ctx); ScriptValue c = Script.evalJsonPathOnVarByName("foo", "$.c", ctx); assertEquals(100, c.getValue()); } @Test public void testCallingFeatureWithJsonArrayCreatedByJavaScript() { ScriptContext ctx = getContext(); Script.assign("fun", "function(){ return [{ c: 100}] }", ctx); Script.assign("res", "call fun", ctx); Script.assign("foo", "call read('test.feature') res", ctx); ScriptValue c = Script.evalJsonPathOnVarByName("foo", "$[0].c", ctx); assertEquals(100, c.getValue()); } @Test public void testGetSyntaxForJson() { ScriptContext ctx = getContext(); Script.assign("foo", "[{baz: 1}, {baz: 2}, {baz: 3}]", ctx); Script.assign("nums", "get foo[*].baz", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "nums", null, "[1, 2, 3]", ctx).pass); Script.assign("foo", "{ bar: [{baz: 1}, {baz: 2}, {baz: 3}]}", ctx); Script.assign("nums", "get foo.bar[*].baz", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "nums", null, "[1, 2, 3]", ctx).pass); Script.assign("nums", "get foo $.bar[*].baz", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "nums", null, "[1, 2, 3]", ctx).pass); } @Test public void testGetSyntaxForXml() { ScriptContext ctx = getContext(); Script.assign("foo", "<records>\n <record>a</record>\n <record>b</record>\n <record>c</record>\n</records>", ctx); Script.assign("count", "get foo count(//record)", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "count", null, "3", ctx).pass); assertTrue(Script.matchNamed(MatchType.EQUALS, "count", null, "3", ctx).pass); } @Test public void testFromJsKarateCallFeatureWithNoArg() { ScriptContext ctx = getContext(); Script.assign("fun", "function(){ return karate.call('test.feature') }", ctx); Script.assign("res", "fun()", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res.a", null, "1", ctx).pass); assertTrue(Script.matchNamed(MatchType.EQUALS, "res.b", null, "2", ctx).pass); } @Test public void testFromJsKarateCallFeatureWithJsonArg() { ScriptContext ctx = getContext(); Script.assign("fun", "function(){ return karate.call('test.feature', {c: 3}) }", ctx); Script.assign("res", "fun()", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res.a", null, "1", ctx).pass); assertTrue(Script.matchNamed(MatchType.EQUALS, "res.b", null, "2", ctx).pass); assertTrue(Script.matchNamed(MatchType.EQUALS, "res.c", null, "3", ctx).pass); } @Test public void testFromJsKarateGetForNonExistentVariable() { ScriptContext ctx = getContext(); Script.assign("fun", "function(){ var foo = karate.get('foo'); return foo ? true : false }", ctx); Script.assign("res", "fun()", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res", null, "false", ctx).pass); } @Test public void testFromJsKarateGetForJsonArrayVariable() { ScriptContext ctx = getContext(); Script.assign("fun", "function(){ return [1, 2, 3] }", ctx); Script.assign("res", "call fun", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res", null, "[1, 2, 3]", ctx).pass); } @Test public void testFromJsKarateGetForJsonObjectVariableAndCallFeatureAndJs() { ScriptContext ctx = getContext(); Script.assign("fun", "read('headers.js')", ctx); Script.assign("res", "call fun", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res", null, "{ foo: 'bar_someValue' }", ctx).pass); Script.assign("signin", "call read('signin.feature')", ctx); Script.assign("ticket", "signin.ticket", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "ticket", null, "{ foo: 'bar' }", ctx).pass); Script.assign("res", "call fun", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "res", null, "{ foo: 'bar_someValue', baz: 'ban' }", ctx).pass); } @Test public void testAssigningRawTextWhichOtherwiseConfusesKarate() { ScriptContext ctx = getContext(); try { Script.assign("foo", "{ not json }", ctx); fail("we expected this to fail"); } catch (InvalidJsonException e) { logger.debug("expected {}", e.getMessage()); } Script.assignText("foo", "{ not json }", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "foo", null, "'{ not json }'", ctx).pass); } @Test public void testBigDecimalsInJson() { ScriptContext ctx = getContext(); Script.assign("foo", "{ val: -1002.2000000000002 }", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "foo", null, "{ val: -1002.2000000000002 }", ctx).pass); assertFalse(Script.matchNamed(MatchType.EQUALS, "foo", null, "{ val: -1002.2000000000001 }", ctx).pass); assertFalse(Script.matchNamed(MatchType.EQUALS, "foo", null, "{ val: -1002.20 }", ctx).pass); Script.assign("foo", "{ val: -1002.20 }", ctx); assertFalse(Script.matchNamed(MatchType.EQUALS, "foo", null, "{ val: -1002.2000000000001 }", ctx).pass); assertTrue(Script.matchNamed(MatchType.EQUALS, "foo", null, "{ val: -1002.2000000000000 }", ctx).pass); Script.assign("foo", "{ val: -1002.2000000000001 }", ctx); assertFalse(Script.matchNamed(MatchType.EQUALS, "foo", null, "{ val: -1002.20 }", ctx).pass); Script.assign("foo", "{ val: -1002.2000000000000 }", ctx); assertTrue(Script.matchNamed(MatchType.EQUALS, "foo", null, "{ val: -1002.20 }", ctx).pass); } }