package com.sas.unravl.assertions; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.sas.unravl.ApiCall; import com.sas.unravl.UnRAVL; import com.sas.unravl.UnRAVLException; import com.sas.unravl.annotations.UnRAVLAssertionPlugin; import com.sas.unravl.generators.Text; import com.sas.unravl.util.Json; import java.io.IOException; /** * Asserts that the HTTP response body is a JSON body that matches the given * JSON specification. * * <pre> * { "json" : expected-json-body } * { "json" : "@file-or-url" } * </pre> * * In the first form the JSON body is coded directly in the UnRAVL script. In * the second form, the JSON is in an external file or URL. It is assumed to be * in UTF-8 encoding. * <p> * This body assertion operation performs a somewhat strict definition of * equality: arrays and JSON objects must match in cardinality and order. * Numbers are matched exactly. Whitespace is ignored. * <h3>Examples</h3> * * <pre> * { "json" : { "x" : 1, "y" : 2, "n" : "NC", "data" : [10, 99, 0.5] } } * </pre> * * this will assert that the HTTP response matches the JSON object with fields * x, y, n, and data, with values 1, 2, "NC", and the array [10, 99, 0.5], * respectively. * * @author David.Biesack@sas.com * */ @UnRAVLAssertionPlugin("json") public class JsonBodyAssertion extends BaseUnRAVLAssertion implements UnRAVLAssertion { @Override public void check(UnRAVL current, ObjectNode assertion, Stage when, ApiCall call) throws UnRAVLAssertionException, UnRAVLException { super.check(current, assertion, when, call); JsonNode expected = Json.firstFieldValue(assertion); ObjectMapper mapper = new ObjectMapper(); String content; try { content = current.expand(Text.utf8ToString(call.getResponseBody() .toByteArray())); JsonNode actual = mapper.readTree(content); JsonNode mapped = Json.expand(actual, current); expected = realize(expected, mapper); boolean same = mapped.equals(expected); if (!same) throw new UnRAVLAssertionException( "Response body does not match expected. Received:" + actual); } catch (JsonProcessingException e) { throw new UnRAVLException("Could not parse response body as JSON: " + e.getMessage(), e); } catch (IOException e) { throw new UnRAVLException("Could not parse response body as JSON: " + e.getMessage(), e); } // Following JSONAssert fails - I get java.lang.NoClassDefFoundError: // org/skyscreamer/jsonassert/JSONAssert // even though it is right here... // Bummer, JSONAssert uses org.json.JSONObject, not Jackson JsonNode // So I'll convert to string then compare with JSONAssert // String expected = expectedj.toString(); // String body = new String(current.responseBody().toByteArray()); // try { // // TODO: add additional fields to control the strictness, // // maybe to ignore some values? // JSONAssert.assertEquals(expected, body, false); // } catch (JSONException e) { // throw new UnravlAssertionException(e.getMessage(), e); // } } // If node is "@file-or-URL , read text from that file and parse as JSON private JsonNode realize(JsonNode expected, ObjectMapper mapper) throws IOException, UnRAVLException { JsonNode json = expected; if (expected.isTextual()) { String path = getScript().expand(expected.textValue()); String text = getScript() .expand(new Text(getScript(), path).text()); json = mapper.readTree(text); } return json; } }