2 * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.sal.restconf.impl.test;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertNull;
15 import static org.junit.Assert.assertTrue;
16 import static org.junit.Assert.fail;
17 import static org.mockito.Matchers.any;
18 import static org.mockito.Mockito.mock;
19 import static org.mockito.Mockito.reset;
20 import static org.mockito.Mockito.when;
21 import com.google.common.collect.Maps;
22 import com.google.common.io.ByteStreams;
23 import com.google.gson.JsonArray;
24 import com.google.gson.JsonElement;
25 import com.google.gson.JsonParser;
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.util.Arrays;
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.parsers.DocumentBuilderFactory;
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.controller.sal.rest.api.Draft02;
57 import org.opendaylight.controller.sal.rest.api.RestconfService;
58 import org.opendaylight.controller.sal.rest.impl.JsonNormalizedNodeBodyReader;
59 import org.opendaylight.controller.sal.rest.impl.NormalizedNodeJsonBodyWriter;
60 import org.opendaylight.controller.sal.rest.impl.NormalizedNodeXmlBodyWriter;
61 import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
62 import org.opendaylight.controller.sal.rest.impl.XmlNormalizedNodeBodyReader;
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.Node;
71 import org.w3c.dom.NodeList;
74 * Unit tests for RestconfDocumentedExceptionMapper.
76 * @author Thomas Pantelis
78 public class RestconfDocumentedExceptionMapperTest extends JerseyTest {
80 interface ErrorInfoVerifier {
81 void verifyXML(Node errorInfoNode);
83 void verifyJson(JsonElement errorInfoElement);
86 static class SimpleErrorInfoVerifier implements ErrorInfoVerifier {
88 String expTextContent;
90 public SimpleErrorInfoVerifier(final String expErrorInfo) {
91 expTextContent = expErrorInfo;
94 void verifyContent(final String actualContent) {
95 assertNotNull("Actual \"error-info\" text content is null", actualContent);
96 assertTrue("", actualContent.contains(expTextContent));
100 public void verifyXML(final Node errorInfoNode) {
101 verifyContent(errorInfoNode.getTextContent());
105 public void verifyJson(final JsonElement errorInfoElement) {
106 verifyContent(errorInfoElement.getAsString());
110 static RestconfService mockRestConf = mock(RestconfService.class);
112 static XPath XPATH = XPathFactory.newInstance().newXPath();
113 static XPathExpression ERROR_LIST;
114 static XPathExpression ERROR_TYPE;
115 static XPathExpression ERROR_TAG;
116 static XPathExpression ERROR_MESSAGE;
117 static XPathExpression ERROR_APP_TAG;
118 static XPathExpression ERROR_INFO;
121 public static void init() throws Exception {
122 ControllerContext.getInstance().setGlobalSchema(TestUtils.loadSchemaContext("/modules"));
124 final NamespaceContext nsContext = new NamespaceContext() {
126 public Iterator<?> getPrefixes(final String namespaceURI) {
131 public String getPrefix(final String namespaceURI) {
136 public String getNamespaceURI(final String prefix) {
137 return "ietf-restconf".equals(prefix) ? Draft02.RestConfModule.NAMESPACE : null;
141 XPATH.setNamespaceContext(nsContext);
142 ERROR_LIST = XPATH.compile("ietf-restconf:errors/ietf-restconf:error");
143 ERROR_TYPE = XPATH.compile("ietf-restconf:error-type");
144 ERROR_TAG = XPATH.compile("ietf-restconf:error-tag");
145 ERROR_MESSAGE = XPATH.compile("ietf-restconf:error-message");
146 ERROR_APP_TAG = XPATH.compile("ietf-restconf:error-app-tag");
147 ERROR_INFO = XPATH.compile("ietf-restconf:error-info");
152 public void setUp() throws Exception {
158 protected Application configure() {
159 ResourceConfig resourceConfig = new ResourceConfig();
160 resourceConfig = resourceConfig.registerInstances(mockRestConf, new XmlNormalizedNodeBodyReader(),
161 new JsonNormalizedNodeBodyReader(), new NormalizedNodeJsonBodyWriter(), new NormalizedNodeXmlBodyWriter());
162 resourceConfig.registerClasses(RestconfDocumentedExceptionMapper.class);
163 return resourceConfig;
166 void stageMockEx(final RestconfDocumentedException ex) {
168 when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenThrow(ex);
171 void testJsonResponse(final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
172 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
173 final ErrorInfoVerifier errorInfoVerifier) throws Exception {
177 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
179 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, expStatus);
181 verifyJsonResponseBody(stream, expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, errorInfoVerifier);
185 public void testToJsonResponseWithMessageOnly() throws Exception {
187 testJsonResponse(new RestconfDocumentedException("mock error"), Status.INTERNAL_SERVER_ERROR,
188 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
191 // To test verification code
195 // " error-tag : \"operation-failed\"" +
196 // " ,error-type : \"application\"" +
197 // " ,error-message : \"An error occurred\"" +
198 // " ,error-info : {" +
199 // " session-id: \"123\"" +
200 // " ,address: \"1.2.3.4\"" +
206 // verifyJsonResponseBody( new java.io.StringBufferInputStream(json ),
207 // ErrorType.APPLICATION,
208 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
209 // com.google.common.collect.ImmutableMap.of( "session-id", "123",
210 // "address", "1.2.3.4" ) );
214 public void testToJsonResponseWithInUseErrorTag() throws Exception {
216 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.IN_USE),
217 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.IN_USE, "mock error", null, null);
221 public void testToJsonResponseWithInvalidValueErrorTag() throws Exception {
223 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.RPC, ErrorTag.INVALID_VALUE),
224 Status.BAD_REQUEST, ErrorType.RPC, ErrorTag.INVALID_VALUE, "mock error", null, null);
229 public void testToJsonResponseWithTooBigErrorTag() throws Exception {
231 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.TRANSPORT, ErrorTag.TOO_BIG),
232 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT, ErrorTag.TOO_BIG, "mock error", null, null);
237 public void testToJsonResponseWithMissingAttributeErrorTag() throws Exception {
239 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE),
240 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null);
244 public void testToJsonResponseWithBadAttributeErrorTag() throws Exception {
246 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE),
247 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error", null, null);
251 public void testToJsonResponseWithUnknownAttributeErrorTag() throws Exception {
253 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE),
254 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null);
258 public void testToJsonResponseWithBadElementErrorTag() throws Exception {
260 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT),
261 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null);
265 public void testToJsonResponseWithUnknownElementErrorTag() throws Exception {
267 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT),
268 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null);
272 public void testToJsonResponseWithUnknownNamespaceErrorTag() throws Exception {
274 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE),
275 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null);
279 public void testToJsonResponseWithMalformedMessageErrorTag() throws Exception {
281 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE),
282 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "mock error", null, null);
286 public void testToJsonResponseWithAccessDeniedErrorTag() throws Exception {
288 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED),
289 Status.FORBIDDEN, ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED, "mock error", null, null);
293 public void testToJsonResponseWithLockDeniedErrorTag() throws Exception {
295 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED),
296 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED, "mock error", null, null);
300 public void testToJsonResponseWithResourceDeniedErrorTag() throws Exception {
302 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED),
303 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED, "mock error", null, null);
307 public void testToJsonResponseWithRollbackFailedErrorTag() throws Exception {
309 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED),
310 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED, "mock error", null, null);
314 public void testToJsonResponseWithDataExistsErrorTag() throws Exception {
316 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS),
317 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, "mock error", null, null);
321 public void testToJsonResponseWithDataMissingErrorTag() throws Exception {
323 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING),
324 Status.NOT_FOUND, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
328 public void testToJsonResponseWithOperationNotSupportedErrorTag() throws Exception {
330 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL,
331 ErrorTag.OPERATION_NOT_SUPPORTED), Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
332 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null);
336 public void testToJsonResponseWithOperationFailedErrorTag() throws Exception {
338 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED),
339 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED, "mock error", null, null);
343 public void testToJsonResponseWithPartialOperationErrorTag() throws Exception {
345 testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION),
346 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION, "mock error", null, null);
350 public void testToJsonResponseWithErrorAppTag() throws Exception {
352 testJsonResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
353 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag")), Status.BAD_REQUEST, ErrorType.APPLICATION,
354 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null);
358 @Ignore // FIXME : find why it return "error-type" RPC no expected APPLICATION
359 public void testToJsonResponseWithMultipleErrors() throws Exception {
361 final List<RestconfError> errorList = Arrays.asList(new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
362 "mock error1"), new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2"));
363 stageMockEx(new RestconfDocumentedException("mock", null, errorList));
365 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
367 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, Status.CONFLICT);
369 final JsonArray arrayElement = parseJsonErrorArrayElement(stream);
371 assertEquals("\"error\" Json array element length", 2, arrayElement.size());
373 verifyJsonErrorNode(arrayElement.get(0), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1", null, null);
375 verifyJsonErrorNode(arrayElement.get(1), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2", null, null);
379 public void testToJsonResponseWithErrorInfo() throws Exception {
381 final String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
382 testJsonResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
383 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", errorInfo)), Status.BAD_REQUEST,
384 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
385 new SimpleErrorInfoVerifier(errorInfo));
389 public void testToJsonResponseWithExceptionCause() throws Exception {
391 final Exception cause = new Exception("mock exception cause");
392 testJsonResponse(new RestconfDocumentedException("mock error", cause), Status.INTERNAL_SERVER_ERROR,
393 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null,
394 new SimpleErrorInfoVerifier(cause.getMessage()));
397 void testXMLResponse(final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
398 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
399 final ErrorInfoVerifier errorInfoVerifier) throws Exception {
402 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get();
404 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_XML, expStatus);
406 verifyXMLResponseBody(stream, expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, errorInfoVerifier);
410 public void testToXMLResponseWithMessageOnly() throws Exception {
412 testXMLResponse(new RestconfDocumentedException("mock error"), Status.INTERNAL_SERVER_ERROR,
413 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
415 // To test verification code
417 // "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">"+
419 // " <error-type>application</error-type>"+
420 // " <error-tag>operation-failed</error-tag>"+
421 // " <error-message>An error occurred</error-message>"+
423 // " <session-id>123</session-id>" +
424 // " <address>1.2.3.4</address>" +
425 // " </error-info>" +
429 // verifyXMLResponseBody( new java.io.StringBufferInputStream(xml),
430 // ErrorType.APPLICATION,
431 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
432 // com.google.common.collect.ImmutableMap.of( "session-id", "123",
433 // "address", "1.2.3.4" ) );
437 public void testToXMLResponseWithInUseErrorTag() throws Exception {
439 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.IN_USE),
440 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.IN_USE, "mock error", null, null);
444 public void testToXMLResponseWithInvalidValueErrorTag() throws Exception {
446 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.RPC, ErrorTag.INVALID_VALUE),
447 Status.BAD_REQUEST, ErrorType.RPC, ErrorTag.INVALID_VALUE, "mock error", null, null);
451 public void testToXMLResponseWithTooBigErrorTag() throws Exception {
453 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.TRANSPORT, ErrorTag.TOO_BIG),
454 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT, ErrorTag.TOO_BIG, "mock error", null, null);
458 public void testToXMLResponseWithMissingAttributeErrorTag() throws Exception {
460 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE),
461 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null);
465 public void testToXMLResponseWithBadAttributeErrorTag() throws Exception {
467 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE),
468 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error", null, null);
472 public void testToXMLResponseWithUnknownAttributeErrorTag() throws Exception {
474 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE),
475 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null);
479 public void testToXMLResponseWithBadElementErrorTag() throws Exception {
481 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT),
482 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null);
486 public void testToXMLResponseWithUnknownElementErrorTag() throws Exception {
488 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT),
489 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null);
493 public void testToXMLResponseWithUnknownNamespaceErrorTag() throws Exception {
495 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE),
496 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null);
500 public void testToXMLResponseWithMalformedMessageErrorTag() throws Exception {
502 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE),
503 Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "mock error", null, null);
507 public void testToXMLResponseWithAccessDeniedErrorTag() throws Exception {
509 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED),
510 Status.FORBIDDEN, ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED, "mock error", null, null);
514 public void testToXMLResponseWithLockDeniedErrorTag() throws Exception {
516 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED),
517 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED, "mock error", null, null);
521 public void testToXMLResponseWithResourceDeniedErrorTag() throws Exception {
523 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED),
524 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED, "mock error", null, null);
528 public void testToXMLResponseWithRollbackFailedErrorTag() throws Exception {
530 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED),
531 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED, "mock error", null, null);
535 public void testToXMLResponseWithDataExistsErrorTag() throws Exception {
537 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS),
538 Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, "mock error", null, null);
542 public void testToXMLResponseWithDataMissingErrorTag() throws Exception {
544 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING),
545 Status.NOT_FOUND, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
549 public void testToXMLResponseWithOperationNotSupportedErrorTag() throws Exception {
551 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL,
552 ErrorTag.OPERATION_NOT_SUPPORTED), Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
553 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null);
557 public void testToXMLResponseWithOperationFailedErrorTag() throws Exception {
559 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED),
560 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED, "mock error", null, null);
564 public void testToXMLResponseWithPartialOperationErrorTag() throws Exception {
566 testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION),
567 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION, "mock error", null, null);
571 public void testToXMLResponseWithErrorAppTag() throws Exception {
573 testXMLResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
574 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag")), Status.BAD_REQUEST, ErrorType.APPLICATION,
575 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null);
579 public void testToXMLResponseWithErrorInfo() throws Exception {
581 final String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
582 testXMLResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION,
583 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", errorInfo)), Status.BAD_REQUEST,
584 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
585 new SimpleErrorInfoVerifier(errorInfo));
589 public void testToXMLResponseWithExceptionCause() throws Exception {
591 final Exception cause = new Exception("mock exception cause");
592 testXMLResponse(new RestconfDocumentedException("mock error", cause), Status.INTERNAL_SERVER_ERROR,
593 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null,
594 new SimpleErrorInfoVerifier(cause.getMessage()));
598 @Ignore // FIXME : find why it return error-type as RPC no APPLICATION
599 public void testToXMLResponseWithMultipleErrors() throws Exception {
601 final List<RestconfError> errorList = Arrays.asList(new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
602 "mock error1"), new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2"));
603 stageMockEx(new RestconfDocumentedException("mock", null, errorList));
605 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get();
607 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_XML, Status.CONFLICT);
609 final Document doc = parseXMLDocument(stream);
611 final NodeList children = getXMLErrorList(doc, 2);
613 verifyXMLErrorNode(children.item(0), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1", null, null);
615 verifyXMLErrorNode(children.item(1), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2", null, null);
619 public void testToResponseWithAcceptHeader() throws Exception {
621 stageMockEx(new RestconfDocumentedException("mock error"));
623 final Response resp = target("/operational/foo").request().header("Accept", MediaType.APPLICATION_JSON).get();
625 final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, Status.INTERNAL_SERVER_ERROR);
627 verifyJsonResponseBody(stream, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null);
632 public void testToResponseWithStatusOnly() throws Exception {
634 // The StructuredDataToJsonProvider should throw a
635 // RestconfDocumentedException with no data
637 when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenReturn(
638 new NormalizedNodeContext(null, null));
640 final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
642 verifyResponse(resp, MediaType.TEXT_PLAIN, Status.NOT_FOUND);
645 InputStream verifyResponse(final Response resp, final String expMediaType, final Status expStatus) {
646 assertEquals("getMediaType", MediaType.valueOf(expMediaType), resp.getMediaType());
647 assertEquals("getStatus", expStatus.getStatusCode(), resp.getStatus());
649 final Object entity = resp.getEntity();
650 assertEquals("Response entity", true, entity instanceof InputStream);
651 final InputStream stream = (InputStream) entity;
655 void verifyJsonResponseBody(final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
656 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
659 final JsonArray arrayElement = parseJsonErrorArrayElement(stream);
661 assertEquals("\"error\" Json array element length", 1, arrayElement.size());
663 verifyJsonErrorNode(arrayElement.get(0), expErrorType, expErrorTag, expErrorMessage, expErrorAppTag,
667 private JsonArray parseJsonErrorArrayElement(final InputStream stream) throws IOException {
668 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
669 ByteStreams.copy(stream, bos);
671 System.out.println("JSON: " + bos.toString());
673 final JsonParser parser = new JsonParser();
674 JsonElement rootElement;
677 rootElement = parser.parse(new InputStreamReader(new ByteArrayInputStream(bos.toByteArray())));
678 } catch (final Exception e) {
679 throw new IllegalArgumentException("Invalid JSON response:\n" + bos.toString(), e);
682 assertTrue("Root element of Json is not an Object", rootElement.isJsonObject());
684 final Set<Entry<String, JsonElement>> errorsEntrySet = rootElement.getAsJsonObject().entrySet();
685 assertEquals("Json Object element set count", 1, errorsEntrySet.size());
687 final Entry<String, JsonElement> errorsEntry = errorsEntrySet.iterator().next();
688 final JsonElement errorsElement = errorsEntry.getValue();
689 assertEquals("First Json element name", "errors", errorsEntry.getKey());
690 assertTrue("\"errors\" Json element is not an Object", errorsElement.isJsonObject());
692 final Set<Entry<String, JsonElement>> errorListEntrySet = errorsElement.getAsJsonObject().entrySet();
693 assertEquals("Root \"errors\" element child count", 1, errorListEntrySet.size());
695 final JsonElement errorListElement = errorListEntrySet.iterator().next().getValue();
696 assertEquals("\"errors\" child Json element name", "error", errorListEntrySet.iterator().next().getKey());
697 assertTrue("\"error\" Json element is not an Array", errorListElement.isJsonArray());
699 // As a final check, make sure there aren't multiple "error" array
700 // elements. Unfortunately,
701 // the call above to getAsJsonObject().entrySet() will out duplicate
702 // "error" elements. So
703 // we'll use regex on the json string to verify this.
705 final Matcher matcher = Pattern.compile("\"error\"[ ]*:[ ]*\\[", Pattern.DOTALL).matcher(bos.toString());
706 assertTrue("Expected 1 \"error\" element", matcher.find());
707 assertFalse("Found multiple \"error\" elements", matcher.find());
709 return errorListElement.getAsJsonArray();
712 void verifyJsonErrorNode(final JsonElement errorEntryElement, final ErrorType expErrorType,
713 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
714 final ErrorInfoVerifier errorInfoVerifier) {
716 JsonElement errorInfoElement = null;
717 final Map<String, String> leafMap = Maps.newHashMap();
718 for (final Entry<String, JsonElement> entry : errorEntryElement.getAsJsonObject().entrySet()) {
719 final String leafName = entry.getKey();
720 final JsonElement leafElement = entry.getValue();
722 if ("error-info".equals(leafName)) {
723 assertNotNull("Found unexpected \"error-info\" element", errorInfoVerifier);
724 errorInfoElement = leafElement;
726 assertTrue("\"error\" leaf Json element " + leafName + " is not a Primitive",
727 leafElement.isJsonPrimitive());
729 leafMap.put(leafName, leafElement.getAsString());
733 assertEquals("error-type", expErrorType.getErrorTypeTag(), leafMap.remove("error-type"));
734 assertEquals("error-tag", expErrorTag.getTagValue(), leafMap.remove("error-tag"));
736 verifyOptionalJsonLeaf(leafMap.remove("error-message"), expErrorMessage, "error-message");
737 verifyOptionalJsonLeaf(leafMap.remove("error-app-tag"), expErrorAppTag, "error-app-tag");
739 if (!leafMap.isEmpty()) {
740 fail("Found unexpected Json leaf elements for \"error\" element: " + leafMap);
743 if (errorInfoVerifier != null) {
744 assertNotNull("Missing \"error-info\" element", errorInfoElement);
745 errorInfoVerifier.verifyJson(errorInfoElement);
749 void verifyOptionalJsonLeaf(final String actualValue, final String expValue, final String tagName) {
750 if (expValue != null) {
751 assertEquals(tagName, expValue, actualValue);
753 assertNull("Found unexpected \"error\" leaf entry for: " + tagName, actualValue);
757 void verifyXMLResponseBody(final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
758 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
761 final Document doc = parseXMLDocument(stream);
763 final NodeList children = getXMLErrorList(doc, 1);
765 verifyXMLErrorNode(children.item(0), expErrorType, expErrorTag, expErrorMessage, expErrorAppTag,
769 private Document parseXMLDocument(final InputStream stream) throws IOException {
770 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
771 factory.setNamespaceAware(true);
772 factory.setCoalescing(true);
773 factory.setIgnoringElementContentWhitespace(true);
774 factory.setIgnoringComments(true);
776 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
777 ByteStreams.copy(stream, bos);
779 System.out.println("XML: " + bos.toString());
783 doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(bos.toByteArray()));
784 } catch (final Exception e) {
785 throw new IllegalArgumentException("Invalid XML response:\n" + bos.toString(), e);
790 void verifyXMLErrorNode(final Node errorNode, final ErrorType expErrorType, final ErrorTag expErrorTag,
791 final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier)
794 final String errorType = (String) ERROR_TYPE.evaluate(errorNode, XPathConstants.STRING);
795 assertEquals("error-type", expErrorType.getErrorTypeTag(), errorType);
797 final String errorTag = (String) ERROR_TAG.evaluate(errorNode, XPathConstants.STRING);
798 assertEquals("error-tag", expErrorTag.getTagValue(), errorTag);
800 verifyOptionalXMLLeaf(errorNode, ERROR_MESSAGE, expErrorMessage, "error-message");
801 verifyOptionalXMLLeaf(errorNode, ERROR_APP_TAG, expErrorAppTag, "error-app-tag");
803 final Node errorInfoNode = (Node) ERROR_INFO.evaluate(errorNode, XPathConstants.NODE);
804 if (errorInfoVerifier != null) {
805 assertNotNull("Missing \"error-info\" node", errorInfoNode);
807 errorInfoVerifier.verifyXML(errorInfoNode);
809 assertNull("Found unexpected \"error-info\" node", errorInfoNode);
813 void verifyOptionalXMLLeaf(final Node fromNode, final XPathExpression xpath, final String expValue,
814 final String tagName) throws Exception {
815 if (expValue != null) {
816 final String actual = (String) xpath.evaluate(fromNode, XPathConstants.STRING);
817 assertEquals(tagName, expValue, actual);
819 assertNull("Found unexpected \"error\" leaf entry for: " + tagName,
820 xpath.evaluate(fromNode, XPathConstants.NODE));
824 NodeList getXMLErrorList(final Node fromNode, final int count) throws Exception {
825 final NodeList errorList = (NodeList) ERROR_LIST.evaluate(fromNode, XPathConstants.NODESET);
826 assertNotNull("Root errors node is empty", errorList);
827 assertEquals("Root errors node child count", count, errorList.getLength());