2 * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.sal.restconf.impl.test;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertNull;
15 import static org.junit.Assert.assertTrue;
16 import static org.junit.Assert.fail;
17 import static org.mockito.Matchers.any;
18 import static org.mockito.Mockito.mock;
19 import static org.mockito.Mockito.reset;
20 import static org.mockito.Mockito.when;
21 import com.google.common.collect.ImmutableMap;
22 import com.google.common.collect.Maps;
23 import com.google.common.io.ByteStreams;
24 import com.google.gson.JsonArray;
25 import com.google.gson.JsonElement;
26 import com.google.gson.JsonParser;
27 import java.io.ByteArrayInputStream;
28 import java.io.ByteArrayOutputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.InputStreamReader;
32 import java.util.Arrays;
33 import java.util.Iterator;
34 import java.util.List;
36 import java.util.Map.Entry;
38 import java.util.regex.Matcher;
39 import java.util.regex.Pattern;
40 import javax.ws.rs.core.Application;
41 import javax.ws.rs.core.MediaType;
42 import javax.ws.rs.core.Response;
43 import javax.ws.rs.core.Response.Status;
44 import javax.ws.rs.core.UriInfo;
45 import javax.xml.namespace.NamespaceContext;
46 import javax.xml.parsers.DocumentBuilderFactory;
47 import javax.xml.xpath.XPath;
48 import javax.xml.xpath.XPathConstants;
49 import javax.xml.xpath.XPathExpression;
50 import javax.xml.xpath.XPathFactory;
51 import org.glassfish.jersey.server.ResourceConfig;
52 import org.glassfish.jersey.test.JerseyTest;
53 import org.junit.Before;
54 import org.junit.BeforeClass;
55 import org.junit.Ignore;
56 import org.junit.Test;
57 import org.opendaylight.controller.sal.rest.api.Draft02;
58 import org.opendaylight.controller.sal.rest.api.RestconfService;
59 import org.opendaylight.controller.sal.rest.impl.JsonNormalizedNodeBodyReader;
60 import org.opendaylight.controller.sal.rest.impl.NormalizedNodeJsonBodyWriter;
61 import org.opendaylight.controller.sal.rest.impl.NormalizedNodeXmlBodyWriter;
62 import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
63 import org.opendaylight.controller.sal.rest.impl.XmlNormalizedNodeBodyReader;
64 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
65 import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
66 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
67 import org.opendaylight.controller.sal.restconf.impl.RestconfError;
68 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
69 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
70 import org.w3c.dom.Document;
71 import org.w3c.dom.Element;
72 import org.w3c.dom.Node;
73 import org.w3c.dom.NodeList;
76 * Unit tests for RestconfDocumentedExceptionMapper.
78 * @author Thomas Pantelis
80 public class RestconfDocumentedExceptionMapperTest extends JerseyTest {
82 interface ErrorInfoVerifier {
83 void verifyXML(Node errorInfoNode);
85 void verifyJson(JsonElement errorInfoElement);
88 static class ComplexErrorInfoVerifier implements ErrorInfoVerifier {
90 Map<String, String> expErrorInfo;
92 public ComplexErrorInfoVerifier(final Map<String, String> expErrorInfo) {
93 this.expErrorInfo = expErrorInfo;
97 public void verifyXML(final Node errorInfoNode) {
99 final Map<String, String> mutableExpMap = Maps.newHashMap(expErrorInfo);
100 final NodeList childNodes = errorInfoNode.getChildNodes();
101 for (int i = 0; i < childNodes.getLength(); i++) {
102 final Node child = childNodes.item(i);
103 if (child instanceof Element) {
104 final String expValue = mutableExpMap.remove(child.getNodeName());
105 assertNotNull("Found unexpected \"error-info\" child node: " + child.getNodeName(), expValue);
106 assertEquals("Text content for \"error-info\" child node " + child.getNodeName(), expValue,
107 child.getTextContent());
111 if (!mutableExpMap.isEmpty()) {
112 fail("Missing \"error-info\" child nodes: " + mutableExpMap);
117 public void verifyJson(final JsonElement errorInfoElement) {
119 assertTrue("\"error-info\" Json element is not an Object", errorInfoElement.isJsonObject());
121 final Map<String, String> actualErrorInfo = Maps.newHashMap();
122 for (final Entry<String, JsonElement> entry : errorInfoElement.getAsJsonObject().entrySet()) {
123 final String leafName = entry.getKey();
124 final JsonElement leafElement = entry.getValue();
125 actualErrorInfo.put(leafName, leafElement.getAsString());
128 final Map<String, String> mutableExpMap = Maps.newHashMap(expErrorInfo);
129 for (final Entry<String, String> actual : actualErrorInfo.entrySet()) {
130 final String expValue = mutableExpMap.remove(actual.getKey());
131 assertNotNull("Found unexpected \"error-info\" child node: " + actual.getKey(), expValue);
132 assertEquals("Text content for \"error-info\" child node " + actual.getKey(), expValue,
136 if (!mutableExpMap.isEmpty()) {
137 fail("Missing \"error-info\" child nodes: " + mutableExpMap);
142 static class SimpleErrorInfoVerifier implements ErrorInfoVerifier {
144 String expTextContent;
146 public SimpleErrorInfoVerifier(final String expErrorInfo) {
147 expTextContent = expErrorInfo;
150 void verifyContent(final String actualContent) {
151 assertNotNull("Actual \"error-info\" text content is null", actualContent);
152 assertTrue("", actualContent.contains(expTextContent));
156 public void verifyXML(final Node errorInfoNode) {
157 verifyContent(errorInfoNode.getTextContent());
161 public void verifyJson(final JsonElement errorInfoElement) {
162 verifyContent(errorInfoElement.getAsString());
166 static RestconfService mockRestConf = mock(RestconfService.class);
168 static XPath XPATH = XPathFactory.newInstance().newXPath();
169 static XPathExpression ERROR_LIST;
170 static XPathExpression ERROR_TYPE;
171 static XPathExpression ERROR_TAG;
172 static XPathExpression ERROR_MESSAGE;
173 static XPathExpression ERROR_APP_TAG;
174 static XPathExpression ERROR_INFO;
177 public static void init() throws Exception {
178 ControllerContext.getInstance().setGlobalSchema(TestUtils.loadSchemaContext("/modules"));
180 final NamespaceContext nsContext = new NamespaceContext() {
182 public Iterator<?> getPrefixes(final String namespaceURI) {
187 public String getPrefix(final String namespaceURI) {
192 public String getNamespaceURI(final String prefix) {
193 return "ietf-restconf".equals(prefix) ? Draft02.RestConfModule.NAMESPACE : null;
197 XPATH.setNamespaceContext(nsContext);
198 ERROR_LIST = XPATH.compile("ietf-restconf:errors/ietf-restconf:error");
199 ERROR_TYPE = XPATH.compile("ietf-restconf:error-type");
200 ERROR_TAG = XPATH.compile("ietf-restconf:error-tag");
201 ERROR_MESSAGE = XPATH.compile("ietf-restconf:error-message");
202 ERROR_APP_TAG = XPATH.compile("ietf-restconf:error-app-tag");
203 ERROR_INFO = XPATH.compile("ietf-restconf:error-info");
208 public void setUp() throws Exception {
214 protected Application configure() {
215 ResourceConfig resourceConfig = new ResourceConfig();
216 resourceConfig = resourceConfig.registerInstances(mockRestConf, new XmlNormalizedNodeBodyReader(),
217 new JsonNormalizedNodeBodyReader(), new NormalizedNodeJsonBodyWriter(), new NormalizedNodeXmlBodyWriter());
218 resourceConfig.registerClasses(RestconfDocumentedExceptionMapper.class);
219 return resourceConfig;
222 void stageMockEx(final RestconfDocumentedException ex) {
224 when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenThrow(ex);
227 void testJsonResponse(final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
228 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
229 final ErrorInfoVerifier errorInfoVerifier) throws Exception {
233 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
235 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, expStatus);
237 verifyJsonResponseBody(stream, expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, errorInfoVerifier);
241 public void testToJsonResponseWithMessageOnly() throws Exception {
243 testJsonResponse(new RestconfDocumentedException("mock error"), Status.INTERNAL_SERVER_ERROR,
244 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
247 // To test verification code
251 // " error-tag : \"operation-failed\"" +
252 // " ,error-type : \"application\"" +
253 // " ,error-message : \"An error occurred\"" +
254 // " ,error-info : {" +
255 // " session-id: \"123\"" +
256 // " ,address: \"1.2.3.4\"" +
262 // verifyJsonResponseBody( new java.io.StringBufferInputStream(json ),
263 // ErrorType.APPLICATION,
264 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
265 // com.google.common.collect.ImmutableMap.of( "session-id", "123",
266 // "address", "1.2.3.4" ) );
270 public void testToJsonResponseWithInUseErrorTag() throws Exception {
272 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.IN_USE),
273 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.IN_USE, "mock error", null, null);
277 public void testToJsonResponseWithInvalidValueErrorTag() throws Exception {
279 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.RPC, ErrorTag.INVALID_VALUE),
280 Status.BAD_REQUEST, ErrorType.RPC, ErrorTag.INVALID_VALUE, "mock error", null, null);
285 public void testToJsonResponseWithTooBigErrorTag() throws Exception {
287 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.TRANSPORT, ErrorTag.TOO_BIG),
288 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT, ErrorTag.TOO_BIG, "mock error", null, null);
293 public void testToJsonResponseWithMissingAttributeErrorTag() throws Exception {
295 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE),
296 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null);
300 public void testToJsonResponseWithBadAttributeErrorTag() throws Exception {
302 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE),
303 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error", null, null);
307 public void testToJsonResponseWithUnknownAttributeErrorTag() throws Exception {
309 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE),
310 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null);
314 public void testToJsonResponseWithBadElementErrorTag() throws Exception {
316 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT),
317 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null);
321 public void testToJsonResponseWithUnknownElementErrorTag() throws Exception {
323 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT),
324 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null);
328 public void testToJsonResponseWithUnknownNamespaceErrorTag() throws Exception {
330 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE),
331 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null);
335 public void testToJsonResponseWithMalformedMessageErrorTag() throws Exception {
337 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE),
338 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "mock error", null, null);
342 public void testToJsonResponseWithAccessDeniedErrorTag() throws Exception {
344 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED),
345 Status.FORBIDDEN, ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED, "mock error", null, null);
349 public void testToJsonResponseWithLockDeniedErrorTag() throws Exception {
351 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED),
352 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED, "mock error", null, null);
356 public void testToJsonResponseWithResourceDeniedErrorTag() throws Exception {
358 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED),
359 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED, "mock error", null, null);
363 public void testToJsonResponseWithRollbackFailedErrorTag() throws Exception {
365 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED),
366 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED, "mock error", null, null);
370 public void testToJsonResponseWithDataExistsErrorTag() throws Exception {
372 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS),
373 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, "mock error", null, null);
377 public void testToJsonResponseWithDataMissingErrorTag() throws Exception {
379 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING),
380 Status.NOT_FOUND, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
384 public void testToJsonResponseWithOperationNotSupportedErrorTag() throws Exception {
386 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL,
387 ErrorTag.OPERATION_NOT_SUPPORTED), Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
388 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null);
392 public void testToJsonResponseWithOperationFailedErrorTag() throws Exception {
394 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED),
395 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED, "mock error", null, null);
399 public void testToJsonResponseWithPartialOperationErrorTag() throws Exception {
401 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION),
402 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION, "mock error", null, null);
406 public void testToJsonResponseWithErrorAppTag() throws Exception {
408 testJsonResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
409 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag")), Status.BAD_REQUEST, ErrorType.APPLICATION,
410 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null);
414 @Ignore // FIXME : find why it return "error-type" RPC no expected APPLICATION
415 public void testToJsonResponseWithMultipleErrors() throws Exception {
417 final List<RestconfError> errorList = Arrays.asList(new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
418 "mock error1"), new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2"));
419 stageMockEx(new RestconfDocumentedException("mock", null, errorList));
421 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
423 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, Status.CONFLICT);
425 final JsonArray arrayElement = parseJsonErrorArrayElement(stream);
427 assertEquals("\"error\" Json array element length", 2, arrayElement.size());
429 verifyJsonErrorNode(arrayElement.get(0), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1", null, null);
431 verifyJsonErrorNode(arrayElement.get(1), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2", null, null);
435 @Ignore // TODO : we are not supported "error-info" element yet
436 public void testToJsonResponseWithErrorInfo() throws Exception {
438 final String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
439 testJsonResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
440 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", errorInfo)), Status.BAD_REQUEST,
441 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
442 new ComplexErrorInfoVerifier(ImmutableMap.of("session-id", "123", "address", "1.2.3.4")));
446 @Ignore //TODO : we are not supporting "error-info" yet
447 public void testToJsonResponseWithExceptionCause() throws Exception {
449 final Exception cause = new Exception("mock exception cause");
450 testJsonResponse(new RestconfDocumentedException("mock error", cause), Status.INTERNAL_SERVER_ERROR,
451 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null,
452 new SimpleErrorInfoVerifier(cause.getMessage()));
455 void testXMLResponse(final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
456 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
457 final ErrorInfoVerifier errorInfoVerifier) throws Exception {
460 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get();
462 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_XML, expStatus);
464 verifyXMLResponseBody(stream, expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, errorInfoVerifier);
468 public void testToXMLResponseWithMessageOnly() throws Exception {
470 testXMLResponse(new RestconfDocumentedException("mock error"), Status.INTERNAL_SERVER_ERROR,
471 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
473 // To test verification code
475 // "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">"+
477 // " <error-type>application</error-type>"+
478 // " <error-tag>operation-failed</error-tag>"+
479 // " <error-message>An error occurred</error-message>"+
481 // " <session-id>123</session-id>" +
482 // " <address>1.2.3.4</address>" +
483 // " </error-info>" +
487 // verifyXMLResponseBody( new java.io.StringBufferInputStream(xml),
488 // ErrorType.APPLICATION,
489 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
490 // com.google.common.collect.ImmutableMap.of( "session-id", "123",
491 // "address", "1.2.3.4" ) );
495 public void testToXMLResponseWithInUseErrorTag() throws Exception {
497 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.IN_USE),
498 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.IN_USE, "mock error", null, null);
502 public void testToXMLResponseWithInvalidValueErrorTag() throws Exception {
504 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.RPC, ErrorTag.INVALID_VALUE),
505 Status.BAD_REQUEST, ErrorType.RPC, ErrorTag.INVALID_VALUE, "mock error", null, null);
509 public void testToXMLResponseWithTooBigErrorTag() throws Exception {
511 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.TRANSPORT, ErrorTag.TOO_BIG),
512 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT, ErrorTag.TOO_BIG, "mock error", null, null);
516 public void testToXMLResponseWithMissingAttributeErrorTag() throws Exception {
518 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE),
519 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null);
523 public void testToXMLResponseWithBadAttributeErrorTag() throws Exception {
525 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE),
526 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error", null, null);
530 public void testToXMLResponseWithUnknownAttributeErrorTag() throws Exception {
532 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE),
533 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null);
537 public void testToXMLResponseWithBadElementErrorTag() throws Exception {
539 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT),
540 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null);
544 public void testToXMLResponseWithUnknownElementErrorTag() throws Exception {
546 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT),
547 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null);
551 public void testToXMLResponseWithUnknownNamespaceErrorTag() throws Exception {
553 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE),
554 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null);
558 public void testToXMLResponseWithMalformedMessageErrorTag() throws Exception {
560 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE),
561 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "mock error", null, null);
565 public void testToXMLResponseWithAccessDeniedErrorTag() throws Exception {
567 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED),
568 Status.FORBIDDEN, ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED, "mock error", null, null);
572 public void testToXMLResponseWithLockDeniedErrorTag() throws Exception {
574 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED),
575 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED, "mock error", null, null);
579 public void testToXMLResponseWithResourceDeniedErrorTag() throws Exception {
581 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED),
582 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED, "mock error", null, null);
586 public void testToXMLResponseWithRollbackFailedErrorTag() throws Exception {
588 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED),
589 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED, "mock error", null, null);
593 public void testToXMLResponseWithDataExistsErrorTag() throws Exception {
595 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS),
596 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, "mock error", null, null);
600 public void testToXMLResponseWithDataMissingErrorTag() throws Exception {
602 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING),
603 Status.NOT_FOUND, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
607 public void testToXMLResponseWithOperationNotSupportedErrorTag() throws Exception {
609 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL,
610 ErrorTag.OPERATION_NOT_SUPPORTED), Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
611 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null);
615 public void testToXMLResponseWithOperationFailedErrorTag() throws Exception {
617 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED),
618 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED, "mock error", null, null);
622 public void testToXMLResponseWithPartialOperationErrorTag() throws Exception {
624 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION),
625 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION, "mock error", null, null);
629 public void testToXMLResponseWithErrorAppTag() throws Exception {
631 testXMLResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
632 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag")), Status.BAD_REQUEST, ErrorType.APPLICATION,
633 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null);
637 @Ignore // TODO : we are not supporting "error-info" node yet
638 public void testToXMLResponseWithErrorInfo() throws Exception {
640 final String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
641 testXMLResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
642 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", errorInfo)), Status.BAD_REQUEST,
643 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
644 new ComplexErrorInfoVerifier(ImmutableMap.of("session-id", "123", "address", "1.2.3.4")));
648 @Ignore // TODO : we are not supporting "error-info" node yet
649 public void testToXMLResponseWithExceptionCause() throws Exception {
651 final Exception cause = new Exception("mock exception cause");
652 testXMLResponse(new RestconfDocumentedException("mock error", cause), Status.INTERNAL_SERVER_ERROR,
653 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null,
654 new SimpleErrorInfoVerifier(cause.getMessage()));
658 @Ignore // FIXME : find why it return error-type as RPC no APPLICATION
659 public void testToXMLResponseWithMultipleErrors() throws Exception {
661 final List<RestconfError> errorList = Arrays.asList(new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
662 "mock error1"), new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2"));
663 stageMockEx(new RestconfDocumentedException("mock", null, errorList));
665 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get();
667 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_XML, Status.CONFLICT);
669 final Document doc = parseXMLDocument(stream);
671 final NodeList children = getXMLErrorList(doc, 2);
673 verifyXMLErrorNode(children.item(0), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1", null, null);
675 verifyXMLErrorNode(children.item(1), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2", null, null);
679 public void testToResponseWithAcceptHeader() throws Exception {
681 stageMockEx(new RestconfDocumentedException("mock error"));
683 final Response resp = target("/operational/foo").request().header("Accept", MediaType.APPLICATION_JSON).get();
685 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, Status.INTERNAL_SERVER_ERROR);
687 verifyJsonResponseBody(stream, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
692 public void testToResponseWithStatusOnly() throws Exception {
694 // The StructuredDataToJsonProvider should throw a
695 // RestconfDocumentedException with no data
697 when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenReturn(
698 new NormalizedNodeContext(null, null));
700 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
702 verifyResponse(resp, MediaType.TEXT_PLAIN, Status.NOT_FOUND);
705 InputStream verifyResponse(final Response resp, final String expMediaType, final Status expStatus) {
706 assertEquals("getMediaType", MediaType.valueOf(expMediaType), resp.getMediaType());
707 assertEquals("getStatus", expStatus.getStatusCode(), resp.getStatus());
709 final Object entity = resp.getEntity();
710 assertEquals("Response entity", true, entity instanceof InputStream);
711 final InputStream stream = (InputStream) entity;
715 void verifyJsonResponseBody(final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
716 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
719 final JsonArray arrayElement = parseJsonErrorArrayElement(stream);
721 assertEquals("\"error\" Json array element length", 1, arrayElement.size());
723 verifyJsonErrorNode(arrayElement.get(0), expErrorType, expErrorTag, expErrorMessage, expErrorAppTag,
727 private JsonArray parseJsonErrorArrayElement(final InputStream stream) throws IOException {
728 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
729 ByteStreams.copy(stream, bos);
731 System.out.println("JSON: " + bos.toString());
733 final JsonParser parser = new JsonParser();
734 JsonElement rootElement;
737 rootElement = parser.parse(new InputStreamReader(new ByteArrayInputStream(bos.toByteArray())));
738 } catch (final Exception e) {
739 throw new IllegalArgumentException("Invalid JSON response:\n" + bos.toString(), e);
742 assertTrue("Root element of Json is not an Object", rootElement.isJsonObject());
744 final Set<Entry<String, JsonElement>> errorsEntrySet = rootElement.getAsJsonObject().entrySet();
745 assertEquals("Json Object element set count", 1, errorsEntrySet.size());
747 final Entry<String, JsonElement> errorsEntry = errorsEntrySet.iterator().next();
748 final JsonElement errorsElement = errorsEntry.getValue();
749 assertEquals("First Json element name", "errors", errorsEntry.getKey());
750 assertTrue("\"errors\" Json element is not an Object", errorsElement.isJsonObject());
752 final Set<Entry<String, JsonElement>> errorListEntrySet = errorsElement.getAsJsonObject().entrySet();
753 assertEquals("Root \"errors\" element child count", 1, errorListEntrySet.size());
755 final JsonElement errorListElement = errorListEntrySet.iterator().next().getValue();
756 assertEquals("\"errors\" child Json element name", "error", errorListEntrySet.iterator().next().getKey());
757 assertTrue("\"error\" Json element is not an Array", errorListElement.isJsonArray());
759 // As a final check, make sure there aren't multiple "error" array
760 // elements. Unfortunately,
761 // the call above to getAsJsonObject().entrySet() will out duplicate
762 // "error" elements. So
763 // we'll use regex on the json string to verify this.
765 final Matcher matcher = Pattern.compile("\"error\"[ ]*:[ ]*\\[", Pattern.DOTALL).matcher(bos.toString());
766 assertTrue("Expected 1 \"error\" element", matcher.find());
767 assertFalse("Found multiple \"error\" elements", matcher.find());
769 return errorListElement.getAsJsonArray();
772 void verifyJsonErrorNode(final JsonElement errorEntryElement, final ErrorType expErrorType,
773 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
774 final ErrorInfoVerifier errorInfoVerifier) {
776 JsonElement errorInfoElement = null;
777 final Map<String, String> leafMap = Maps.newHashMap();
778 for (final Entry<String, JsonElement> entry : errorEntryElement.getAsJsonObject().entrySet()) {
779 final String leafName = entry.getKey();
780 final JsonElement leafElement = entry.getValue();
782 if ("error-info".equals(leafName)) {
783 assertNotNull("Found unexpected \"error-info\" element", errorInfoVerifier);
784 errorInfoElement = leafElement;
786 assertTrue("\"error\" leaf Json element " + leafName + " is not a Primitive",
787 leafElement.isJsonPrimitive());
789 leafMap.put(leafName, leafElement.getAsString());
793 assertEquals("error-type", expErrorType.getErrorTypeTag(), leafMap.remove("error-type"));
794 assertEquals("error-tag", expErrorTag.getTagValue(), leafMap.remove("error-tag"));
796 verifyOptionalJsonLeaf(leafMap.remove("error-message"), expErrorMessage, "error-message");
797 verifyOptionalJsonLeaf(leafMap.remove("error-app-tag"), expErrorAppTag, "error-app-tag");
799 if (!leafMap.isEmpty()) {
800 fail("Found unexpected Json leaf elements for \"error\" element: " + leafMap);
803 if (errorInfoVerifier != null) {
804 assertNotNull("Missing \"error-info\" element", errorInfoElement);
805 errorInfoVerifier.verifyJson(errorInfoElement);
809 void verifyOptionalJsonLeaf(final String actualValue, final String expValue, final String tagName) {
810 if (expValue != null) {
811 assertEquals(tagName, expValue, actualValue);
813 assertNull("Found unexpected \"error\" leaf entry for: " + tagName, actualValue);
817 void verifyXMLResponseBody(final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
818 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
821 final Document doc = parseXMLDocument(stream);
823 final NodeList children = getXMLErrorList(doc, 1);
825 verifyXMLErrorNode(children.item(0), expErrorType, expErrorTag, expErrorMessage, expErrorAppTag,
829 private Document parseXMLDocument(final InputStream stream) throws IOException {
830 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
831 factory.setNamespaceAware(true);
832 factory.setCoalescing(true);
833 factory.setIgnoringElementContentWhitespace(true);
834 factory.setIgnoringComments(true);
836 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
837 ByteStreams.copy(stream, bos);
839 System.out.println("XML: " + bos.toString());
843 doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(bos.toByteArray()));
844 } catch (final Exception e) {
845 throw new IllegalArgumentException("Invalid XML response:\n" + bos.toString(), e);
850 void verifyXMLErrorNode(final Node errorNode, final ErrorType expErrorType, final ErrorTag expErrorTag,
851 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
854 final String errorType = (String) ERROR_TYPE.evaluate(errorNode, XPathConstants.STRING);
855 assertEquals("error-type", expErrorType.getErrorTypeTag(), errorType);
857 final String errorTag = (String) ERROR_TAG.evaluate(errorNode, XPathConstants.STRING);
858 assertEquals("error-tag", expErrorTag.getTagValue(), errorTag);
860 verifyOptionalXMLLeaf(errorNode, ERROR_MESSAGE, expErrorMessage, "error-message");
861 verifyOptionalXMLLeaf(errorNode, ERROR_APP_TAG, expErrorAppTag, "error-app-tag");
863 final Node errorInfoNode = (Node) ERROR_INFO.evaluate(errorNode, XPathConstants.NODE);
864 if (errorInfoVerifier != null) {
865 assertNotNull("Missing \"error-info\" node", errorInfoNode);
867 errorInfoVerifier.verifyXML(errorInfoNode);
869 assertNull("Found unexpected \"error-info\" node", errorInfoNode);
873 void verifyOptionalXMLLeaf(final Node fromNode, final XPathExpression xpath, final String expValue,
874 final String tagName) throws Exception {
875 if (expValue != null) {
876 final String actual = (String) xpath.evaluate(fromNode, XPathConstants.STRING);
877 assertEquals(tagName, expValue, actual);
879 assertNull("Found unexpected \"error\" leaf entry for: " + tagName,
880 xpath.evaluate(fromNode, XPathConstants.NODE));
884 NodeList getXMLErrorList(final Node fromNode, final int count) throws Exception {
885 final NodeList errorList = (NodeList) ERROR_LIST.evaluate(fromNode, XPathConstants.NODESET);
886 assertNotNull("Root errors node is empty", errorList);
887 assertEquals("Root errors node child count", count, errorList.getLength());