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
8 package org.opendaylight.controller.sal.restconf.impl.test;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertNull;
14 import static org.junit.Assert.assertTrue;
15 import static org.junit.Assert.fail;
16 import static org.mockito.ArgumentMatchers.any;
17 import static org.mockito.Mockito.mock;
18 import static org.mockito.Mockito.reset;
19 import static org.mockito.Mockito.when;
21 import com.google.common.collect.Iterators;
22 import com.google.gson.JsonArray;
23 import com.google.gson.JsonElement;
24 import com.google.gson.JsonParser;
25 import java.io.ByteArrayInputStream;
26 import java.io.ByteArrayOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.InputStreamReader;
30 import java.util.Arrays;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.List;
35 import java.util.Map.Entry;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39 import javax.ws.rs.core.Application;
40 import javax.ws.rs.core.MediaType;
41 import javax.ws.rs.core.Response;
42 import javax.ws.rs.core.Response.Status;
43 import javax.ws.rs.core.UriInfo;
44 import javax.xml.namespace.NamespaceContext;
45 import javax.xml.xpath.XPath;
46 import javax.xml.xpath.XPathConstants;
47 import javax.xml.xpath.XPathExpression;
48 import javax.xml.xpath.XPathFactory;
49 import org.glassfish.jersey.server.ResourceConfig;
50 import org.glassfish.jersey.test.JerseyTest;
51 import org.junit.Before;
52 import org.junit.BeforeClass;
53 import org.junit.Ignore;
54 import org.junit.Test;
55 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
56 import org.opendaylight.netconf.sal.rest.api.Draft02;
57 import org.opendaylight.netconf.sal.rest.api.RestconfService;
58 import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader;
59 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter;
60 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter;
61 import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper;
62 import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader;
63 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
64 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
65 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
66 import org.opendaylight.restconf.common.errors.RestconfError;
67 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
68 import org.opendaylight.yangtools.util.xml.UntrustedXML;
69 import org.opendaylight.yangtools.yang.common.ErrorType;
70 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73 import org.w3c.dom.Document;
74 import org.w3c.dom.Node;
75 import org.w3c.dom.NodeList;
76 import org.xml.sax.SAXException;
79 * Unit tests for RestconfDocumentedExceptionMapper.
81 * @author Thomas Pantelis
83 public class RestconfDocumentedExceptionMapperTest extends JerseyTest {
85 interface ErrorInfoVerifier {
86 void verifyXML(Node errorInfoNode);
88 void verifyJson(JsonElement errorInfoElement);
91 static class SimpleErrorInfoVerifier implements ErrorInfoVerifier {
93 String expTextContent;
95 SimpleErrorInfoVerifier(final String expErrorInfo) {
96 expTextContent = expErrorInfo;
99 void verifyContent(final String actualContent) {
100 assertNotNull("Actual \"error-info\" text content is null", actualContent);
101 assertTrue("", actualContent.contains(expTextContent));
105 public void verifyXML(final Node errorInfoNode) {
106 verifyContent(errorInfoNode.getTextContent());
110 public void verifyJson(final JsonElement errorInfoElement) {
111 verifyContent(errorInfoElement.getAsString());
115 private static final Logger LOG = LoggerFactory.getLogger(RestconfDocumentedExceptionMapperTest.class);
116 private static final String IETF_RESTCONF = "ietf-restconf";
117 static RestconfService mockRestConf = mock(RestconfService.class);
119 static XPath XPATH = XPathFactory.newInstance().newXPath();
120 static XPathExpression ERROR_LIST;
121 static XPathExpression ERROR_TYPE;
122 static XPathExpression ERROR_TAG;
123 static XPathExpression ERROR_MESSAGE;
124 static XPathExpression ERROR_APP_TAG;
125 static XPathExpression ERROR_INFO;
127 private static EffectiveModelContext schemaContext;
130 public static void init() throws Exception {
131 schemaContext = TestUtils.loadSchemaContext("/modules");
133 final NamespaceContext nsContext = new NamespaceContext() {
135 public Iterator<String> getPrefixes(final String namespaceURI) {
136 return Iterators.singletonIterator(IETF_RESTCONF);
140 public String getPrefix(final String namespaceURI) {
145 public String getNamespaceURI(final String prefix) {
146 return IETF_RESTCONF.equals(prefix) ? Draft02.RestConfModule.NAMESPACE : null;
150 XPATH.setNamespaceContext(nsContext);
151 ERROR_LIST = XPATH.compile("ietf-restconf:errors/ietf-restconf:error");
152 ERROR_TYPE = XPATH.compile("ietf-restconf:error-type");
153 ERROR_TAG = XPATH.compile("ietf-restconf:error-tag");
154 ERROR_MESSAGE = XPATH.compile("ietf-restconf:error-message");
155 ERROR_APP_TAG = XPATH.compile("ietf-restconf:error-app-tag");
156 ERROR_INFO = XPATH.compile("ietf-restconf:error-info");
161 public void setUp() throws Exception {
167 protected Application configure() {
168 ResourceConfig resourceConfig = new ResourceConfig();
169 ControllerContext controllerContext = TestRestconfUtils.newControllerContext(schemaContext);
170 resourceConfig = resourceConfig.registerInstances(mockRestConf,
171 new XmlNormalizedNodeBodyReader(controllerContext), new JsonNormalizedNodeBodyReader(controllerContext),
172 new NormalizedNodeJsonBodyWriter(), new NormalizedNodeXmlBodyWriter(),
173 new RestconfDocumentedExceptionMapper(controllerContext));
174 return resourceConfig;
177 void stageMockEx(final RestconfDocumentedException ex) {
179 when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenThrow(ex);
182 void testJsonResponse(final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
183 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
184 final ErrorInfoVerifier errorInfoVerifier) throws Exception {
188 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
190 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, expStatus);
192 verifyJsonResponseBody(stream, expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, errorInfoVerifier);
196 public void testToJsonResponseWithMessageOnly() throws Exception {
198 testJsonResponse(new RestconfDocumentedException("mock error"), Status.INTERNAL_SERVER_ERROR,
199 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
202 // To test verification code
206 // " error-tag : \"operation-failed\"" +
207 // " ,error-type : \"application\"" +
208 // " ,error-message : \"An error occurred\"" +
209 // " ,error-info : {" +
210 // " session-id: \"123\"" +
211 // " ,address: \"1.2.3.4\"" +
217 // verifyJsonResponseBody( new java.io.StringBufferInputStream(json ),
218 // ErrorType.APPLICATION,
219 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
220 // com.google.common.collect.ImmutableMap.of( "session-id", "123",
221 // "address", "1.2.3.4" ) );
225 public void testToJsonResponseWithInUseErrorTag() throws Exception {
227 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.IN_USE),
228 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.IN_USE, "mock error", null, null);
232 public void testToJsonResponseWithInvalidValueErrorTag() throws Exception {
234 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.RPC, ErrorTag.INVALID_VALUE),
235 Status.BAD_REQUEST, ErrorType.RPC, ErrorTag.INVALID_VALUE, "mock error", null, null);
240 public void testToJsonResponseWithTooBigErrorTag() throws Exception {
242 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.TRANSPORT, ErrorTag.TOO_BIG),
243 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT, ErrorTag.TOO_BIG, "mock error", null, null);
248 public void testToJsonResponseWithMissingAttributeErrorTag() throws Exception {
250 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE),
251 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null);
255 public void testToJsonResponseWithBadAttributeErrorTag() throws Exception {
257 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE),
258 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error", null, null);
262 public void testToJsonResponseWithUnknownAttributeErrorTag() throws Exception {
264 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE),
265 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null);
269 public void testToJsonResponseWithBadElementErrorTag() throws Exception {
271 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT),
272 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null);
276 public void testToJsonResponseWithUnknownElementErrorTag() throws Exception {
278 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT),
279 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null);
283 public void testToJsonResponseWithUnknownNamespaceErrorTag() throws Exception {
285 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE),
286 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null);
290 public void testToJsonResponseWithMalformedMessageErrorTag() throws Exception {
292 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE),
293 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "mock error", null, null);
297 public void testToJsonResponseWithAccessDeniedErrorTag() throws Exception {
299 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED),
300 Status.FORBIDDEN, ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED, "mock error", null, null);
304 public void testToJsonResponseWithLockDeniedErrorTag() throws Exception {
306 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED),
307 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED, "mock error", null, null);
311 public void testToJsonResponseWithResourceDeniedErrorTag() throws Exception {
313 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED),
314 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED, "mock error", null, null);
318 public void testToJsonResponseWithRollbackFailedErrorTag() throws Exception {
320 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED),
321 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED, "mock error", null, null);
325 public void testToJsonResponseWithDataExistsErrorTag() throws Exception {
327 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS),
328 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, "mock error", null, null);
332 public void testToJsonResponseWithDataMissingErrorTag() throws Exception {
334 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING),
335 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
339 public void testToJsonResponseWithOperationNotSupportedErrorTag() throws Exception {
341 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL,
342 ErrorTag.OPERATION_NOT_SUPPORTED), Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
343 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null);
347 public void testToJsonResponseWithOperationFailedErrorTag() throws Exception {
349 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED),
350 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED, "mock error", null, null);
354 public void testToJsonResponseWithPartialOperationErrorTag() throws Exception {
356 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION),
357 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION, "mock error", null, null);
361 public void testToJsonResponseWithErrorAppTag() throws Exception {
363 testJsonResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
364 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag")), Status.BAD_REQUEST, ErrorType.APPLICATION,
365 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null);
369 @Ignore // FIXME : find why it return "error-type" RPC no expected APPLICATION
370 public void testToJsonResponseWithMultipleErrors() throws Exception {
372 final List<RestconfError> errorList = Arrays.asList(
373 new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1"),
374 new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2"));
375 stageMockEx(new RestconfDocumentedException("mock", null, errorList));
377 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
379 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, Status.CONFLICT);
381 final JsonArray arrayElement = parseJsonErrorArrayElement(stream);
383 assertEquals("\"error\" Json array element length", 2, arrayElement.size());
386 arrayElement.get(0), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1", null, null);
388 verifyJsonErrorNode(arrayElement.get(1), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2", null, null);
392 public void testToJsonResponseWithErrorInfo() throws Exception {
394 final String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
395 testJsonResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
396 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", errorInfo)), Status.BAD_REQUEST,
397 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
398 new SimpleErrorInfoVerifier(errorInfo));
402 public void testToJsonResponseWithExceptionCause() throws Exception {
404 final Exception cause = new Exception("mock exception cause");
405 testJsonResponse(new RestconfDocumentedException("mock error", cause), Status.INTERNAL_SERVER_ERROR,
406 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null,
407 new SimpleErrorInfoVerifier(cause.getMessage()));
410 void testXMLResponse(final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
411 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
412 final ErrorInfoVerifier errorInfoVerifier) throws Exception {
415 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get();
417 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_XML, expStatus);
419 verifyXMLResponseBody(stream, expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, errorInfoVerifier);
423 public void testToXMLResponseWithMessageOnly() throws Exception {
425 testXMLResponse(new RestconfDocumentedException("mock error"), Status.INTERNAL_SERVER_ERROR,
426 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
428 // To test verification code
430 // "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">"+
432 // " <error-type>application</error-type>"+
433 // " <error-tag>operation-failed</error-tag>"+
434 // " <error-message>An error occurred</error-message>"+
436 // " <session-id>123</session-id>" +
437 // " <address>1.2.3.4</address>" +
438 // " </error-info>" +
442 // verifyXMLResponseBody( new java.io.StringBufferInputStream(xml),
443 // ErrorType.APPLICATION,
444 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
445 // com.google.common.collect.ImmutableMap.of( "session-id", "123",
446 // "address", "1.2.3.4" ) );
450 public void testToXMLResponseWithInUseErrorTag() throws Exception {
452 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.IN_USE),
453 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.IN_USE, "mock error", null, null);
457 public void testToXMLResponseWithInvalidValueErrorTag() throws Exception {
459 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.RPC, ErrorTag.INVALID_VALUE),
460 Status.BAD_REQUEST, ErrorType.RPC, ErrorTag.INVALID_VALUE, "mock error", null, null);
464 public void testToXMLResponseWithTooBigErrorTag() throws Exception {
466 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.TRANSPORT, ErrorTag.TOO_BIG),
467 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT, ErrorTag.TOO_BIG, "mock error", null, null);
471 public void testToXMLResponseWithMissingAttributeErrorTag() throws Exception {
473 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE),
474 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null);
478 public void testToXMLResponseWithBadAttributeErrorTag() throws Exception {
480 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE),
481 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error", null, null);
485 public void testToXMLResponseWithUnknownAttributeErrorTag() throws Exception {
487 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE),
488 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null);
492 public void testToXMLResponseWithBadElementErrorTag() throws Exception {
494 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT),
495 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null);
499 public void testToXMLResponseWithUnknownElementErrorTag() throws Exception {
501 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT),
502 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null);
506 public void testToXMLResponseWithUnknownNamespaceErrorTag() throws Exception {
508 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE),
509 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null);
513 public void testToXMLResponseWithMalformedMessageErrorTag() throws Exception {
515 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE),
516 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "mock error", null, null);
520 public void testToXMLResponseWithAccessDeniedErrorTag() throws Exception {
522 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED),
523 Status.FORBIDDEN, ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED, "mock error", null, null);
527 public void testToXMLResponseWithLockDeniedErrorTag() throws Exception {
529 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED),
530 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED, "mock error", null, null);
534 public void testToXMLResponseWithResourceDeniedErrorTag() throws Exception {
536 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED),
537 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED, "mock error", null, null);
541 public void testToXMLResponseWithRollbackFailedErrorTag() throws Exception {
543 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED),
544 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED, "mock error", null, null);
548 public void testToXMLResponseWithDataExistsErrorTag() throws Exception {
550 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS),
551 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, "mock error", null, null);
555 public void testToXMLResponseWithDataMissingErrorTag() throws Exception {
557 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING),
558 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
562 public void testToXMLResponseWithOperationNotSupportedErrorTag() throws Exception {
564 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL,
565 ErrorTag.OPERATION_NOT_SUPPORTED), Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
566 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null);
570 public void testToXMLResponseWithOperationFailedErrorTag() throws Exception {
572 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED),
573 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED, "mock error", null, null);
577 public void testToXMLResponseWithPartialOperationErrorTag() throws Exception {
579 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION),
580 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION, "mock error", null, null);
584 public void testToXMLResponseWithErrorAppTag() throws Exception {
586 testXMLResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
587 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag")), Status.BAD_REQUEST, ErrorType.APPLICATION,
588 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null);
592 public void testToXMLResponseWithErrorInfo() throws Exception {
594 final String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
595 testXMLResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
596 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", errorInfo)), Status.BAD_REQUEST,
597 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
598 new SimpleErrorInfoVerifier(errorInfo));
602 public void testToXMLResponseWithExceptionCause() throws Exception {
604 final Exception cause = new Exception("mock exception cause");
605 testXMLResponse(new RestconfDocumentedException("mock error", cause), Status.INTERNAL_SERVER_ERROR,
606 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null,
607 new SimpleErrorInfoVerifier(cause.getMessage()));
611 @Ignore // FIXME : find why it return error-type as RPC no APPLICATION
612 public void testToXMLResponseWithMultipleErrors() throws Exception {
614 final List<RestconfError> errorList = Arrays.asList(
615 new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1"),
616 new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2"));
617 stageMockEx(new RestconfDocumentedException("mock", null, errorList));
619 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get();
621 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_XML, Status.CONFLICT);
623 final Document doc = parseXMLDocument(stream);
625 final NodeList children = getXMLErrorList(doc, 2);
627 verifyXMLErrorNode(children.item(0), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1", null, null);
629 verifyXMLErrorNode(children.item(1), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2", null, null);
633 public void testToResponseWithAcceptHeader() throws Exception {
635 stageMockEx(new RestconfDocumentedException("mock error"));
637 final Response resp = target("/operational/foo").request().header("Accept", MediaType.APPLICATION_JSON).get();
639 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, Status.INTERNAL_SERVER_ERROR);
641 verifyJsonResponseBody(stream, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
646 public void testToResponseWithStatusOnly() throws Exception {
648 // The StructuredDataToJsonProvider should throw a
649 // RestconfDocumentedException with no data
651 when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenReturn(
652 new NormalizedNodeContext(null, null));
654 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
656 verifyResponse(resp, MediaType.TEXT_PLAIN, Status.NOT_FOUND);
659 InputStream verifyResponse(final Response resp, final String expMediaType, final Status expStatus) {
660 assertEquals("getMediaType", MediaType.valueOf(expMediaType), resp.getMediaType());
661 assertEquals("getStatus", expStatus.getStatusCode(), resp.getStatus());
663 final Object entity = resp.getEntity();
664 assertEquals("Response entity", true, entity instanceof InputStream);
665 final InputStream stream = (InputStream) entity;
669 void verifyJsonResponseBody(final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
670 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
673 final JsonArray arrayElement = parseJsonErrorArrayElement(stream);
675 assertEquals("\"error\" Json array element length", 1, arrayElement.size());
677 verifyJsonErrorNode(arrayElement.get(0), expErrorType, expErrorTag, expErrorMessage, expErrorAppTag,
681 @SuppressWarnings("checkstyle:IllegalCatch")
682 private static JsonArray parseJsonErrorArrayElement(final InputStream stream) throws IOException {
683 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
684 stream.transferTo(bos);
686 LOG.info("JSON: {}", bos);
688 final JsonParser parser = new JsonParser();
689 JsonElement rootElement;
692 rootElement = parser.parse(new InputStreamReader(new ByteArrayInputStream(bos.toByteArray())));
693 } catch (final Exception e) {
694 throw new IllegalArgumentException("Invalid JSON response:\n" + bos.toString(), e);
697 assertTrue("Root element of Json is not an Object", rootElement.isJsonObject());
699 final Set<Entry<String, JsonElement>> errorsEntrySet = rootElement.getAsJsonObject().entrySet();
700 assertEquals("Json Object element set count", 1, errorsEntrySet.size());
702 final Entry<String, JsonElement> errorsEntry = errorsEntrySet.iterator().next();
703 final JsonElement errorsElement = errorsEntry.getValue();
704 assertEquals("First Json element name", "errors", errorsEntry.getKey());
705 assertTrue("\"errors\" Json element is not an Object", errorsElement.isJsonObject());
707 final Set<Entry<String, JsonElement>> errorListEntrySet = errorsElement.getAsJsonObject().entrySet();
708 assertEquals("Root \"errors\" element child count", 1, errorListEntrySet.size());
710 final JsonElement errorListElement = errorListEntrySet.iterator().next().getValue();
711 assertEquals("\"errors\" child Json element name", "error", errorListEntrySet.iterator().next().getKey());
712 assertTrue("\"error\" Json element is not an Array", errorListElement.isJsonArray());
714 // As a final check, make sure there aren't multiple "error" array
715 // elements. Unfortunately,
716 // the call above to getAsJsonObject().entrySet() will out duplicate
717 // "error" elements. So
718 // we'll use regex on the json string to verify this.
720 final Matcher matcher = Pattern.compile("\"error\"[ ]*:[ ]*\\[", Pattern.DOTALL).matcher(bos.toString());
721 assertTrue("Expected 1 \"error\" element", matcher.find());
722 assertFalse("Found multiple \"error\" elements", matcher.find());
724 return errorListElement.getAsJsonArray();
727 void verifyJsonErrorNode(final JsonElement errorEntryElement, final ErrorType expErrorType,
728 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
729 final ErrorInfoVerifier errorInfoVerifier) {
731 JsonElement errorInfoElement = null;
732 final Map<String, String> leafMap = new HashMap<>();
733 for (final Entry<String, JsonElement> entry : errorEntryElement.getAsJsonObject().entrySet()) {
734 final String leafName = entry.getKey();
735 final JsonElement leafElement = entry.getValue();
737 if ("error-info".equals(leafName)) {
738 assertNotNull("Found unexpected \"error-info\" element", errorInfoVerifier);
739 errorInfoElement = leafElement;
741 assertTrue("\"error\" leaf Json element " + leafName + " is not a Primitive",
742 leafElement.isJsonPrimitive());
744 leafMap.put(leafName, leafElement.getAsString());
748 assertEquals("error-type", expErrorType.elementBody(), leafMap.remove("error-type"));
749 assertEquals("error-tag", expErrorTag.getTagValue(), leafMap.remove("error-tag"));
751 verifyOptionalJsonLeaf(leafMap.remove("error-message"), expErrorMessage, "error-message");
752 verifyOptionalJsonLeaf(leafMap.remove("error-app-tag"), expErrorAppTag, "error-app-tag");
754 if (!leafMap.isEmpty()) {
755 fail("Found unexpected Json leaf elements for \"error\" element: " + leafMap);
758 if (errorInfoVerifier != null) {
759 assertNotNull("Missing \"error-info\" element", errorInfoElement);
760 errorInfoVerifier.verifyJson(errorInfoElement);
764 void verifyOptionalJsonLeaf(final String actualValue, final String expValue, final String tagName) {
765 if (expValue != null) {
766 assertEquals(tagName, expValue, actualValue);
768 assertNull("Found unexpected \"error\" leaf entry for: " + tagName, actualValue);
772 void verifyXMLResponseBody(final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
773 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
776 final Document doc = parseXMLDocument(stream);
778 final NodeList children = getXMLErrorList(doc, 1);
780 verifyXMLErrorNode(children.item(0), expErrorType, expErrorTag, expErrorMessage, expErrorAppTag,
784 private static Document parseXMLDocument(final InputStream stream) throws IOException, SAXException {
785 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
786 stream.transferTo(bos);
788 LOG.debug("XML: {}", bos);
790 return UntrustedXML.newDocumentBuilder().parse(new ByteArrayInputStream(bos.toByteArray()));
793 void verifyXMLErrorNode(final Node errorNode, final ErrorType expErrorType, final ErrorTag expErrorTag,
794 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
797 final String errorType = (String) ERROR_TYPE.evaluate(errorNode, XPathConstants.STRING);
798 assertEquals("error-type", expErrorType.elementBody(), errorType);
800 final String errorTag = (String) ERROR_TAG.evaluate(errorNode, XPathConstants.STRING);
801 assertEquals("error-tag", expErrorTag.getTagValue(), errorTag);
803 verifyOptionalXMLLeaf(errorNode, ERROR_MESSAGE, expErrorMessage, "error-message");
804 verifyOptionalXMLLeaf(errorNode, ERROR_APP_TAG, expErrorAppTag, "error-app-tag");
806 final Node errorInfoNode = (Node) ERROR_INFO.evaluate(errorNode, XPathConstants.NODE);
807 if (errorInfoVerifier != null) {
808 assertNotNull("Missing \"error-info\" node", errorInfoNode);
810 errorInfoVerifier.verifyXML(errorInfoNode);
812 assertNull("Found unexpected \"error-info\" node", errorInfoNode);
816 void verifyOptionalXMLLeaf(final Node fromNode, final XPathExpression xpath, final String expValue,
817 final String tagName) throws Exception {
818 if (expValue != null) {
819 final String actual = (String) xpath.evaluate(fromNode, XPathConstants.STRING);
820 assertEquals(tagName, expValue, actual);
822 assertNull("Found unexpected \"error\" leaf entry for: " + tagName,
823 xpath.evaluate(fromNode, XPathConstants.NODE));
827 NodeList getXMLErrorList(final Node fromNode, final int count) throws Exception {
828 final NodeList errorList = (NodeList) ERROR_LIST.evaluate(fromNode, XPathConstants.NODESET);
829 assertNotNull("Root errors node is empty", errorList);
830 assertEquals("Root errors node child count", count, errorList.getLength());