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.*;
12 import static org.mockito.Matchers.*;
13 import static org.mockito.Mockito.*;
15 import java.io.ByteArrayInputStream;
16 import java.io.ByteArrayOutputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.InputStreamReader;
20 import java.util.Arrays;
21 import java.util.Iterator;
22 import java.util.List;
25 import java.util.Map.Entry;
27 import javax.ws.rs.core.Application;
28 import javax.ws.rs.core.MediaType;
29 import javax.ws.rs.core.Response;
30 import javax.ws.rs.core.Response.Status;
31 import javax.xml.namespace.NamespaceContext;
32 import javax.xml.parsers.DocumentBuilderFactory;
33 import javax.xml.xpath.XPath;
34 import javax.xml.xpath.XPathConstants;
35 import javax.xml.xpath.XPathExpression;
36 import javax.xml.xpath.XPathFactory;
38 import org.glassfish.jersey.server.ResourceConfig;
39 import org.glassfish.jersey.test.JerseyTest;
40 import org.junit.Before;
41 import org.junit.BeforeClass;
42 import org.junit.Test;
43 import org.opendaylight.controller.sal.rest.api.Draft02;
44 import org.opendaylight.controller.sal.rest.api.RestconfService;
45 import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
46 import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
47 import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
48 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
49 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
50 import org.opendaylight.controller.sal.restconf.impl.RestconfError;
51 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
52 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
53 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
54 import org.w3c.dom.Document;
55 import org.w3c.dom.Element;
56 import org.w3c.dom.Node;
57 import org.w3c.dom.NodeList;
59 import com.google.common.collect.ImmutableMap;
60 import com.google.common.collect.Maps;
61 import com.google.common.io.ByteStreams;
62 import com.google.gson.JsonArray;
63 import com.google.gson.JsonElement;
64 import com.google.gson.JsonParser;
67 * Unit tests for RestconfDocumentedExceptionMapper.
69 * @author Thomas Pantelis
71 public class RestconfDocumentedExceptionMapperTest extends JerseyTest {
73 interface ErrorInfoVerifier {
74 void verifyXML( Node errorInfoNode );
75 void verifyJson( JsonElement errorInfoElement );
78 static class ComplexErrorInfoVerifier implements ErrorInfoVerifier {
80 Map<String, String> expErrorInfo;
82 public ComplexErrorInfoVerifier( Map<String, String> expErrorInfo ) {
83 this.expErrorInfo = expErrorInfo;
87 public void verifyXML( Node errorInfoNode ) {
89 Map<String, String> mutableExpMap = Maps.newHashMap( expErrorInfo );
90 NodeList childNodes = errorInfoNode.getChildNodes();
91 for( int i = 0; i < childNodes.getLength(); i++ ) {
92 Node child = childNodes.item( i );
93 if( child instanceof Element ) {
94 String expValue = mutableExpMap.remove( child.getNodeName() );
95 assertNotNull( "Found unexpected \"error-info\" child node: " +
96 child.getNodeName(), expValue );
97 assertEquals( "Text content for \"error-info\" child node " +
98 child.getNodeName(), expValue, child.getTextContent() );
102 if( !mutableExpMap.isEmpty() ) {
103 fail( "Missing \"error-info\" child nodes: " + mutableExpMap );
108 public void verifyJson( JsonElement errorInfoElement ) {
110 assertTrue( "\"error-info\" Json element is not an Object",
111 errorInfoElement.isJsonObject() );
113 Map<String, String> actualErrorInfo = Maps.newHashMap();
114 for( Entry<String, JsonElement> entry: errorInfoElement.getAsJsonObject().entrySet() ) {
115 String leafName = entry.getKey();
116 JsonElement leafElement = entry.getValue();
117 actualErrorInfo.put( leafName, leafElement.getAsString() );
120 Map<String, String> mutableExpMap = Maps.newHashMap( expErrorInfo );
121 for( Entry<String,String> actual: actualErrorInfo.entrySet() ) {
122 String expValue = mutableExpMap.remove( actual.getKey() );
123 assertNotNull( "Found unexpected \"error-info\" child node: " +
124 actual.getKey(), expValue );
125 assertEquals( "Text content for \"error-info\" child node " +
126 actual.getKey(), expValue, actual.getValue() );
129 if( !mutableExpMap.isEmpty() ) {
130 fail( "Missing \"error-info\" child nodes: " + mutableExpMap );
135 static class SimpleErrorInfoVerifier implements ErrorInfoVerifier {
137 String expTextContent;
139 public SimpleErrorInfoVerifier( String expErrorInfo ) {
140 this.expTextContent = expErrorInfo;
143 void verifyContent( String actualContent ) {
144 assertNotNull( "Actual \"error-info\" text content is null", actualContent );
145 assertTrue( "", actualContent.contains( expTextContent ) );
149 public void verifyXML( Node errorInfoNode ) {
150 verifyContent( errorInfoNode.getTextContent() );
154 public void verifyJson( JsonElement errorInfoElement ) {
155 verifyContent( errorInfoElement.getAsString() );
159 static RestconfService mockRestConf = mock( RestconfService.class );
161 static XPath XPATH = XPathFactory.newInstance().newXPath();
162 static XPathExpression ERROR_LIST;
163 static XPathExpression ERROR_TYPE;
164 static XPathExpression ERROR_TAG;
165 static XPathExpression ERROR_MESSAGE;
166 static XPathExpression ERROR_APP_TAG;
167 static XPathExpression ERROR_INFO;
170 public static void init() throws Exception {
171 ControllerContext.getInstance().setGlobalSchema( TestUtils.loadSchemaContext("/modules") );
173 NamespaceContext nsContext = new NamespaceContext() {
175 public Iterator getPrefixes( String namespaceURI ) {
180 public String getPrefix( String namespaceURI ) {
185 public String getNamespaceURI( String prefix ) {
186 return "ietf-restconf".equals( prefix ) ? Draft02.RestConfModule.NAMESPACE : null;
190 XPATH.setNamespaceContext( nsContext );
191 ERROR_LIST = XPATH.compile( "ietf-restconf:errors/ietf-restconf:error" );
192 ERROR_TYPE = XPATH.compile( "ietf-restconf:error-type" );
193 ERROR_TAG = XPATH.compile( "ietf-restconf:error-tag" );
194 ERROR_MESSAGE = XPATH.compile( "ietf-restconf:error-message" );
195 ERROR_APP_TAG = XPATH.compile( "ietf-restconf:error-app-tag" );
196 ERROR_INFO = XPATH.compile( "ietf-restconf:error-info" );
201 public void setUp() throws Exception {
202 reset( mockRestConf );
207 protected Application configure() {
208 ResourceConfig resourceConfig = new ResourceConfig();
209 resourceConfig = resourceConfig.registerInstances( mockRestConf, StructuredDataToXmlProvider.INSTANCE,
210 StructuredDataToJsonProvider.INSTANCE );
211 resourceConfig.registerClasses( RestconfDocumentedExceptionMapper.class );
212 return resourceConfig;
215 void stageMockEx( RestconfDocumentedException ex ) {
216 reset( mockRestConf );
217 when( mockRestConf.readOperationalData( any( String.class ) ) ).thenThrow( ex );
220 void testJsonResponse( RestconfDocumentedException ex, Status expStatus, ErrorType expErrorType,
221 ErrorTag expErrorTag, String expErrorMessage, String expErrorAppTag,
222 ErrorInfoVerifier errorInfoVerifier ) throws Exception {
226 Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
228 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_JSON, expStatus );
230 verifyJsonResponseBody( stream, expErrorType, expErrorTag, expErrorMessage,
231 expErrorAppTag, errorInfoVerifier );
235 public void testToJsonResponseWithMessageOnly() throws Exception {
237 testJsonResponse( new RestconfDocumentedException( "mock error" ), Status.INTERNAL_SERVER_ERROR,
238 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null );
240 // To test verification code
244 // " error-tag : \"operation-failed\"" +
245 // " ,error-type : \"application\"" +
246 // " ,error-message : \"An error occurred\"" +
247 // " ,error-info : {" +
248 // " session-id: \"123\"" +
249 // " ,address: \"1.2.3.4\"" +
255 // verifyJsonResponseBody( new java.io.StringBufferInputStream(json ), ErrorType.APPLICATION,
256 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
257 // com.google.common.collect.ImmutableMap.of( "session-id", "123", "address", "1.2.3.4" ) );
261 public void testToJsonResponseWithInUseErrorTag() throws Exception {
263 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
265 Status.CONFLICT, ErrorType.PROTOCOL,
266 ErrorTag.IN_USE, "mock error", null, null );
270 public void testToJsonResponseWithInvalidValueErrorTag() throws Exception {
272 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.RPC,
273 ErrorTag.INVALID_VALUE ),
274 Status.BAD_REQUEST, ErrorType.RPC,
275 ErrorTag.INVALID_VALUE, "mock error", null, null );
280 public void testToJsonResponseWithTooBigErrorTag() throws Exception {
282 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.TRANSPORT,
284 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT,
285 ErrorTag.TOO_BIG, "mock error", null, null );
290 public void testToJsonResponseWithMissingAttributeErrorTag() throws Exception {
292 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
293 ErrorTag.MISSING_ATTRIBUTE ),
294 Status.BAD_REQUEST, ErrorType.PROTOCOL,
295 ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null );
299 public void testToJsonResponseWithBadAttributeErrorTag() throws Exception {
301 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
302 ErrorTag.BAD_ATTRIBUTE ),
303 Status.BAD_REQUEST, ErrorType.PROTOCOL,
304 ErrorTag.BAD_ATTRIBUTE, "mock error", null, null );
307 public void testToJsonResponseWithUnknownAttributeErrorTag() throws Exception {
309 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
310 ErrorTag.UNKNOWN_ATTRIBUTE ),
311 Status.BAD_REQUEST, ErrorType.PROTOCOL,
312 ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null );
316 public void testToJsonResponseWithBadElementErrorTag() throws Exception {
318 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
319 ErrorTag.BAD_ELEMENT ),
321 ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null );
325 public void testToJsonResponseWithUnknownElementErrorTag() throws Exception {
327 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
328 ErrorTag.UNKNOWN_ELEMENT ),
329 Status.BAD_REQUEST, ErrorType.PROTOCOL,
330 ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null );
334 public void testToJsonResponseWithUnknownNamespaceErrorTag() throws Exception {
336 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
337 ErrorTag.UNKNOWN_NAMESPACE ),
338 Status.BAD_REQUEST, ErrorType.PROTOCOL,
339 ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null );
343 public void testToJsonResponseWithMalformedMessageErrorTag() throws Exception {
345 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
346 ErrorTag.MALFORMED_MESSAGE ),
347 Status.BAD_REQUEST, ErrorType.PROTOCOL,
348 ErrorTag.MALFORMED_MESSAGE, "mock error", null, null );
352 public void testToJsonResponseWithAccessDeniedErrorTag() throws Exception {
354 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
355 ErrorTag.ACCESS_DENIED ),
356 Status.FORBIDDEN, ErrorType.PROTOCOL,
357 ErrorTag.ACCESS_DENIED, "mock error", null, null );
361 public void testToJsonResponseWithLockDeniedErrorTag() throws Exception {
363 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
364 ErrorTag.LOCK_DENIED ),
365 Status.CONFLICT, ErrorType.PROTOCOL,
366 ErrorTag.LOCK_DENIED, "mock error", null, null );
370 public void testToJsonResponseWithResourceDeniedErrorTag() throws Exception {
372 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
373 ErrorTag.RESOURCE_DENIED ),
374 Status.CONFLICT, ErrorType.PROTOCOL,
375 ErrorTag.RESOURCE_DENIED, "mock error", null, null );
379 public void testToJsonResponseWithRollbackFailedErrorTag() throws Exception {
381 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
382 ErrorTag.ROLLBACK_FAILED ),
383 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
384 ErrorTag.ROLLBACK_FAILED, "mock error", null, null );
388 public void testToJsonResponseWithDataExistsErrorTag() throws Exception {
390 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
391 ErrorTag.DATA_EXISTS ),
392 Status.CONFLICT, ErrorType.PROTOCOL,
393 ErrorTag.DATA_EXISTS, "mock error", null, null );
397 public void testToJsonResponseWithDataMissingErrorTag() throws Exception {
399 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
400 ErrorTag.DATA_MISSING ),
401 Status.CONFLICT, ErrorType.PROTOCOL,
402 ErrorTag.DATA_MISSING, "mock error", null, null );
406 public void testToJsonResponseWithOperationNotSupportedErrorTag() throws Exception {
408 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
409 ErrorTag.OPERATION_NOT_SUPPORTED ),
410 Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
411 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null );
415 public void testToJsonResponseWithOperationFailedErrorTag() throws Exception {
417 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
418 ErrorTag.OPERATION_FAILED ),
419 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
420 ErrorTag.OPERATION_FAILED, "mock error", null, null );
424 public void testToJsonResponseWithPartialOperationErrorTag() throws Exception {
426 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
427 ErrorTag.PARTIAL_OPERATION ),
428 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
429 ErrorTag.PARTIAL_OPERATION, "mock error", null, null );
433 public void testToJsonResponseWithErrorAppTag() throws Exception {
435 testJsonResponse( new RestconfDocumentedException( new RestconfError(
436 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
437 "mock error", "mock-app-tag" ) ),
438 Status.BAD_REQUEST, ErrorType.APPLICATION,
439 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null );
443 public void testToJsonResponseWithMultipleErrors() throws Exception {
445 List<RestconfError> errorList = Arrays.asList(
446 new RestconfError( ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1" ),
447 new RestconfError( ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2" ) );
448 stageMockEx( new RestconfDocumentedException( errorList ) );
450 Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
452 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_JSON, Status.CONFLICT );
454 JsonArray arrayElement = parseJsonErrorArrayElement( stream );
456 assertEquals( "\"error\" Json array element length", 2, arrayElement.size() );
458 verifyJsonErrorNode( arrayElement.get( 0 ), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
459 "mock error1", null, null );
461 verifyJsonErrorNode( arrayElement.get( 1 ), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED,
462 "mock error2", null, null );
466 public void testToJsonResponseWithErrorInfo() throws Exception {
468 String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
469 testJsonResponse( new RestconfDocumentedException( new RestconfError(
470 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
471 "mock error", "mock-app-tag", errorInfo ) ),
472 Status.BAD_REQUEST, ErrorType.APPLICATION,
473 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
474 new ComplexErrorInfoVerifier( ImmutableMap.of(
475 "session-id", "123", "address", "1.2.3.4" ) ) );
479 public void testToJsonResponseWithExceptionCause() throws Exception {
481 Exception cause = new Exception( "mock exception cause" );
482 testJsonResponse( new RestconfDocumentedException( "mock error", cause ),
483 Status.INTERNAL_SERVER_ERROR, ErrorType.APPLICATION,
484 ErrorTag.OPERATION_FAILED, "mock error", null,
485 new SimpleErrorInfoVerifier( cause.getMessage() ) );
488 void testXMLResponse( RestconfDocumentedException ex, Status expStatus, ErrorType expErrorType,
489 ErrorTag expErrorTag, String expErrorMessage,
490 String expErrorAppTag, ErrorInfoVerifier errorInfoVerifier ) throws Exception
494 Response resp = target("/operational/foo").request( MediaType.APPLICATION_XML ).get();
496 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_XML, expStatus );
498 verifyXMLResponseBody( stream, expErrorType, expErrorTag, expErrorMessage,
499 expErrorAppTag, errorInfoVerifier );
503 public void testToXMLResponseWithMessageOnly() throws Exception {
505 testXMLResponse( new RestconfDocumentedException( "mock error" ), Status.INTERNAL_SERVER_ERROR,
506 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null );
508 // To test verification code
510 // "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">"+
512 // " <error-type>application</error-type>"+
513 // " <error-tag>operation-failed</error-tag>"+
514 // " <error-message>An error occurred</error-message>"+
516 // " <session-id>123</session-id>" +
517 // " <address>1.2.3.4</address>" +
518 // " </error-info>" +
522 // verifyXMLResponseBody( new java.io.StringBufferInputStream(xml), ErrorType.APPLICATION,
523 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
524 // com.google.common.collect.ImmutableMap.of( "session-id", "123", "address", "1.2.3.4" ) );
528 public void testToXMLResponseWithInUseErrorTag() throws Exception {
530 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
532 Status.CONFLICT, ErrorType.PROTOCOL,
533 ErrorTag.IN_USE, "mock error", null, null );
537 public void testToXMLResponseWithInvalidValueErrorTag() throws Exception {
539 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.RPC,
540 ErrorTag.INVALID_VALUE ),
541 Status.BAD_REQUEST, ErrorType.RPC,
542 ErrorTag.INVALID_VALUE, "mock error", null, null );
546 public void testToXMLResponseWithTooBigErrorTag() throws Exception {
548 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.TRANSPORT,
550 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT,
551 ErrorTag.TOO_BIG, "mock error", null, null );
555 public void testToXMLResponseWithMissingAttributeErrorTag() throws Exception {
557 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
558 ErrorTag.MISSING_ATTRIBUTE ),
559 Status.BAD_REQUEST, ErrorType.PROTOCOL,
560 ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null );
564 public void testToXMLResponseWithBadAttributeErrorTag() throws Exception {
566 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
567 ErrorTag.BAD_ATTRIBUTE ),
568 Status.BAD_REQUEST, ErrorType.PROTOCOL,
569 ErrorTag.BAD_ATTRIBUTE, "mock error", null, null );
572 public void testToXMLResponseWithUnknownAttributeErrorTag() throws Exception {
574 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
575 ErrorTag.UNKNOWN_ATTRIBUTE ),
576 Status.BAD_REQUEST, ErrorType.PROTOCOL,
577 ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null );
581 public void testToXMLResponseWithBadElementErrorTag() throws Exception {
583 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
584 ErrorTag.BAD_ELEMENT ),
585 Status.BAD_REQUEST, ErrorType.PROTOCOL,
586 ErrorTag.BAD_ELEMENT, "mock error", null, null );
590 public void testToXMLResponseWithUnknownElementErrorTag() throws Exception {
592 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
593 ErrorTag.UNKNOWN_ELEMENT ),
594 Status.BAD_REQUEST, ErrorType.PROTOCOL,
595 ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null );
599 public void testToXMLResponseWithUnknownNamespaceErrorTag() throws Exception {
601 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
602 ErrorTag.UNKNOWN_NAMESPACE ),
603 Status.BAD_REQUEST, ErrorType.PROTOCOL,
604 ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null );
608 public void testToXMLResponseWithMalformedMessageErrorTag() throws Exception {
610 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
611 ErrorTag.MALFORMED_MESSAGE ),
612 Status.BAD_REQUEST, ErrorType.PROTOCOL,
613 ErrorTag.MALFORMED_MESSAGE, "mock error", null, null );
617 public void testToXMLResponseWithAccessDeniedErrorTag() throws Exception {
619 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
620 ErrorTag.ACCESS_DENIED ),
621 Status.FORBIDDEN, ErrorType.PROTOCOL,
622 ErrorTag.ACCESS_DENIED, "mock error", null, null );
626 public void testToXMLResponseWithLockDeniedErrorTag() throws Exception {
628 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
629 ErrorTag.LOCK_DENIED ),
630 Status.CONFLICT, ErrorType.PROTOCOL,
631 ErrorTag.LOCK_DENIED, "mock error", null, null );
635 public void testToXMLResponseWithResourceDeniedErrorTag() throws Exception {
637 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
638 ErrorTag.RESOURCE_DENIED ),
639 Status.CONFLICT, ErrorType.PROTOCOL,
640 ErrorTag.RESOURCE_DENIED, "mock error", null, null );
644 public void testToXMLResponseWithRollbackFailedErrorTag() throws Exception {
646 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
647 ErrorTag.ROLLBACK_FAILED ),
648 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
649 ErrorTag.ROLLBACK_FAILED, "mock error", null, null );
653 public void testToXMLResponseWithDataExistsErrorTag() throws Exception {
655 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
656 ErrorTag.DATA_EXISTS ),
657 Status.CONFLICT, ErrorType.PROTOCOL,
658 ErrorTag.DATA_EXISTS, "mock error", null, null );
662 public void testToXMLResponseWithDataMissingErrorTag() throws Exception {
664 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
665 ErrorTag.DATA_MISSING ),
666 Status.CONFLICT, ErrorType.PROTOCOL,
667 ErrorTag.DATA_MISSING, "mock error", null, null );
671 public void testToXMLResponseWithOperationNotSupportedErrorTag() throws Exception {
673 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
674 ErrorTag.OPERATION_NOT_SUPPORTED ),
675 Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
676 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null );
680 public void testToXMLResponseWithOperationFailedErrorTag() throws Exception {
682 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
683 ErrorTag.OPERATION_FAILED ),
684 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
685 ErrorTag.OPERATION_FAILED, "mock error", null, null );
689 public void testToXMLResponseWithPartialOperationErrorTag() throws Exception {
691 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
692 ErrorTag.PARTIAL_OPERATION ),
693 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
694 ErrorTag.PARTIAL_OPERATION, "mock error", null, null );
698 public void testToXMLResponseWithErrorAppTag() throws Exception {
700 testXMLResponse( new RestconfDocumentedException( new RestconfError(
701 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
702 "mock error", "mock-app-tag" ) ),
703 Status.BAD_REQUEST, ErrorType.APPLICATION,
704 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null );
708 public void testToXMLResponseWithErrorInfo() throws Exception {
710 String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
711 testXMLResponse( new RestconfDocumentedException( new RestconfError(
712 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
713 "mock error", "mock-app-tag", errorInfo ) ),
714 Status.BAD_REQUEST, ErrorType.APPLICATION,
715 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
716 new ComplexErrorInfoVerifier( ImmutableMap.of(
717 "session-id", "123", "address", "1.2.3.4" ) ) );
721 public void testToXMLResponseWithExceptionCause() throws Exception {
723 Exception cause = new Exception( "mock exception cause" );
724 testXMLResponse( new RestconfDocumentedException( "mock error", cause ),
725 Status.INTERNAL_SERVER_ERROR, ErrorType.APPLICATION,
726 ErrorTag.OPERATION_FAILED, "mock error", null,
727 new SimpleErrorInfoVerifier( cause.getMessage() ) );
731 public void testToXMLResponseWithMultipleErrors() throws Exception {
733 List<RestconfError> errorList = Arrays.asList(
734 new RestconfError( ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1" ),
735 new RestconfError( ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2" ) );
736 stageMockEx( new RestconfDocumentedException( errorList ) );
738 Response resp = target("/operational/foo").request( MediaType.APPLICATION_XML ).get();
740 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_XML, Status.CONFLICT );
742 Document doc = parseXMLDocument( stream );
744 NodeList children = getXMLErrorList( doc, 2 );
746 verifyXMLErrorNode( children.item( 0 ), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
747 "mock error1", null, null );
749 verifyXMLErrorNode( children.item( 1 ), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED,
750 "mock error2", null, null );
754 public void testToResponseWithAcceptHeader() throws Exception {
756 stageMockEx( new RestconfDocumentedException( "mock error" ) );
758 Response resp = target("/operational/foo")
759 .request().header( "Accept", MediaType.APPLICATION_JSON ).get();
761 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_JSON,
762 Status.INTERNAL_SERVER_ERROR );
764 verifyJsonResponseBody( stream, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error",
769 public void testToResponseWithStatusOnly() throws Exception {
771 // The StructuredDataToJsonProvider should throw a RestconfDocumentedException with no data
773 when( mockRestConf.readOperationalData( any( String.class ) ) )
774 .thenReturn( new StructuredData( null, null, null ) );
776 Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
778 verifyResponse( resp, MediaType.TEXT_PLAIN, Status.NOT_FOUND );
781 InputStream verifyResponse( Response resp, String expMediaType, Status expStatus ) {
782 assertEquals( "getMediaType", MediaType.valueOf( expMediaType ), resp.getMediaType() );
783 assertEquals( "getStatus", expStatus.getStatusCode(), resp.getStatus() );
785 Object entity = resp.getEntity();
786 assertEquals( "Response entity", true, entity instanceof InputStream );
787 InputStream stream = (InputStream)entity;
791 void verifyJsonResponseBody( InputStream stream, ErrorType expErrorType, ErrorTag expErrorTag,
792 String expErrorMessage, String expErrorAppTag,
793 ErrorInfoVerifier errorInfoVerifier ) throws Exception {
795 JsonArray arrayElement = parseJsonErrorArrayElement( stream );
797 assertEquals( "\"error\" Json array element length", 1, arrayElement.size() );
799 verifyJsonErrorNode( arrayElement.get( 0 ), expErrorType, expErrorTag, expErrorMessage,
800 expErrorAppTag, errorInfoVerifier );
803 private JsonArray parseJsonErrorArrayElement( InputStream stream ) throws IOException {
804 ByteArrayOutputStream bos = new ByteArrayOutputStream();
805 ByteStreams.copy( stream, bos );
807 System.out.println("JSON: "+bos.toString());
809 JsonParser parser = new JsonParser();
810 JsonElement rootElement;
813 rootElement = parser.parse(
814 new InputStreamReader( new ByteArrayInputStream( bos.toByteArray() ) ) );
816 catch( Exception e ) {
817 throw new IllegalArgumentException( "Invalid JSON response:\n" + bos.toString(), e );
820 assertTrue( "Root element of Json is not an Object", rootElement.isJsonObject() );
822 Set<Entry<String, JsonElement>> errorsEntrySet = rootElement.getAsJsonObject().entrySet();
823 assertEquals( "Json Object element set count", 1, errorsEntrySet.size() );
825 Entry<String, JsonElement> errorsEntry = errorsEntrySet.iterator().next();
826 JsonElement errorsElement = errorsEntry.getValue();
827 assertEquals( "First Json element name", "errors", errorsEntry.getKey() );
828 assertTrue( "\"errors\" Json element is not an Object", errorsElement.isJsonObject() );
830 Set<Entry<String, JsonElement>> errorListEntrySet = errorsElement.getAsJsonObject().entrySet();
831 assertEquals( "Root \"errors\" element child count", 1, errorListEntrySet.size() );
833 JsonElement errorListElement = errorListEntrySet.iterator().next().getValue();
834 assertEquals( "\"errors\" child Json element name", "error",
835 errorListEntrySet.iterator().next().getKey() );
836 assertTrue( "\"error\" Json element is not an Array", errorListElement.isJsonArray() );
838 return errorListElement.getAsJsonArray();
841 void verifyJsonErrorNode( JsonElement errorEntryElement, ErrorType expErrorType, ErrorTag expErrorTag,
842 String expErrorMessage, String expErrorAppTag,
843 ErrorInfoVerifier errorInfoVerifier ) {
845 JsonElement errorInfoElement = null;
846 Map<String, String> actualErrorInfo = null;
847 Map<String, String> leafMap = Maps.newHashMap();
848 for( Entry<String, JsonElement> entry: errorEntryElement.getAsJsonObject().entrySet() ) {
849 String leafName = entry.getKey();
850 JsonElement leafElement = entry.getValue();
852 if( "error-info".equals( leafName ) ) {
853 assertNotNull( "Found unexpected \"error-info\" element", errorInfoVerifier );
854 errorInfoElement = leafElement;
857 assertTrue( "\"error\" leaf Json element " + leafName +
858 " is not a Primitive", leafElement.isJsonPrimitive() );
860 leafMap.put( leafName, leafElement.getAsString() );
864 assertEquals( "error-type", expErrorType.getErrorTypeTag(), leafMap.remove( "error-type" ) );
865 assertEquals( "error-tag", expErrorTag.getTagValue(), leafMap.remove( "error-tag" ) );
867 verifyOptionalJsonLeaf( leafMap.remove( "error-message" ), expErrorMessage, "error-message" );
868 verifyOptionalJsonLeaf( leafMap.remove( "error-app-tag" ), expErrorAppTag, "error-app-tag" );
870 if( !leafMap.isEmpty() ) {
871 fail( "Found unexpected Json leaf elements for \"error\" element: " + leafMap );
874 if( errorInfoVerifier != null ) {
875 assertNotNull( "Missing \"error-info\" element", errorInfoElement );
876 errorInfoVerifier.verifyJson( errorInfoElement );
880 void verifyOptionalJsonLeaf( String actualValue, String expValue, String tagName ) {
881 if( expValue != null ) {
882 assertEquals( tagName, expValue, actualValue );
885 assertNull( "Found unexpected \"error\" leaf entry for: " + tagName, actualValue );
889 void verifyXMLResponseBody( InputStream stream, ErrorType expErrorType, ErrorTag expErrorTag,
890 String expErrorMessage, String expErrorAppTag,
891 ErrorInfoVerifier errorInfoVerifier )
894 Document doc = parseXMLDocument( stream );
896 NodeList children = getXMLErrorList( doc, 1 );
898 verifyXMLErrorNode( children.item( 0 ), expErrorType, expErrorTag, expErrorMessage,
899 expErrorAppTag, errorInfoVerifier );
902 private Document parseXMLDocument( InputStream stream ) throws IOException {
903 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
904 factory.setNamespaceAware(true);
905 factory.setCoalescing(true);
906 factory.setIgnoringElementContentWhitespace(true);
907 factory.setIgnoringComments(true);
909 ByteArrayOutputStream bos = new ByteArrayOutputStream();
910 ByteStreams.copy( stream, bos );
912 System.out.println("XML: "+bos.toString());
916 doc = factory.newDocumentBuilder().parse( new ByteArrayInputStream( bos.toByteArray() ) );
918 catch( Exception e ) {
919 throw new IllegalArgumentException( "Invalid XML response:\n" + bos.toString(), e );
924 void verifyXMLErrorNode( Node errorNode, ErrorType expErrorType, ErrorTag expErrorTag,
925 String expErrorMessage, String expErrorAppTag,
926 ErrorInfoVerifier errorInfoVerifier ) throws Exception {
928 String errorType = (String)ERROR_TYPE.evaluate( errorNode, XPathConstants.STRING );
929 assertEquals( "error-type", expErrorType.getErrorTypeTag(), errorType );
931 String errorTag = (String)ERROR_TAG.evaluate( errorNode, XPathConstants.STRING );
932 assertEquals( "error-tag", expErrorTag.getTagValue(), errorTag );
934 verifyOptionalXMLLeaf( errorNode, ERROR_MESSAGE, expErrorMessage, "error-message" );
935 verifyOptionalXMLLeaf( errorNode, ERROR_APP_TAG, expErrorAppTag, "error-app-tag" );
937 Node errorInfoNode = (Node)ERROR_INFO.evaluate( errorNode, XPathConstants.NODE );
938 if( errorInfoVerifier != null ) {
939 assertNotNull( "Missing \"error-info\" node", errorInfoNode );
941 errorInfoVerifier.verifyXML( errorInfoNode );
944 assertNull( "Found unexpected \"error-info\" node", errorInfoNode );
948 void verifyOptionalXMLLeaf( Node fromNode, XPathExpression xpath, String expValue,
949 String tagName ) throws Exception {
950 if( expValue != null ) {
951 String actual = (String)xpath.evaluate( fromNode, XPathConstants.STRING );
952 assertEquals( tagName, expValue, actual );
955 assertNull( "Found unexpected \"error\" leaf entry for: " + tagName,
956 xpath.evaluate( fromNode, XPathConstants.NODE ) );
960 NodeList getXMLErrorList( Node fromNode, int count ) throws Exception {
961 NodeList errorList = (NodeList)ERROR_LIST.evaluate( fromNode, XPathConstants.NODESET );
962 assertNotNull( "Root errors node is empty", errorList );
963 assertEquals( "Root errors node child count", count, errorList.getLength() );