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;
22 import com.google.common.collect.ImmutableMap;
23 import com.google.common.collect.Maps;
24 import com.google.common.io.ByteStreams;
25 import com.google.gson.JsonArray;
26 import com.google.gson.JsonElement;
27 import com.google.gson.JsonParser;
28 import java.io.ByteArrayInputStream;
29 import java.io.ByteArrayOutputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.InputStreamReader;
33 import java.util.Arrays;
34 import java.util.Iterator;
35 import java.util.List;
37 import java.util.Map.Entry;
39 import java.util.regex.Matcher;
40 import java.util.regex.Pattern;
41 import javax.ws.rs.core.Application;
42 import javax.ws.rs.core.MediaType;
43 import javax.ws.rs.core.Response;
44 import javax.ws.rs.core.Response.Status;
45 import javax.ws.rs.core.UriInfo;
46 import javax.xml.namespace.NamespaceContext;
47 import javax.xml.parsers.DocumentBuilderFactory;
48 import javax.xml.xpath.XPath;
49 import javax.xml.xpath.XPathConstants;
50 import javax.xml.xpath.XPathExpression;
51 import javax.xml.xpath.XPathFactory;
52 import org.glassfish.jersey.server.ResourceConfig;
53 import org.glassfish.jersey.test.JerseyTest;
54 import org.junit.Before;
55 import org.junit.BeforeClass;
56 import org.junit.Ignore;
57 import org.junit.Test;
58 import org.opendaylight.controller.sal.rest.api.Draft02;
59 import org.opendaylight.controller.sal.rest.api.RestconfService;
60 import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
61 import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
62 import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
63 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
64 import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
65 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
66 import org.opendaylight.controller.sal.restconf.impl.RestconfError;
67 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
68 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
69 import org.w3c.dom.Document;
70 import org.w3c.dom.Element;
71 import org.w3c.dom.Node;
72 import org.w3c.dom.NodeList;
75 * Unit tests for RestconfDocumentedExceptionMapper.
77 * @author Thomas Pantelis
79 public class RestconfDocumentedExceptionMapperTest extends JerseyTest {
81 interface ErrorInfoVerifier {
82 void verifyXML(Node errorInfoNode);
84 void verifyJson(JsonElement errorInfoElement);
87 static class ComplexErrorInfoVerifier implements ErrorInfoVerifier {
89 Map<String, String> expErrorInfo;
91 public ComplexErrorInfoVerifier(final Map<String, String> expErrorInfo) {
92 this.expErrorInfo = expErrorInfo;
96 public void verifyXML(final Node errorInfoNode) {
98 Map<String, String> mutableExpMap = Maps.newHashMap(expErrorInfo);
99 NodeList childNodes = errorInfoNode.getChildNodes();
100 for (int i = 0; i < childNodes.getLength(); i++) {
101 Node child = childNodes.item(i);
102 if (child instanceof Element) {
103 String expValue = mutableExpMap.remove(child.getNodeName());
104 assertNotNull("Found unexpected \"error-info\" child node: " + child.getNodeName(), expValue);
105 assertEquals("Text content for \"error-info\" child node " + child.getNodeName(), expValue,
106 child.getTextContent());
110 if (!mutableExpMap.isEmpty()) {
111 fail("Missing \"error-info\" child nodes: " + mutableExpMap);
116 public void verifyJson(final JsonElement errorInfoElement) {
118 assertTrue("\"error-info\" Json element is not an Object", errorInfoElement.isJsonObject());
120 Map<String, String> actualErrorInfo = Maps.newHashMap();
121 for (Entry<String, JsonElement> entry : errorInfoElement.getAsJsonObject().entrySet()) {
122 String leafName = entry.getKey();
123 JsonElement leafElement = entry.getValue();
124 actualErrorInfo.put(leafName, leafElement.getAsString());
127 Map<String, String> mutableExpMap = Maps.newHashMap(expErrorInfo);
128 for (Entry<String, String> actual : actualErrorInfo.entrySet()) {
129 String expValue = mutableExpMap.remove(actual.getKey());
130 assertNotNull("Found unexpected \"error-info\" child node: " + actual.getKey(), expValue);
131 assertEquals("Text content for \"error-info\" child node " + actual.getKey(), expValue,
135 if (!mutableExpMap.isEmpty()) {
136 fail("Missing \"error-info\" child nodes: " + mutableExpMap);
141 static class SimpleErrorInfoVerifier implements ErrorInfoVerifier {
143 String expTextContent;
145 public SimpleErrorInfoVerifier(final String expErrorInfo) {
146 this.expTextContent = expErrorInfo;
149 void verifyContent(final String actualContent) {
150 assertNotNull("Actual \"error-info\" text content is null", actualContent);
151 assertTrue("", actualContent.contains(expTextContent));
155 public void verifyXML(final Node errorInfoNode) {
156 verifyContent(errorInfoNode.getTextContent());
160 public void verifyJson(final JsonElement errorInfoElement) {
161 verifyContent(errorInfoElement.getAsString());
165 static RestconfService mockRestConf = mock(RestconfService.class);
167 static XPath XPATH = XPathFactory.newInstance().newXPath();
168 static XPathExpression ERROR_LIST;
169 static XPathExpression ERROR_TYPE;
170 static XPathExpression ERROR_TAG;
171 static XPathExpression ERROR_MESSAGE;
172 static XPathExpression ERROR_APP_TAG;
173 static XPathExpression ERROR_INFO;
176 public static void init() throws Exception {
177 ControllerContext.getInstance().setGlobalSchema(TestUtils.loadSchemaContext("/modules"));
179 NamespaceContext nsContext = new NamespaceContext() {
181 public Iterator<?> getPrefixes(final String namespaceURI) {
186 public String getPrefix(final String namespaceURI) {
191 public String getNamespaceURI(final String prefix) {
192 return "ietf-restconf".equals(prefix) ? Draft02.RestConfModule.NAMESPACE : null;
196 XPATH.setNamespaceContext(nsContext);
197 ERROR_LIST = XPATH.compile("ietf-restconf:errors/ietf-restconf:error");
198 ERROR_TYPE = XPATH.compile("ietf-restconf:error-type");
199 ERROR_TAG = XPATH.compile("ietf-restconf:error-tag");
200 ERROR_MESSAGE = XPATH.compile("ietf-restconf:error-message");
201 ERROR_APP_TAG = XPATH.compile("ietf-restconf:error-app-tag");
202 ERROR_INFO = XPATH.compile("ietf-restconf:error-info");
207 public void setUp() throws Exception {
213 protected Application configure() {
214 ResourceConfig resourceConfig = new ResourceConfig();
215 resourceConfig = resourceConfig.registerInstances(mockRestConf, StructuredDataToXmlProvider.INSTANCE,
216 StructuredDataToJsonProvider.INSTANCE);
217 resourceConfig.registerClasses(RestconfDocumentedExceptionMapper.class);
218 return resourceConfig;
221 void stageMockEx(final RestconfDocumentedException ex) {
223 when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenThrow(ex);
226 void testJsonResponse(final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
227 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
228 final ErrorInfoVerifier errorInfoVerifier) throws Exception {
232 Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
234 InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, expStatus);
236 verifyJsonResponseBody(stream, expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, errorInfoVerifier);
240 public void testToJsonResponseWithMessageOnly() throws Exception {
242 testJsonResponse(new RestconfDocumentedException("mock error"), Status.INTERNAL_SERVER_ERROR,
243 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
245 // To test verification code
249 // " error-tag : \"operation-failed\"" +
250 // " ,error-type : \"application\"" +
251 // " ,error-message : \"An error occurred\"" +
252 // " ,error-info : {" +
253 // " session-id: \"123\"" +
254 // " ,address: \"1.2.3.4\"" +
260 // verifyJsonResponseBody( new java.io.StringBufferInputStream(json ),
261 // ErrorType.APPLICATION,
262 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
263 // com.google.common.collect.ImmutableMap.of( "session-id", "123",
264 // "address", "1.2.3.4" ) );
268 public void testToJsonResponseWithInUseErrorTag() throws Exception {
270 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.IN_USE),
271 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.IN_USE, "mock error", null, null);
275 public void testToJsonResponseWithInvalidValueErrorTag() throws Exception {
277 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.RPC, ErrorTag.INVALID_VALUE),
278 Status.BAD_REQUEST, ErrorType.RPC, ErrorTag.INVALID_VALUE, "mock error", null, null);
283 public void testToJsonResponseWithTooBigErrorTag() throws Exception {
285 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.TRANSPORT, ErrorTag.TOO_BIG),
286 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT, ErrorTag.TOO_BIG, "mock error", null, null);
291 public void testToJsonResponseWithMissingAttributeErrorTag() throws Exception {
293 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE),
294 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null);
298 public void testToJsonResponseWithBadAttributeErrorTag() throws Exception {
300 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE),
301 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error", null, null);
305 public void testToJsonResponseWithUnknownAttributeErrorTag() throws Exception {
307 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE),
308 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null);
312 public void testToJsonResponseWithBadElementErrorTag() throws Exception {
314 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT),
315 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null);
319 public void testToJsonResponseWithUnknownElementErrorTag() throws Exception {
321 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT),
322 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null);
326 public void testToJsonResponseWithUnknownNamespaceErrorTag() throws Exception {
328 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE),
329 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null);
333 public void testToJsonResponseWithMalformedMessageErrorTag() throws Exception {
335 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE),
336 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "mock error", null, null);
340 public void testToJsonResponseWithAccessDeniedErrorTag() throws Exception {
342 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED),
343 Status.FORBIDDEN, ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED, "mock error", null, null);
347 public void testToJsonResponseWithLockDeniedErrorTag() throws Exception {
349 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED),
350 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED, "mock error", null, null);
354 public void testToJsonResponseWithResourceDeniedErrorTag() throws Exception {
356 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED),
357 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED, "mock error", null, null);
361 public void testToJsonResponseWithRollbackFailedErrorTag() throws Exception {
363 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED),
364 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED, "mock error", null, null);
368 public void testToJsonResponseWithDataExistsErrorTag() throws Exception {
370 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS),
371 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, "mock error", null, null);
375 public void testToJsonResponseWithDataMissingErrorTag() throws Exception {
377 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING),
378 Status.NOT_FOUND, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
382 public void testToJsonResponseWithOperationNotSupportedErrorTag() throws Exception {
384 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL,
385 ErrorTag.OPERATION_NOT_SUPPORTED), Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
386 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null);
390 public void testToJsonResponseWithOperationFailedErrorTag() throws Exception {
392 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED),
393 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED, "mock error", null, null);
397 public void testToJsonResponseWithPartialOperationErrorTag() throws Exception {
399 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION),
400 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION, "mock error", null, null);
404 public void testToJsonResponseWithErrorAppTag() throws Exception {
406 testJsonResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
407 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag")), Status.BAD_REQUEST, ErrorType.APPLICATION,
408 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null);
412 public void testToJsonResponseWithMultipleErrors() throws Exception {
414 List<RestconfError> errorList = Arrays.asList(new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
415 "mock error1"), new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2"));
416 stageMockEx(new RestconfDocumentedException("mock", null, errorList));
418 Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
420 InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, Status.CONFLICT);
422 JsonArray arrayElement = parseJsonErrorArrayElement(stream);
424 assertEquals("\"error\" Json array element length", 2, arrayElement.size());
426 verifyJsonErrorNode(arrayElement.get(0), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1", null, null);
428 verifyJsonErrorNode(arrayElement.get(1), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2", null, null);
432 public void testToJsonResponseWithErrorInfo() throws Exception {
434 String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
435 testJsonResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
436 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", errorInfo)), Status.BAD_REQUEST,
437 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
438 new ComplexErrorInfoVerifier(ImmutableMap.of("session-id", "123", "address", "1.2.3.4")));
442 public void testToJsonResponseWithExceptionCause() throws Exception {
444 Exception cause = new Exception("mock exception cause");
445 testJsonResponse(new RestconfDocumentedException("mock error", cause), Status.INTERNAL_SERVER_ERROR,
446 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null,
447 new SimpleErrorInfoVerifier(cause.getMessage()));
450 void testXMLResponse(final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
451 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
452 final ErrorInfoVerifier errorInfoVerifier) throws Exception {
455 Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get();
457 InputStream stream = verifyResponse(resp, MediaType.APPLICATION_XML, expStatus);
459 verifyXMLResponseBody(stream, expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, errorInfoVerifier);
463 public void testToXMLResponseWithMessageOnly() throws Exception {
465 testXMLResponse(new RestconfDocumentedException("mock error"), Status.INTERNAL_SERVER_ERROR,
466 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
468 // To test verification code
470 // "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">"+
472 // " <error-type>application</error-type>"+
473 // " <error-tag>operation-failed</error-tag>"+
474 // " <error-message>An error occurred</error-message>"+
476 // " <session-id>123</session-id>" +
477 // " <address>1.2.3.4</address>" +
478 // " </error-info>" +
482 // verifyXMLResponseBody( new java.io.StringBufferInputStream(xml),
483 // ErrorType.APPLICATION,
484 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
485 // com.google.common.collect.ImmutableMap.of( "session-id", "123",
486 // "address", "1.2.3.4" ) );
490 public void testToXMLResponseWithInUseErrorTag() throws Exception {
492 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.IN_USE),
493 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.IN_USE, "mock error", null, null);
497 public void testToXMLResponseWithInvalidValueErrorTag() throws Exception {
499 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.RPC, ErrorTag.INVALID_VALUE),
500 Status.BAD_REQUEST, ErrorType.RPC, ErrorTag.INVALID_VALUE, "mock error", null, null);
504 public void testToXMLResponseWithTooBigErrorTag() throws Exception {
506 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.TRANSPORT, ErrorTag.TOO_BIG),
507 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT, ErrorTag.TOO_BIG, "mock error", null, null);
511 public void testToXMLResponseWithMissingAttributeErrorTag() throws Exception {
513 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE),
514 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null);
518 public void testToXMLResponseWithBadAttributeErrorTag() throws Exception {
520 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE),
521 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error", null, null);
525 public void testToXMLResponseWithUnknownAttributeErrorTag() throws Exception {
527 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE),
528 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null);
532 public void testToXMLResponseWithBadElementErrorTag() throws Exception {
534 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT),
535 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null);
539 public void testToXMLResponseWithUnknownElementErrorTag() throws Exception {
541 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT),
542 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null);
546 public void testToXMLResponseWithUnknownNamespaceErrorTag() throws Exception {
548 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE),
549 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null);
553 public void testToXMLResponseWithMalformedMessageErrorTag() throws Exception {
555 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE),
556 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "mock error", null, null);
560 public void testToXMLResponseWithAccessDeniedErrorTag() throws Exception {
562 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED),
563 Status.FORBIDDEN, ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED, "mock error", null, null);
567 public void testToXMLResponseWithLockDeniedErrorTag() throws Exception {
569 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED),
570 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED, "mock error", null, null);
574 public void testToXMLResponseWithResourceDeniedErrorTag() throws Exception {
576 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED),
577 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED, "mock error", null, null);
581 public void testToXMLResponseWithRollbackFailedErrorTag() throws Exception {
583 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED),
584 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED, "mock error", null, null);
588 public void testToXMLResponseWithDataExistsErrorTag() throws Exception {
590 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS),
591 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, "mock error", null, null);
595 public void testToXMLResponseWithDataMissingErrorTag() throws Exception {
597 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING),
598 Status.NOT_FOUND, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
602 public void testToXMLResponseWithOperationNotSupportedErrorTag() throws Exception {
604 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL,
605 ErrorTag.OPERATION_NOT_SUPPORTED), Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
606 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null);
610 public void testToXMLResponseWithOperationFailedErrorTag() throws Exception {
612 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED),
613 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED, "mock error", null, null);
617 public void testToXMLResponseWithPartialOperationErrorTag() throws Exception {
619 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION),
620 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION, "mock error", null, null);
624 public void testToXMLResponseWithErrorAppTag() throws Exception {
626 testXMLResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
627 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag")), Status.BAD_REQUEST, ErrorType.APPLICATION,
628 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null);
632 public void testToXMLResponseWithErrorInfo() throws Exception {
634 String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
635 testXMLResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
636 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", errorInfo)), Status.BAD_REQUEST,
637 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
638 new ComplexErrorInfoVerifier(ImmutableMap.of("session-id", "123", "address", "1.2.3.4")));
642 public void testToXMLResponseWithExceptionCause() throws Exception {
644 Exception cause = new Exception("mock exception cause");
645 testXMLResponse(new RestconfDocumentedException("mock error", cause), Status.INTERNAL_SERVER_ERROR,
646 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null,
647 new SimpleErrorInfoVerifier(cause.getMessage()));
651 public void testToXMLResponseWithMultipleErrors() throws Exception {
653 List<RestconfError> errorList = Arrays.asList(new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
654 "mock error1"), new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2"));
655 stageMockEx(new RestconfDocumentedException("mock", null, errorList));
657 Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get();
659 InputStream stream = verifyResponse(resp, MediaType.APPLICATION_XML, Status.CONFLICT);
661 Document doc = parseXMLDocument(stream);
663 NodeList children = getXMLErrorList(doc, 2);
665 verifyXMLErrorNode(children.item(0), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1", null, null);
667 verifyXMLErrorNode(children.item(1), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2", null, null);
671 public void testToResponseWithAcceptHeader() throws Exception {
673 stageMockEx(new RestconfDocumentedException("mock error"));
675 Response resp = target("/operational/foo").request().header("Accept", MediaType.APPLICATION_JSON).get();
677 InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, Status.INTERNAL_SERVER_ERROR);
679 verifyJsonResponseBody(stream, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
684 public void testToResponseWithStatusOnly() throws Exception {
686 // The StructuredDataToJsonProvider should throw a
687 // RestconfDocumentedException with no data
689 when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenReturn(
690 new NormalizedNodeContext(null, null));
692 Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
694 verifyResponse(resp, MediaType.TEXT_PLAIN, Status.NOT_FOUND);
697 InputStream verifyResponse(final Response resp, final String expMediaType, final Status expStatus) {
698 assertEquals("getMediaType", MediaType.valueOf(expMediaType), resp.getMediaType());
699 assertEquals("getStatus", expStatus.getStatusCode(), resp.getStatus());
701 Object entity = resp.getEntity();
702 assertEquals("Response entity", true, entity instanceof InputStream);
703 InputStream stream = (InputStream) entity;
707 void verifyJsonResponseBody(final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
708 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
711 JsonArray arrayElement = parseJsonErrorArrayElement(stream);
713 assertEquals("\"error\" Json array element length", 1, arrayElement.size());
715 verifyJsonErrorNode(arrayElement.get(0), expErrorType, expErrorTag, expErrorMessage, expErrorAppTag,
719 private JsonArray parseJsonErrorArrayElement(final InputStream stream) throws IOException {
720 ByteArrayOutputStream bos = new ByteArrayOutputStream();
721 ByteStreams.copy(stream, bos);
723 System.out.println("JSON: " + bos.toString());
725 JsonParser parser = new JsonParser();
726 JsonElement rootElement;
729 rootElement = parser.parse(new InputStreamReader(new ByteArrayInputStream(bos.toByteArray())));
730 } catch (Exception e) {
731 throw new IllegalArgumentException("Invalid JSON response:\n" + bos.toString(), e);
734 assertTrue("Root element of Json is not an Object", rootElement.isJsonObject());
736 Set<Entry<String, JsonElement>> errorsEntrySet = rootElement.getAsJsonObject().entrySet();
737 assertEquals("Json Object element set count", 1, errorsEntrySet.size());
739 Entry<String, JsonElement> errorsEntry = errorsEntrySet.iterator().next();
740 JsonElement errorsElement = errorsEntry.getValue();
741 assertEquals("First Json element name", "errors", errorsEntry.getKey());
742 assertTrue("\"errors\" Json element is not an Object", errorsElement.isJsonObject());
744 Set<Entry<String, JsonElement>> errorListEntrySet = errorsElement.getAsJsonObject().entrySet();
745 assertEquals("Root \"errors\" element child count", 1, errorListEntrySet.size());
747 JsonElement errorListElement = errorListEntrySet.iterator().next().getValue();
748 assertEquals("\"errors\" child Json element name", "error", errorListEntrySet.iterator().next().getKey());
749 assertTrue("\"error\" Json element is not an Array", errorListElement.isJsonArray());
751 // As a final check, make sure there aren't multiple "error" array
752 // elements. Unfortunately,
753 // the call above to getAsJsonObject().entrySet() will out duplicate
754 // "error" elements. So
755 // we'll use regex on the json string to verify this.
757 Matcher matcher = Pattern.compile("\"error\"[ ]*:[ ]*\\[", Pattern.DOTALL).matcher(bos.toString());
758 assertTrue("Expected 1 \"error\" element", matcher.find());
759 assertFalse("Found multiple \"error\" elements", matcher.find());
761 return errorListElement.getAsJsonArray();
764 void verifyJsonErrorNode(final JsonElement errorEntryElement, final ErrorType expErrorType,
765 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
766 final ErrorInfoVerifier errorInfoVerifier) {
768 JsonElement errorInfoElement = null;
769 Map<String, String> leafMap = Maps.newHashMap();
770 for (Entry<String, JsonElement> entry : errorEntryElement.getAsJsonObject().entrySet()) {
771 String leafName = entry.getKey();
772 JsonElement leafElement = entry.getValue();
774 if ("error-info".equals(leafName)) {
775 assertNotNull("Found unexpected \"error-info\" element", errorInfoVerifier);
776 errorInfoElement = leafElement;
778 assertTrue("\"error\" leaf Json element " + leafName + " is not a Primitive",
779 leafElement.isJsonPrimitive());
781 leafMap.put(leafName, leafElement.getAsString());
785 assertEquals("error-type", expErrorType.getErrorTypeTag(), leafMap.remove("error-type"));
786 assertEquals("error-tag", expErrorTag.getTagValue(), leafMap.remove("error-tag"));
788 verifyOptionalJsonLeaf(leafMap.remove("error-message"), expErrorMessage, "error-message");
789 verifyOptionalJsonLeaf(leafMap.remove("error-app-tag"), expErrorAppTag, "error-app-tag");
791 if (!leafMap.isEmpty()) {
792 fail("Found unexpected Json leaf elements for \"error\" element: " + leafMap);
795 if (errorInfoVerifier != null) {
796 assertNotNull("Missing \"error-info\" element", errorInfoElement);
797 errorInfoVerifier.verifyJson(errorInfoElement);
801 void verifyOptionalJsonLeaf(final String actualValue, final String expValue, final String tagName) {
802 if (expValue != null) {
803 assertEquals(tagName, expValue, actualValue);
805 assertNull("Found unexpected \"error\" leaf entry for: " + tagName, actualValue);
809 void verifyXMLResponseBody(final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
810 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
813 Document doc = parseXMLDocument(stream);
815 NodeList children = getXMLErrorList(doc, 1);
817 verifyXMLErrorNode(children.item(0), expErrorType, expErrorTag, expErrorMessage, expErrorAppTag,
821 private Document parseXMLDocument(final InputStream stream) throws IOException {
822 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
823 factory.setNamespaceAware(true);
824 factory.setCoalescing(true);
825 factory.setIgnoringElementContentWhitespace(true);
826 factory.setIgnoringComments(true);
828 ByteArrayOutputStream bos = new ByteArrayOutputStream();
829 ByteStreams.copy(stream, bos);
831 System.out.println("XML: " + bos.toString());
835 doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(bos.toByteArray()));
836 } catch (Exception e) {
837 throw new IllegalArgumentException("Invalid XML response:\n" + bos.toString(), e);
842 void verifyXMLErrorNode(final Node errorNode, final ErrorType expErrorType, final ErrorTag expErrorTag,
843 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
846 String errorType = (String) ERROR_TYPE.evaluate(errorNode, XPathConstants.STRING);
847 assertEquals("error-type", expErrorType.getErrorTypeTag(), errorType);
849 String errorTag = (String) ERROR_TAG.evaluate(errorNode, XPathConstants.STRING);
850 assertEquals("error-tag", expErrorTag.getTagValue(), errorTag);
852 verifyOptionalXMLLeaf(errorNode, ERROR_MESSAGE, expErrorMessage, "error-message");
853 verifyOptionalXMLLeaf(errorNode, ERROR_APP_TAG, expErrorAppTag, "error-app-tag");
855 Node errorInfoNode = (Node) ERROR_INFO.evaluate(errorNode, XPathConstants.NODE);
856 if (errorInfoVerifier != null) {
857 assertNotNull("Missing \"error-info\" node", errorInfoNode);
859 errorInfoVerifier.verifyXML(errorInfoNode);
861 assertNull("Found unexpected \"error-info\" node", errorInfoNode);
865 void verifyOptionalXMLLeaf(final Node fromNode, final XPathExpression xpath, final String expValue,
866 final String tagName) throws Exception {
867 if (expValue != null) {
868 String actual = (String) xpath.evaluate(fromNode, XPathConstants.STRING);
869 assertEquals(tagName, expValue, actual);
871 assertNull("Found unexpected \"error\" leaf entry for: " + tagName,
872 xpath.evaluate(fromNode, XPathConstants.NODE));
876 NodeList getXMLErrorList(final Node fromNode, final int count) throws Exception {
877 NodeList errorList = (NodeList) ERROR_LIST.evaluate(fromNode, XPathConstants.NODESET);
878 assertNotNull("Root errors node is empty", errorList);
879 assertEquals("Root errors node child count", count, errorList.getLength());