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.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.xpath.XPath;
47 import javax.xml.xpath.XPathConstants;
48 import javax.xml.xpath.XPathExpression;
49 import javax.xml.xpath.XPathFactory;
50 import org.glassfish.jersey.server.ResourceConfig;
51 import org.glassfish.jersey.test.JerseyTest;
52 import org.junit.Before;
53 import org.junit.BeforeClass;
54 import org.junit.Ignore;
55 import org.junit.Test;
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.netconf.sal.restconf.impl.NormalizedNodeContext;
65 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
66 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
67 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
68 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
69 import org.opendaylight.yangtools.util.xml.UntrustedXML;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72 import org.w3c.dom.Document;
73 import org.w3c.dom.Node;
74 import org.w3c.dom.NodeList;
75 import org.xml.sax.SAXException;
78 * Unit tests for RestconfDocumentedExceptionMapper.
80 * @author Thomas Pantelis
82 public class RestconfDocumentedExceptionMapperTest extends JerseyTest {
84 interface ErrorInfoVerifier {
85 void verifyXML(Node errorInfoNode);
87 void verifyJson(JsonElement errorInfoElement);
90 static class SimpleErrorInfoVerifier implements ErrorInfoVerifier {
92 String expTextContent;
94 public SimpleErrorInfoVerifier(final String expErrorInfo) {
95 expTextContent = expErrorInfo;
98 void verifyContent(final String actualContent) {
99 assertNotNull("Actual \"error-info\" text content is null", actualContent);
100 assertTrue("", actualContent.contains(expTextContent));
104 public void verifyXML(final Node errorInfoNode) {
105 verifyContent(errorInfoNode.getTextContent());
109 public void verifyJson(final JsonElement errorInfoElement) {
110 verifyContent(errorInfoElement.getAsString());
114 private static final Logger LOG = LoggerFactory.getLogger(RestconfDocumentedExceptionMapperTest.class);
115 static RestconfService mockRestConf = mock(RestconfService.class);
117 static XPath XPATH = XPathFactory.newInstance().newXPath();
118 static XPathExpression ERROR_LIST;
119 static XPathExpression ERROR_TYPE;
120 static XPathExpression ERROR_TAG;
121 static XPathExpression ERROR_MESSAGE;
122 static XPathExpression ERROR_APP_TAG;
123 static XPathExpression ERROR_INFO;
126 public static void init() throws Exception {
127 ControllerContext.getInstance().setGlobalSchema(TestUtils.loadSchemaContext("/modules"));
129 final NamespaceContext nsContext = new NamespaceContext() {
131 public Iterator<?> getPrefixes(final String namespaceURI) {
136 public String getPrefix(final String namespaceURI) {
141 public String getNamespaceURI(final String prefix) {
142 return "ietf-restconf".equals(prefix) ? Draft02.RestConfModule.NAMESPACE : null;
146 XPATH.setNamespaceContext(nsContext);
147 ERROR_LIST = XPATH.compile("ietf-restconf:errors/ietf-restconf:error");
148 ERROR_TYPE = XPATH.compile("ietf-restconf:error-type");
149 ERROR_TAG = XPATH.compile("ietf-restconf:error-tag");
150 ERROR_MESSAGE = XPATH.compile("ietf-restconf:error-message");
151 ERROR_APP_TAG = XPATH.compile("ietf-restconf:error-app-tag");
152 ERROR_INFO = XPATH.compile("ietf-restconf:error-info");
157 public void setUp() throws Exception {
163 protected Application configure() {
164 ResourceConfig resourceConfig = new ResourceConfig();
165 resourceConfig = resourceConfig.registerInstances(mockRestConf, new XmlNormalizedNodeBodyReader(),
166 new JsonNormalizedNodeBodyReader(), new NormalizedNodeJsonBodyWriter(), new NormalizedNodeXmlBodyWriter());
167 resourceConfig.registerClasses(RestconfDocumentedExceptionMapper.class);
168 return resourceConfig;
171 void stageMockEx(final RestconfDocumentedException ex) {
173 when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenThrow(ex);
176 void testJsonResponse(final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
177 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
178 final ErrorInfoVerifier errorInfoVerifier) throws Exception {
182 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
184 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, expStatus);
186 verifyJsonResponseBody(stream, expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, errorInfoVerifier);
190 public void testToJsonResponseWithMessageOnly() throws Exception {
192 testJsonResponse(new RestconfDocumentedException("mock error"), Status.INTERNAL_SERVER_ERROR,
193 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
196 // To test verification code
200 // " error-tag : \"operation-failed\"" +
201 // " ,error-type : \"application\"" +
202 // " ,error-message : \"An error occurred\"" +
203 // " ,error-info : {" +
204 // " session-id: \"123\"" +
205 // " ,address: \"1.2.3.4\"" +
211 // verifyJsonResponseBody( new java.io.StringBufferInputStream(json ),
212 // ErrorType.APPLICATION,
213 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
214 // com.google.common.collect.ImmutableMap.of( "session-id", "123",
215 // "address", "1.2.3.4" ) );
219 public void testToJsonResponseWithInUseErrorTag() throws Exception {
221 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.IN_USE),
222 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.IN_USE, "mock error", null, null);
226 public void testToJsonResponseWithInvalidValueErrorTag() throws Exception {
228 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.RPC, ErrorTag.INVALID_VALUE),
229 Status.BAD_REQUEST, ErrorType.RPC, ErrorTag.INVALID_VALUE, "mock error", null, null);
234 public void testToJsonResponseWithTooBigErrorTag() throws Exception {
236 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.TRANSPORT, ErrorTag.TOO_BIG),
237 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT, ErrorTag.TOO_BIG, "mock error", null, null);
242 public void testToJsonResponseWithMissingAttributeErrorTag() throws Exception {
244 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE),
245 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null);
249 public void testToJsonResponseWithBadAttributeErrorTag() throws Exception {
251 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE),
252 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error", null, null);
256 public void testToJsonResponseWithUnknownAttributeErrorTag() throws Exception {
258 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE),
259 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null);
263 public void testToJsonResponseWithBadElementErrorTag() throws Exception {
265 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT),
266 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null);
270 public void testToJsonResponseWithUnknownElementErrorTag() throws Exception {
272 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT),
273 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null);
277 public void testToJsonResponseWithUnknownNamespaceErrorTag() throws Exception {
279 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE),
280 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null);
284 public void testToJsonResponseWithMalformedMessageErrorTag() throws Exception {
286 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE),
287 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "mock error", null, null);
291 public void testToJsonResponseWithAccessDeniedErrorTag() throws Exception {
293 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED),
294 Status.FORBIDDEN, ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED, "mock error", null, null);
298 public void testToJsonResponseWithLockDeniedErrorTag() throws Exception {
300 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED),
301 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED, "mock error", null, null);
305 public void testToJsonResponseWithResourceDeniedErrorTag() throws Exception {
307 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED),
308 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED, "mock error", null, null);
312 public void testToJsonResponseWithRollbackFailedErrorTag() throws Exception {
314 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED),
315 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED, "mock error", null, null);
319 public void testToJsonResponseWithDataExistsErrorTag() throws Exception {
321 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS),
322 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, "mock error", null, null);
326 public void testToJsonResponseWithDataMissingErrorTag() throws Exception {
328 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING),
329 Status.NOT_FOUND, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
333 public void testToJsonResponseWithOperationNotSupportedErrorTag() throws Exception {
335 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL,
336 ErrorTag.OPERATION_NOT_SUPPORTED), Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
337 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null);
341 public void testToJsonResponseWithOperationFailedErrorTag() throws Exception {
343 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED),
344 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED, "mock error", null, null);
348 public void testToJsonResponseWithPartialOperationErrorTag() throws Exception {
350 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION),
351 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION, "mock error", null, null);
355 public void testToJsonResponseWithErrorAppTag() throws Exception {
357 testJsonResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
358 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag")), Status.BAD_REQUEST, ErrorType.APPLICATION,
359 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null);
363 @Ignore // FIXME : find why it return "error-type" RPC no expected APPLICATION
364 public void testToJsonResponseWithMultipleErrors() throws Exception {
366 final List<RestconfError> errorList = Arrays.asList(new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
367 "mock error1"), new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2"));
368 stageMockEx(new RestconfDocumentedException("mock", null, errorList));
370 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
372 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, Status.CONFLICT);
374 final JsonArray arrayElement = parseJsonErrorArrayElement(stream);
376 assertEquals("\"error\" Json array element length", 2, arrayElement.size());
378 verifyJsonErrorNode(arrayElement.get(0), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1", null, null);
380 verifyJsonErrorNode(arrayElement.get(1), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2", null, null);
384 public void testToJsonResponseWithErrorInfo() throws Exception {
386 final String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
387 testJsonResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
388 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", errorInfo)), Status.BAD_REQUEST,
389 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
390 new SimpleErrorInfoVerifier(errorInfo));
394 public void testToJsonResponseWithExceptionCause() throws Exception {
396 final Exception cause = new Exception("mock exception cause");
397 testJsonResponse(new RestconfDocumentedException("mock error", cause), Status.INTERNAL_SERVER_ERROR,
398 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null,
399 new SimpleErrorInfoVerifier(cause.getMessage()));
402 void testXMLResponse(final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
403 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
404 final ErrorInfoVerifier errorInfoVerifier) throws Exception {
407 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get();
409 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_XML, expStatus);
411 verifyXMLResponseBody(stream, expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, errorInfoVerifier);
415 public void testToXMLResponseWithMessageOnly() throws Exception {
417 testXMLResponse(new RestconfDocumentedException("mock error"), Status.INTERNAL_SERVER_ERROR,
418 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
420 // To test verification code
422 // "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">"+
424 // " <error-type>application</error-type>"+
425 // " <error-tag>operation-failed</error-tag>"+
426 // " <error-message>An error occurred</error-message>"+
428 // " <session-id>123</session-id>" +
429 // " <address>1.2.3.4</address>" +
430 // " </error-info>" +
434 // verifyXMLResponseBody( new java.io.StringBufferInputStream(xml),
435 // ErrorType.APPLICATION,
436 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
437 // com.google.common.collect.ImmutableMap.of( "session-id", "123",
438 // "address", "1.2.3.4" ) );
442 public void testToXMLResponseWithInUseErrorTag() throws Exception {
444 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.IN_USE),
445 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.IN_USE, "mock error", null, null);
449 public void testToXMLResponseWithInvalidValueErrorTag() throws Exception {
451 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.RPC, ErrorTag.INVALID_VALUE),
452 Status.BAD_REQUEST, ErrorType.RPC, ErrorTag.INVALID_VALUE, "mock error", null, null);
456 public void testToXMLResponseWithTooBigErrorTag() throws Exception {
458 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.TRANSPORT, ErrorTag.TOO_BIG),
459 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT, ErrorTag.TOO_BIG, "mock error", null, null);
463 public void testToXMLResponseWithMissingAttributeErrorTag() throws Exception {
465 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE),
466 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null);
470 public void testToXMLResponseWithBadAttributeErrorTag() throws Exception {
472 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE),
473 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error", null, null);
477 public void testToXMLResponseWithUnknownAttributeErrorTag() throws Exception {
479 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE),
480 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null);
484 public void testToXMLResponseWithBadElementErrorTag() throws Exception {
486 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT),
487 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null);
491 public void testToXMLResponseWithUnknownElementErrorTag() throws Exception {
493 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT),
494 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null);
498 public void testToXMLResponseWithUnknownNamespaceErrorTag() throws Exception {
500 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE),
501 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null);
505 public void testToXMLResponseWithMalformedMessageErrorTag() throws Exception {
507 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE),
508 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "mock error", null, null);
512 public void testToXMLResponseWithAccessDeniedErrorTag() throws Exception {
514 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED),
515 Status.FORBIDDEN, ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED, "mock error", null, null);
519 public void testToXMLResponseWithLockDeniedErrorTag() throws Exception {
521 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED),
522 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED, "mock error", null, null);
526 public void testToXMLResponseWithResourceDeniedErrorTag() throws Exception {
528 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED),
529 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED, "mock error", null, null);
533 public void testToXMLResponseWithRollbackFailedErrorTag() throws Exception {
535 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED),
536 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED, "mock error", null, null);
540 public void testToXMLResponseWithDataExistsErrorTag() throws Exception {
542 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS),
543 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, "mock error", null, null);
547 public void testToXMLResponseWithDataMissingErrorTag() throws Exception {
549 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING),
550 Status.NOT_FOUND, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
554 public void testToXMLResponseWithOperationNotSupportedErrorTag() throws Exception {
556 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL,
557 ErrorTag.OPERATION_NOT_SUPPORTED), Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
558 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null);
562 public void testToXMLResponseWithOperationFailedErrorTag() throws Exception {
564 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED),
565 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED, "mock error", null, null);
569 public void testToXMLResponseWithPartialOperationErrorTag() throws Exception {
571 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION),
572 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION, "mock error", null, null);
576 public void testToXMLResponseWithErrorAppTag() throws Exception {
578 testXMLResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
579 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag")), Status.BAD_REQUEST, ErrorType.APPLICATION,
580 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null);
584 public void testToXMLResponseWithErrorInfo() throws Exception {
586 final String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
587 testXMLResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
588 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", errorInfo)), Status.BAD_REQUEST,
589 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
590 new SimpleErrorInfoVerifier(errorInfo));
594 public void testToXMLResponseWithExceptionCause() throws Exception {
596 final Exception cause = new Exception("mock exception cause");
597 testXMLResponse(new RestconfDocumentedException("mock error", cause), Status.INTERNAL_SERVER_ERROR,
598 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null,
599 new SimpleErrorInfoVerifier(cause.getMessage()));
603 @Ignore // FIXME : find why it return error-type as RPC no APPLICATION
604 public void testToXMLResponseWithMultipleErrors() throws Exception {
606 final List<RestconfError> errorList = Arrays.asList(new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
607 "mock error1"), new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2"));
608 stageMockEx(new RestconfDocumentedException("mock", null, errorList));
610 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get();
612 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_XML, Status.CONFLICT);
614 final Document doc = parseXMLDocument(stream);
616 final NodeList children = getXMLErrorList(doc, 2);
618 verifyXMLErrorNode(children.item(0), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1", null, null);
620 verifyXMLErrorNode(children.item(1), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2", null, null);
624 public void testToResponseWithAcceptHeader() throws Exception {
626 stageMockEx(new RestconfDocumentedException("mock error"));
628 final Response resp = target("/operational/foo").request().header("Accept", MediaType.APPLICATION_JSON).get();
630 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, Status.INTERNAL_SERVER_ERROR);
632 verifyJsonResponseBody(stream, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
637 public void testToResponseWithStatusOnly() throws Exception {
639 // The StructuredDataToJsonProvider should throw a
640 // RestconfDocumentedException with no data
642 when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenReturn(
643 new NormalizedNodeContext(null, null));
645 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
647 verifyResponse(resp, MediaType.TEXT_PLAIN, Status.NOT_FOUND);
650 InputStream verifyResponse(final Response resp, final String expMediaType, final Status expStatus) {
651 assertEquals("getMediaType", MediaType.valueOf(expMediaType), resp.getMediaType());
652 assertEquals("getStatus", expStatus.getStatusCode(), resp.getStatus());
654 final Object entity = resp.getEntity();
655 assertEquals("Response entity", true, entity instanceof InputStream);
656 final InputStream stream = (InputStream) entity;
660 void verifyJsonResponseBody(final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
661 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
664 final JsonArray arrayElement = parseJsonErrorArrayElement(stream);
666 assertEquals("\"error\" Json array element length", 1, arrayElement.size());
668 verifyJsonErrorNode(arrayElement.get(0), expErrorType, expErrorTag, expErrorMessage, expErrorAppTag,
672 private static JsonArray parseJsonErrorArrayElement(final InputStream stream) throws IOException {
673 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
674 ByteStreams.copy(stream, bos);
676 System.out.println("JSON: " + bos.toString());
678 final JsonParser parser = new JsonParser();
679 JsonElement rootElement;
682 rootElement = parser.parse(new InputStreamReader(new ByteArrayInputStream(bos.toByteArray())));
683 } catch (final Exception e) {
684 throw new IllegalArgumentException("Invalid JSON response:\n" + bos.toString(), e);
687 assertTrue("Root element of Json is not an Object", rootElement.isJsonObject());
689 final Set<Entry<String, JsonElement>> errorsEntrySet = rootElement.getAsJsonObject().entrySet();
690 assertEquals("Json Object element set count", 1, errorsEntrySet.size());
692 final Entry<String, JsonElement> errorsEntry = errorsEntrySet.iterator().next();
693 final JsonElement errorsElement = errorsEntry.getValue();
694 assertEquals("First Json element name", "errors", errorsEntry.getKey());
695 assertTrue("\"errors\" Json element is not an Object", errorsElement.isJsonObject());
697 final Set<Entry<String, JsonElement>> errorListEntrySet = errorsElement.getAsJsonObject().entrySet();
698 assertEquals("Root \"errors\" element child count", 1, errorListEntrySet.size());
700 final JsonElement errorListElement = errorListEntrySet.iterator().next().getValue();
701 assertEquals("\"errors\" child Json element name", "error", errorListEntrySet.iterator().next().getKey());
702 assertTrue("\"error\" Json element is not an Array", errorListElement.isJsonArray());
704 // As a final check, make sure there aren't multiple "error" array
705 // elements. Unfortunately,
706 // the call above to getAsJsonObject().entrySet() will out duplicate
707 // "error" elements. So
708 // we'll use regex on the json string to verify this.
710 final Matcher matcher = Pattern.compile("\"error\"[ ]*:[ ]*\\[", Pattern.DOTALL).matcher(bos.toString());
711 assertTrue("Expected 1 \"error\" element", matcher.find());
712 assertFalse("Found multiple \"error\" elements", matcher.find());
714 return errorListElement.getAsJsonArray();
717 void verifyJsonErrorNode(final JsonElement errorEntryElement, final ErrorType expErrorType,
718 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
719 final ErrorInfoVerifier errorInfoVerifier) {
721 JsonElement errorInfoElement = null;
722 final Map<String, String> leafMap = Maps.newHashMap();
723 for (final Entry<String, JsonElement> entry : errorEntryElement.getAsJsonObject().entrySet()) {
724 final String leafName = entry.getKey();
725 final JsonElement leafElement = entry.getValue();
727 if ("error-info".equals(leafName)) {
728 assertNotNull("Found unexpected \"error-info\" element", errorInfoVerifier);
729 errorInfoElement = leafElement;
731 assertTrue("\"error\" leaf Json element " + leafName + " is not a Primitive",
732 leafElement.isJsonPrimitive());
734 leafMap.put(leafName, leafElement.getAsString());
738 assertEquals("error-type", expErrorType.getErrorTypeTag(), leafMap.remove("error-type"));
739 assertEquals("error-tag", expErrorTag.getTagValue(), leafMap.remove("error-tag"));
741 verifyOptionalJsonLeaf(leafMap.remove("error-message"), expErrorMessage, "error-message");
742 verifyOptionalJsonLeaf(leafMap.remove("error-app-tag"), expErrorAppTag, "error-app-tag");
744 if (!leafMap.isEmpty()) {
745 fail("Found unexpected Json leaf elements for \"error\" element: " + leafMap);
748 if (errorInfoVerifier != null) {
749 assertNotNull("Missing \"error-info\" element", errorInfoElement);
750 errorInfoVerifier.verifyJson(errorInfoElement);
754 void verifyOptionalJsonLeaf(final String actualValue, final String expValue, final String tagName) {
755 if (expValue != null) {
756 assertEquals(tagName, expValue, actualValue);
758 assertNull("Found unexpected \"error\" leaf entry for: " + tagName, actualValue);
762 void verifyXMLResponseBody(final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
763 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
766 final Document doc = parseXMLDocument(stream);
768 final NodeList children = getXMLErrorList(doc, 1);
770 verifyXMLErrorNode(children.item(0), expErrorType, expErrorTag, expErrorMessage, expErrorAppTag,
774 private static Document parseXMLDocument(final InputStream stream) throws IOException, SAXException {
775 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
776 ByteStreams.copy(stream, bos);
778 LOG.debug("XML: " + bos.toString());
780 return UntrustedXML.newDocumentBuilder().parse(new ByteArrayInputStream(bos.toByteArray()));
783 void verifyXMLErrorNode(final Node errorNode, final ErrorType expErrorType, final ErrorTag expErrorTag,
784 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
787 final String errorType = (String) ERROR_TYPE.evaluate(errorNode, XPathConstants.STRING);
788 assertEquals("error-type", expErrorType.getErrorTypeTag(), errorType);
790 final String errorTag = (String) ERROR_TAG.evaluate(errorNode, XPathConstants.STRING);
791 assertEquals("error-tag", expErrorTag.getTagValue(), errorTag);
793 verifyOptionalXMLLeaf(errorNode, ERROR_MESSAGE, expErrorMessage, "error-message");
794 verifyOptionalXMLLeaf(errorNode, ERROR_APP_TAG, expErrorAppTag, "error-app-tag");
796 final Node errorInfoNode = (Node) ERROR_INFO.evaluate(errorNode, XPathConstants.NODE);
797 if (errorInfoVerifier != null) {
798 assertNotNull("Missing \"error-info\" node", errorInfoNode);
800 errorInfoVerifier.verifyXML(errorInfoNode);
802 assertNull("Found unexpected \"error-info\" node", errorInfoNode);
806 void verifyOptionalXMLLeaf(final Node fromNode, final XPathExpression xpath, final String expValue,
807 final String tagName) throws Exception {
808 if (expValue != null) {
809 final String actual = (String) xpath.evaluate(fromNode, XPathConstants.STRING);
810 assertEquals(tagName, expValue, actual);
812 assertNull("Found unexpected \"error\" leaf entry for: " + tagName,
813 xpath.evaluate(fromNode, XPathConstants.NODE));
817 NodeList getXMLErrorList(final Node fromNode, final int count) throws Exception {
818 final NodeList errorList = (NodeList) ERROR_LIST.evaluate(fromNode, XPathConstants.NODESET);
819 assertNotNull("Root errors node is empty", errorList);
820 assertEquals("Root errors node child count", count, errorList.getLength());