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.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.Matchers.any;
17 import static org.mockito.Mockito.mock;
18 import static org.mockito.Mockito.reset;
19 import static org.mockito.Mockito.when;
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.util.Arrays;
27 import java.util.Iterator;
28 import java.util.List;
30 import java.util.Map.Entry;
33 import javax.ws.rs.core.Application;
34 import javax.ws.rs.core.MediaType;
35 import javax.ws.rs.core.Response;
36 import javax.ws.rs.core.Response.Status;
37 import javax.xml.namespace.NamespaceContext;
38 import javax.xml.parsers.DocumentBuilderFactory;
39 import javax.xml.xpath.XPath;
40 import javax.xml.xpath.XPathConstants;
41 import javax.xml.xpath.XPathExpression;
42 import javax.xml.xpath.XPathFactory;
44 import org.glassfish.jersey.server.ResourceConfig;
45 import org.glassfish.jersey.test.JerseyTest;
46 import org.junit.Before;
47 import org.junit.BeforeClass;
48 import org.junit.Test;
49 import org.opendaylight.controller.sal.rest.api.Draft02;
50 import org.opendaylight.controller.sal.rest.api.RestconfService;
51 import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
52 import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
53 import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
54 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
55 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
56 import org.opendaylight.controller.sal.restconf.impl.RestconfError;
57 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
58 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
59 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
60 import org.w3c.dom.Document;
61 import org.w3c.dom.Element;
62 import org.w3c.dom.Node;
63 import org.w3c.dom.NodeList;
65 import com.google.common.collect.ImmutableMap;
66 import com.google.common.collect.Maps;
67 import com.google.common.io.ByteStreams;
68 import com.google.gson.JsonArray;
69 import com.google.gson.JsonElement;
70 import com.google.gson.JsonParser;
73 * Unit tests for RestconfDocumentedExceptionMapper.
75 * @author Thomas Pantelis
77 public class RestconfDocumentedExceptionMapperTest extends JerseyTest {
79 interface ErrorInfoVerifier {
80 void verifyXML( Node errorInfoNode );
81 void verifyJson( JsonElement errorInfoElement );
84 static class ComplexErrorInfoVerifier implements ErrorInfoVerifier {
86 Map<String, String> expErrorInfo;
88 public ComplexErrorInfoVerifier( final Map<String, String> expErrorInfo ) {
89 this.expErrorInfo = expErrorInfo;
93 public void verifyXML( final Node errorInfoNode ) {
95 Map<String, String> mutableExpMap = Maps.newHashMap( expErrorInfo );
96 NodeList childNodes = errorInfoNode.getChildNodes();
97 for( int i = 0; i < childNodes.getLength(); i++ ) {
98 Node child = childNodes.item( i );
99 if( child instanceof Element ) {
100 String expValue = mutableExpMap.remove( child.getNodeName() );
101 assertNotNull( "Found unexpected \"error-info\" child node: " +
102 child.getNodeName(), expValue );
103 assertEquals( "Text content for \"error-info\" child node " +
104 child.getNodeName(), expValue, child.getTextContent() );
108 if( !mutableExpMap.isEmpty() ) {
109 fail( "Missing \"error-info\" child nodes: " + mutableExpMap );
114 public void verifyJson( final JsonElement errorInfoElement ) {
116 assertTrue( "\"error-info\" Json element is not an Object",
117 errorInfoElement.isJsonObject() );
119 Map<String, String> actualErrorInfo = Maps.newHashMap();
120 for( Entry<String, JsonElement> entry: errorInfoElement.getAsJsonObject().entrySet() ) {
121 String leafName = entry.getKey();
122 JsonElement leafElement = entry.getValue();
123 actualErrorInfo.put( leafName, leafElement.getAsString() );
126 Map<String, String> mutableExpMap = Maps.newHashMap( expErrorInfo );
127 for( Entry<String,String> actual: actualErrorInfo.entrySet() ) {
128 String expValue = mutableExpMap.remove( actual.getKey() );
129 assertNotNull( "Found unexpected \"error-info\" child node: " +
130 actual.getKey(), expValue );
131 assertEquals( "Text content for \"error-info\" child node " +
132 actual.getKey(), expValue, actual.getValue() );
135 if( !mutableExpMap.isEmpty() ) {
136 fail( "Missing \"error-info\" child nodes: " + mutableExpMap );
141 static class SimpleErrorInfoVerifier implements ErrorInfoVerifier {
143 String expTextContent;
145 public SimpleErrorInfoVerifier( final String expErrorInfo ) {
146 this.expTextContent = expErrorInfo;
149 void verifyContent( final String actualContent ) {
150 assertNotNull( "Actual \"error-info\" text content is null", actualContent );
151 assertTrue( "", actualContent.contains( expTextContent ) );
155 public void verifyXML( final Node errorInfoNode ) {
156 verifyContent( errorInfoNode.getTextContent() );
160 public void verifyJson( final JsonElement errorInfoElement ) {
161 verifyContent( errorInfoElement.getAsString() );
165 static RestconfService mockRestConf = mock( RestconfService.class );
167 static XPath XPATH = XPathFactory.newInstance().newXPath();
168 static XPathExpression ERROR_LIST;
169 static XPathExpression ERROR_TYPE;
170 static XPathExpression ERROR_TAG;
171 static XPathExpression ERROR_MESSAGE;
172 static XPathExpression ERROR_APP_TAG;
173 static XPathExpression ERROR_INFO;
176 public static void init() throws Exception {
177 ControllerContext.getInstance().setGlobalSchema( TestUtils.loadSchemaContext("/modules") );
179 NamespaceContext nsContext = new NamespaceContext() {
181 public Iterator<?> getPrefixes( final String namespaceURI ) {
186 public String getPrefix( final String namespaceURI ) {
191 public String getNamespaceURI( final String prefix ) {
192 return "ietf-restconf".equals( prefix ) ? Draft02.RestConfModule.NAMESPACE : null;
196 XPATH.setNamespaceContext( nsContext );
197 ERROR_LIST = XPATH.compile( "ietf-restconf:errors/ietf-restconf:error" );
198 ERROR_TYPE = XPATH.compile( "ietf-restconf:error-type" );
199 ERROR_TAG = XPATH.compile( "ietf-restconf:error-tag" );
200 ERROR_MESSAGE = XPATH.compile( "ietf-restconf:error-message" );
201 ERROR_APP_TAG = XPATH.compile( "ietf-restconf:error-app-tag" );
202 ERROR_INFO = XPATH.compile( "ietf-restconf:error-info" );
207 public void setUp() throws Exception {
208 reset( mockRestConf );
213 protected Application configure() {
214 ResourceConfig resourceConfig = new ResourceConfig();
215 resourceConfig = resourceConfig.registerInstances( mockRestConf, StructuredDataToXmlProvider.INSTANCE,
216 StructuredDataToJsonProvider.INSTANCE );
217 resourceConfig.registerClasses( RestconfDocumentedExceptionMapper.class );
218 return resourceConfig;
221 void stageMockEx( final RestconfDocumentedException ex ) {
222 reset( mockRestConf );
223 when( mockRestConf.readOperationalData( any( String.class ) ) ).thenThrow( ex );
226 void testJsonResponse( final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
227 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
228 final ErrorInfoVerifier errorInfoVerifier ) throws Exception {
232 Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
234 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_JSON, expStatus );
236 verifyJsonResponseBody( stream, expErrorType, expErrorTag, expErrorMessage,
237 expErrorAppTag, errorInfoVerifier );
241 public void testToJsonResponseWithMessageOnly() throws Exception {
243 testJsonResponse( new RestconfDocumentedException( "mock error" ), Status.INTERNAL_SERVER_ERROR,
244 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null );
246 // To test verification code
250 // " error-tag : \"operation-failed\"" +
251 // " ,error-type : \"application\"" +
252 // " ,error-message : \"An error occurred\"" +
253 // " ,error-info : {" +
254 // " session-id: \"123\"" +
255 // " ,address: \"1.2.3.4\"" +
261 // verifyJsonResponseBody( new java.io.StringBufferInputStream(json ), ErrorType.APPLICATION,
262 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
263 // com.google.common.collect.ImmutableMap.of( "session-id", "123", "address", "1.2.3.4" ) );
267 public void testToJsonResponseWithInUseErrorTag() throws Exception {
269 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
271 Status.CONFLICT, ErrorType.PROTOCOL,
272 ErrorTag.IN_USE, "mock error", null, null );
276 public void testToJsonResponseWithInvalidValueErrorTag() throws Exception {
278 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.RPC,
279 ErrorTag.INVALID_VALUE ),
280 Status.BAD_REQUEST, ErrorType.RPC,
281 ErrorTag.INVALID_VALUE, "mock error", null, null );
286 public void testToJsonResponseWithTooBigErrorTag() throws Exception {
288 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.TRANSPORT,
290 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT,
291 ErrorTag.TOO_BIG, "mock error", null, null );
296 public void testToJsonResponseWithMissingAttributeErrorTag() throws Exception {
298 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
299 ErrorTag.MISSING_ATTRIBUTE ),
300 Status.BAD_REQUEST, ErrorType.PROTOCOL,
301 ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null );
305 public void testToJsonResponseWithBadAttributeErrorTag() throws Exception {
307 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
308 ErrorTag.BAD_ATTRIBUTE ),
309 Status.BAD_REQUEST, ErrorType.PROTOCOL,
310 ErrorTag.BAD_ATTRIBUTE, "mock error", null, null );
313 public void testToJsonResponseWithUnknownAttributeErrorTag() throws Exception {
315 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
316 ErrorTag.UNKNOWN_ATTRIBUTE ),
317 Status.BAD_REQUEST, ErrorType.PROTOCOL,
318 ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null );
322 public void testToJsonResponseWithBadElementErrorTag() throws Exception {
324 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
325 ErrorTag.BAD_ELEMENT ),
327 ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null );
331 public void testToJsonResponseWithUnknownElementErrorTag() throws Exception {
333 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
334 ErrorTag.UNKNOWN_ELEMENT ),
335 Status.BAD_REQUEST, ErrorType.PROTOCOL,
336 ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null );
340 public void testToJsonResponseWithUnknownNamespaceErrorTag() throws Exception {
342 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
343 ErrorTag.UNKNOWN_NAMESPACE ),
344 Status.BAD_REQUEST, ErrorType.PROTOCOL,
345 ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null );
349 public void testToJsonResponseWithMalformedMessageErrorTag() throws Exception {
351 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
352 ErrorTag.MALFORMED_MESSAGE ),
353 Status.BAD_REQUEST, ErrorType.PROTOCOL,
354 ErrorTag.MALFORMED_MESSAGE, "mock error", null, null );
358 public void testToJsonResponseWithAccessDeniedErrorTag() throws Exception {
360 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
361 ErrorTag.ACCESS_DENIED ),
362 Status.FORBIDDEN, ErrorType.PROTOCOL,
363 ErrorTag.ACCESS_DENIED, "mock error", null, null );
367 public void testToJsonResponseWithLockDeniedErrorTag() throws Exception {
369 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
370 ErrorTag.LOCK_DENIED ),
371 Status.CONFLICT, ErrorType.PROTOCOL,
372 ErrorTag.LOCK_DENIED, "mock error", null, null );
376 public void testToJsonResponseWithResourceDeniedErrorTag() throws Exception {
378 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
379 ErrorTag.RESOURCE_DENIED ),
380 Status.CONFLICT, ErrorType.PROTOCOL,
381 ErrorTag.RESOURCE_DENIED, "mock error", null, null );
385 public void testToJsonResponseWithRollbackFailedErrorTag() throws Exception {
387 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
388 ErrorTag.ROLLBACK_FAILED ),
389 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
390 ErrorTag.ROLLBACK_FAILED, "mock error", null, null );
394 public void testToJsonResponseWithDataExistsErrorTag() throws Exception {
396 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
397 ErrorTag.DATA_EXISTS ),
398 Status.CONFLICT, ErrorType.PROTOCOL,
399 ErrorTag.DATA_EXISTS, "mock error", null, null );
403 public void testToJsonResponseWithDataMissingErrorTag() throws Exception {
405 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
406 ErrorTag.DATA_MISSING ),
407 Status.CONFLICT, ErrorType.PROTOCOL,
408 ErrorTag.DATA_MISSING, "mock error", null, null );
412 public void testToJsonResponseWithOperationNotSupportedErrorTag() throws Exception {
414 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
415 ErrorTag.OPERATION_NOT_SUPPORTED ),
416 Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
417 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null );
421 public void testToJsonResponseWithOperationFailedErrorTag() throws Exception {
423 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
424 ErrorTag.OPERATION_FAILED ),
425 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
426 ErrorTag.OPERATION_FAILED, "mock error", null, null );
430 public void testToJsonResponseWithPartialOperationErrorTag() throws Exception {
432 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
433 ErrorTag.PARTIAL_OPERATION ),
434 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
435 ErrorTag.PARTIAL_OPERATION, "mock error", null, null );
439 public void testToJsonResponseWithErrorAppTag() throws Exception {
441 testJsonResponse( new RestconfDocumentedException( new RestconfError(
442 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
443 "mock error", "mock-app-tag" ) ),
444 Status.BAD_REQUEST, ErrorType.APPLICATION,
445 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null );
449 public void testToJsonResponseWithMultipleErrors() throws Exception {
451 List<RestconfError> errorList = Arrays.asList(
452 new RestconfError( ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1" ),
453 new RestconfError( ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2" ) );
454 stageMockEx( new RestconfDocumentedException( errorList ) );
456 Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
458 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_JSON, Status.CONFLICT );
460 JsonArray arrayElement = parseJsonErrorArrayElement( stream );
462 assertEquals( "\"error\" Json array element length", 2, arrayElement.size() );
464 verifyJsonErrorNode( arrayElement.get( 0 ), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
465 "mock error1", null, null );
467 verifyJsonErrorNode( arrayElement.get( 1 ), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED,
468 "mock error2", null, null );
472 public void testToJsonResponseWithErrorInfo() throws Exception {
474 String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
475 testJsonResponse( new RestconfDocumentedException( new RestconfError(
476 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
477 "mock error", "mock-app-tag", errorInfo ) ),
478 Status.BAD_REQUEST, ErrorType.APPLICATION,
479 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
480 new ComplexErrorInfoVerifier( ImmutableMap.of(
481 "session-id", "123", "address", "1.2.3.4" ) ) );
485 public void testToJsonResponseWithExceptionCause() throws Exception {
487 Exception cause = new Exception( "mock exception cause" );
488 testJsonResponse( new RestconfDocumentedException( "mock error", cause ),
489 Status.INTERNAL_SERVER_ERROR, ErrorType.APPLICATION,
490 ErrorTag.OPERATION_FAILED, "mock error", null,
491 new SimpleErrorInfoVerifier( cause.getMessage() ) );
494 void testXMLResponse( final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
495 final ErrorTag expErrorTag, final String expErrorMessage,
496 final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier ) throws Exception
500 Response resp = target("/operational/foo").request( MediaType.APPLICATION_XML ).get();
502 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_XML, expStatus );
504 verifyXMLResponseBody( stream, expErrorType, expErrorTag, expErrorMessage,
505 expErrorAppTag, errorInfoVerifier );
509 public void testToXMLResponseWithMessageOnly() throws Exception {
511 testXMLResponse( new RestconfDocumentedException( "mock error" ), Status.INTERNAL_SERVER_ERROR,
512 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null );
514 // To test verification code
516 // "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">"+
518 // " <error-type>application</error-type>"+
519 // " <error-tag>operation-failed</error-tag>"+
520 // " <error-message>An error occurred</error-message>"+
522 // " <session-id>123</session-id>" +
523 // " <address>1.2.3.4</address>" +
524 // " </error-info>" +
528 // verifyXMLResponseBody( new java.io.StringBufferInputStream(xml), ErrorType.APPLICATION,
529 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
530 // com.google.common.collect.ImmutableMap.of( "session-id", "123", "address", "1.2.3.4" ) );
534 public void testToXMLResponseWithInUseErrorTag() throws Exception {
536 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
538 Status.CONFLICT, ErrorType.PROTOCOL,
539 ErrorTag.IN_USE, "mock error", null, null );
543 public void testToXMLResponseWithInvalidValueErrorTag() throws Exception {
545 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.RPC,
546 ErrorTag.INVALID_VALUE ),
547 Status.BAD_REQUEST, ErrorType.RPC,
548 ErrorTag.INVALID_VALUE, "mock error", null, null );
552 public void testToXMLResponseWithTooBigErrorTag() throws Exception {
554 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.TRANSPORT,
556 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT,
557 ErrorTag.TOO_BIG, "mock error", null, null );
561 public void testToXMLResponseWithMissingAttributeErrorTag() throws Exception {
563 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
564 ErrorTag.MISSING_ATTRIBUTE ),
565 Status.BAD_REQUEST, ErrorType.PROTOCOL,
566 ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null );
570 public void testToXMLResponseWithBadAttributeErrorTag() throws Exception {
572 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
573 ErrorTag.BAD_ATTRIBUTE ),
574 Status.BAD_REQUEST, ErrorType.PROTOCOL,
575 ErrorTag.BAD_ATTRIBUTE, "mock error", null, null );
578 public void testToXMLResponseWithUnknownAttributeErrorTag() throws Exception {
580 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
581 ErrorTag.UNKNOWN_ATTRIBUTE ),
582 Status.BAD_REQUEST, ErrorType.PROTOCOL,
583 ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null );
587 public void testToXMLResponseWithBadElementErrorTag() throws Exception {
589 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
590 ErrorTag.BAD_ELEMENT ),
591 Status.BAD_REQUEST, ErrorType.PROTOCOL,
592 ErrorTag.BAD_ELEMENT, "mock error", null, null );
596 public void testToXMLResponseWithUnknownElementErrorTag() throws Exception {
598 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
599 ErrorTag.UNKNOWN_ELEMENT ),
600 Status.BAD_REQUEST, ErrorType.PROTOCOL,
601 ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null );
605 public void testToXMLResponseWithUnknownNamespaceErrorTag() throws Exception {
607 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
608 ErrorTag.UNKNOWN_NAMESPACE ),
609 Status.BAD_REQUEST, ErrorType.PROTOCOL,
610 ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null );
614 public void testToXMLResponseWithMalformedMessageErrorTag() throws Exception {
616 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
617 ErrorTag.MALFORMED_MESSAGE ),
618 Status.BAD_REQUEST, ErrorType.PROTOCOL,
619 ErrorTag.MALFORMED_MESSAGE, "mock error", null, null );
623 public void testToXMLResponseWithAccessDeniedErrorTag() throws Exception {
625 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
626 ErrorTag.ACCESS_DENIED ),
627 Status.FORBIDDEN, ErrorType.PROTOCOL,
628 ErrorTag.ACCESS_DENIED, "mock error", null, null );
632 public void testToXMLResponseWithLockDeniedErrorTag() throws Exception {
634 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
635 ErrorTag.LOCK_DENIED ),
636 Status.CONFLICT, ErrorType.PROTOCOL,
637 ErrorTag.LOCK_DENIED, "mock error", null, null );
641 public void testToXMLResponseWithResourceDeniedErrorTag() throws Exception {
643 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
644 ErrorTag.RESOURCE_DENIED ),
645 Status.CONFLICT, ErrorType.PROTOCOL,
646 ErrorTag.RESOURCE_DENIED, "mock error", null, null );
650 public void testToXMLResponseWithRollbackFailedErrorTag() throws Exception {
652 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
653 ErrorTag.ROLLBACK_FAILED ),
654 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
655 ErrorTag.ROLLBACK_FAILED, "mock error", null, null );
659 public void testToXMLResponseWithDataExistsErrorTag() throws Exception {
661 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
662 ErrorTag.DATA_EXISTS ),
663 Status.CONFLICT, ErrorType.PROTOCOL,
664 ErrorTag.DATA_EXISTS, "mock error", null, null );
668 public void testToXMLResponseWithDataMissingErrorTag() throws Exception {
670 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
671 ErrorTag.DATA_MISSING ),
672 Status.CONFLICT, ErrorType.PROTOCOL,
673 ErrorTag.DATA_MISSING, "mock error", null, null );
677 public void testToXMLResponseWithOperationNotSupportedErrorTag() throws Exception {
679 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
680 ErrorTag.OPERATION_NOT_SUPPORTED ),
681 Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
682 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null );
686 public void testToXMLResponseWithOperationFailedErrorTag() throws Exception {
688 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
689 ErrorTag.OPERATION_FAILED ),
690 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
691 ErrorTag.OPERATION_FAILED, "mock error", null, null );
695 public void testToXMLResponseWithPartialOperationErrorTag() throws Exception {
697 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
698 ErrorTag.PARTIAL_OPERATION ),
699 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
700 ErrorTag.PARTIAL_OPERATION, "mock error", null, null );
704 public void testToXMLResponseWithErrorAppTag() throws Exception {
706 testXMLResponse( new RestconfDocumentedException( new RestconfError(
707 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
708 "mock error", "mock-app-tag" ) ),
709 Status.BAD_REQUEST, ErrorType.APPLICATION,
710 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null );
714 public void testToXMLResponseWithErrorInfo() throws Exception {
716 String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
717 testXMLResponse( new RestconfDocumentedException( new RestconfError(
718 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
719 "mock error", "mock-app-tag", errorInfo ) ),
720 Status.BAD_REQUEST, ErrorType.APPLICATION,
721 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
722 new ComplexErrorInfoVerifier( ImmutableMap.of(
723 "session-id", "123", "address", "1.2.3.4" ) ) );
727 public void testToXMLResponseWithExceptionCause() throws Exception {
729 Exception cause = new Exception( "mock exception cause" );
730 testXMLResponse( new RestconfDocumentedException( "mock error", cause ),
731 Status.INTERNAL_SERVER_ERROR, ErrorType.APPLICATION,
732 ErrorTag.OPERATION_FAILED, "mock error", null,
733 new SimpleErrorInfoVerifier( cause.getMessage() ) );
737 public void testToXMLResponseWithMultipleErrors() throws Exception {
739 List<RestconfError> errorList = Arrays.asList(
740 new RestconfError( ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1" ),
741 new RestconfError( ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2" ) );
742 stageMockEx( new RestconfDocumentedException( errorList ) );
744 Response resp = target("/operational/foo").request( MediaType.APPLICATION_XML ).get();
746 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_XML, Status.CONFLICT );
748 Document doc = parseXMLDocument( stream );
750 NodeList children = getXMLErrorList( doc, 2 );
752 verifyXMLErrorNode( children.item( 0 ), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
753 "mock error1", null, null );
755 verifyXMLErrorNode( children.item( 1 ), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED,
756 "mock error2", null, null );
760 public void testToResponseWithAcceptHeader() throws Exception {
762 stageMockEx( new RestconfDocumentedException( "mock error" ) );
764 Response resp = target("/operational/foo")
765 .request().header( "Accept", MediaType.APPLICATION_JSON ).get();
767 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_JSON,
768 Status.INTERNAL_SERVER_ERROR );
770 verifyJsonResponseBody( stream, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error",
775 public void testToResponseWithStatusOnly() throws Exception {
777 // The StructuredDataToJsonProvider should throw a RestconfDocumentedException with no data
779 when( mockRestConf.readOperationalData( any( String.class ) ) )
780 .thenReturn( new StructuredData( null, null, null ) );
782 Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
784 verifyResponse( resp, MediaType.TEXT_PLAIN, Status.NOT_FOUND );
787 InputStream verifyResponse( final Response resp, final String expMediaType, final Status expStatus ) {
788 assertEquals( "getMediaType", MediaType.valueOf( expMediaType ), resp.getMediaType() );
789 assertEquals( "getStatus", expStatus.getStatusCode(), resp.getStatus() );
791 Object entity = resp.getEntity();
792 assertEquals( "Response entity", true, entity instanceof InputStream );
793 InputStream stream = (InputStream)entity;
797 void verifyJsonResponseBody( final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
798 final String expErrorMessage, final String expErrorAppTag,
799 final ErrorInfoVerifier errorInfoVerifier ) throws Exception {
801 JsonArray arrayElement = parseJsonErrorArrayElement( stream );
803 assertEquals( "\"error\" Json array element length", 1, arrayElement.size() );
805 verifyJsonErrorNode( arrayElement.get( 0 ), expErrorType, expErrorTag, expErrorMessage,
806 expErrorAppTag, errorInfoVerifier );
809 private JsonArray parseJsonErrorArrayElement( final InputStream stream ) throws IOException {
810 ByteArrayOutputStream bos = new ByteArrayOutputStream();
811 ByteStreams.copy( stream, bos );
813 System.out.println("JSON: "+bos.toString());
815 JsonParser parser = new JsonParser();
816 JsonElement rootElement;
819 rootElement = parser.parse(
820 new InputStreamReader( new ByteArrayInputStream( bos.toByteArray() ) ) );
822 catch( Exception e ) {
823 throw new IllegalArgumentException( "Invalid JSON response:\n" + bos.toString(), e );
826 assertTrue( "Root element of Json is not an Object", rootElement.isJsonObject() );
828 Set<Entry<String, JsonElement>> errorsEntrySet = rootElement.getAsJsonObject().entrySet();
829 assertEquals( "Json Object element set count", 1, errorsEntrySet.size() );
831 Entry<String, JsonElement> errorsEntry = errorsEntrySet.iterator().next();
832 JsonElement errorsElement = errorsEntry.getValue();
833 assertEquals( "First Json element name", "errors", errorsEntry.getKey() );
834 assertTrue( "\"errors\" Json element is not an Object", errorsElement.isJsonObject() );
836 Set<Entry<String, JsonElement>> errorListEntrySet = errorsElement.getAsJsonObject().entrySet();
837 assertEquals( "Root \"errors\" element child count", 1, errorListEntrySet.size() );
839 JsonElement errorListElement = errorListEntrySet.iterator().next().getValue();
840 assertEquals( "\"errors\" child Json element name", "error",
841 errorListEntrySet.iterator().next().getKey() );
842 assertTrue( "\"error\" Json element is not an Array", errorListElement.isJsonArray() );
844 return errorListElement.getAsJsonArray();
847 void verifyJsonErrorNode( final JsonElement errorEntryElement, final ErrorType expErrorType, final ErrorTag expErrorTag,
848 final String expErrorMessage, final String expErrorAppTag,
849 final ErrorInfoVerifier errorInfoVerifier ) {
851 JsonElement errorInfoElement = null;
852 Map<String, String> actualErrorInfo = null;
853 Map<String, String> leafMap = Maps.newHashMap();
854 for( Entry<String, JsonElement> entry: errorEntryElement.getAsJsonObject().entrySet() ) {
855 String leafName = entry.getKey();
856 JsonElement leafElement = entry.getValue();
858 if( "error-info".equals( leafName ) ) {
859 assertNotNull( "Found unexpected \"error-info\" element", errorInfoVerifier );
860 errorInfoElement = leafElement;
863 assertTrue( "\"error\" leaf Json element " + leafName +
864 " is not a Primitive", leafElement.isJsonPrimitive() );
866 leafMap.put( leafName, leafElement.getAsString() );
870 assertEquals( "error-type", expErrorType.getErrorTypeTag(), leafMap.remove( "error-type" ) );
871 assertEquals( "error-tag", expErrorTag.getTagValue(), leafMap.remove( "error-tag" ) );
873 verifyOptionalJsonLeaf( leafMap.remove( "error-message" ), expErrorMessage, "error-message" );
874 verifyOptionalJsonLeaf( leafMap.remove( "error-app-tag" ), expErrorAppTag, "error-app-tag" );
876 if( !leafMap.isEmpty() ) {
877 fail( "Found unexpected Json leaf elements for \"error\" element: " + leafMap );
880 if( errorInfoVerifier != null ) {
881 assertNotNull( "Missing \"error-info\" element", errorInfoElement );
882 errorInfoVerifier.verifyJson( errorInfoElement );
886 void verifyOptionalJsonLeaf( final String actualValue, final String expValue, final String tagName ) {
887 if( expValue != null ) {
888 assertEquals( tagName, expValue, actualValue );
891 assertNull( "Found unexpected \"error\" leaf entry for: " + tagName, actualValue );
895 void verifyXMLResponseBody( final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
896 final String expErrorMessage, final String expErrorAppTag,
897 final ErrorInfoVerifier errorInfoVerifier )
900 Document doc = parseXMLDocument( stream );
902 NodeList children = getXMLErrorList( doc, 1 );
904 verifyXMLErrorNode( children.item( 0 ), expErrorType, expErrorTag, expErrorMessage,
905 expErrorAppTag, errorInfoVerifier );
908 private Document parseXMLDocument( final InputStream stream ) throws IOException {
909 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
910 factory.setNamespaceAware(true);
911 factory.setCoalescing(true);
912 factory.setIgnoringElementContentWhitespace(true);
913 factory.setIgnoringComments(true);
915 ByteArrayOutputStream bos = new ByteArrayOutputStream();
916 ByteStreams.copy( stream, bos );
918 System.out.println("XML: "+bos.toString());
922 doc = factory.newDocumentBuilder().parse( new ByteArrayInputStream( bos.toByteArray() ) );
924 catch( Exception e ) {
925 throw new IllegalArgumentException( "Invalid XML response:\n" + bos.toString(), e );
930 void verifyXMLErrorNode( final Node errorNode, final ErrorType expErrorType, final ErrorTag expErrorTag,
931 final String expErrorMessage, final String expErrorAppTag,
932 final ErrorInfoVerifier errorInfoVerifier ) throws Exception {
934 String errorType = (String)ERROR_TYPE.evaluate( errorNode, XPathConstants.STRING );
935 assertEquals( "error-type", expErrorType.getErrorTypeTag(), errorType );
937 String errorTag = (String)ERROR_TAG.evaluate( errorNode, XPathConstants.STRING );
938 assertEquals( "error-tag", expErrorTag.getTagValue(), errorTag );
940 verifyOptionalXMLLeaf( errorNode, ERROR_MESSAGE, expErrorMessage, "error-message" );
941 verifyOptionalXMLLeaf( errorNode, ERROR_APP_TAG, expErrorAppTag, "error-app-tag" );
943 Node errorInfoNode = (Node)ERROR_INFO.evaluate( errorNode, XPathConstants.NODE );
944 if( errorInfoVerifier != null ) {
945 assertNotNull( "Missing \"error-info\" node", errorInfoNode );
947 errorInfoVerifier.verifyXML( errorInfoNode );
950 assertNull( "Found unexpected \"error-info\" node", errorInfoNode );
954 void verifyOptionalXMLLeaf( final Node fromNode, final XPathExpression xpath, final String expValue,
955 final String tagName ) throws Exception {
956 if( expValue != null ) {
957 String actual = (String)xpath.evaluate( fromNode, XPathConstants.STRING );
958 assertEquals( tagName, expValue, actual );
961 assertNull( "Found unexpected \"error\" leaf entry for: " + tagName,
962 xpath.evaluate( fromNode, XPathConstants.NODE ) );
966 NodeList getXMLErrorList( final Node fromNode, final int count ) throws Exception {
967 NodeList errorList = (NodeList)ERROR_LIST.evaluate( fromNode, XPathConstants.NODESET );
968 assertNotNull( "Root errors node is empty", errorList );
969 assertEquals( "Root errors node child count", count, errorList.getLength() );