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.ws.rs.core.UriInfo;
38 import javax.xml.namespace.NamespaceContext;
39 import javax.xml.parsers.DocumentBuilderFactory;
40 import javax.xml.xpath.XPath;
41 import javax.xml.xpath.XPathConstants;
42 import javax.xml.xpath.XPathExpression;
43 import javax.xml.xpath.XPathFactory;
45 import org.glassfish.jersey.server.ResourceConfig;
46 import org.glassfish.jersey.test.JerseyTest;
47 import org.junit.Before;
48 import org.junit.BeforeClass;
49 import org.junit.Test;
50 import org.opendaylight.controller.sal.rest.api.Draft02;
51 import org.opendaylight.controller.sal.rest.api.RestconfService;
52 import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
53 import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
54 import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
55 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
56 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
57 import org.opendaylight.controller.sal.restconf.impl.RestconfError;
58 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
59 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
60 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
61 import org.w3c.dom.Document;
62 import org.w3c.dom.Element;
63 import org.w3c.dom.Node;
64 import org.w3c.dom.NodeList;
66 import com.google.common.collect.ImmutableMap;
67 import com.google.common.collect.Maps;
68 import com.google.common.io.ByteStreams;
69 import com.google.gson.JsonArray;
70 import com.google.gson.JsonElement;
71 import com.google.gson.JsonParser;
74 * Unit tests for RestconfDocumentedExceptionMapper.
76 * @author Thomas Pantelis
78 public class RestconfDocumentedExceptionMapperTest extends JerseyTest {
80 interface ErrorInfoVerifier {
81 void verifyXML( Node errorInfoNode );
82 void verifyJson( JsonElement errorInfoElement );
85 static class ComplexErrorInfoVerifier implements ErrorInfoVerifier {
87 Map<String, String> expErrorInfo;
89 public ComplexErrorInfoVerifier( final Map<String, String> expErrorInfo ) {
90 this.expErrorInfo = expErrorInfo;
94 public void verifyXML( final Node errorInfoNode ) {
96 Map<String, String> mutableExpMap = Maps.newHashMap( expErrorInfo );
97 NodeList childNodes = errorInfoNode.getChildNodes();
98 for( int i = 0; i < childNodes.getLength(); i++ ) {
99 Node child = childNodes.item( i );
100 if( child instanceof Element ) {
101 String expValue = mutableExpMap.remove( child.getNodeName() );
102 assertNotNull( "Found unexpected \"error-info\" child node: " +
103 child.getNodeName(), expValue );
104 assertEquals( "Text content for \"error-info\" child node " +
105 child.getNodeName(), expValue, child.getTextContent() );
109 if( !mutableExpMap.isEmpty() ) {
110 fail( "Missing \"error-info\" child nodes: " + mutableExpMap );
115 public void verifyJson( final JsonElement errorInfoElement ) {
117 assertTrue( "\"error-info\" Json element is not an Object",
118 errorInfoElement.isJsonObject() );
120 Map<String, String> actualErrorInfo = Maps.newHashMap();
121 for( Entry<String, JsonElement> entry: errorInfoElement.getAsJsonObject().entrySet() ) {
122 String leafName = entry.getKey();
123 JsonElement leafElement = entry.getValue();
124 actualErrorInfo.put( leafName, leafElement.getAsString() );
127 Map<String, String> mutableExpMap = Maps.newHashMap( expErrorInfo );
128 for( Entry<String,String> actual: actualErrorInfo.entrySet() ) {
129 String expValue = mutableExpMap.remove( actual.getKey() );
130 assertNotNull( "Found unexpected \"error-info\" child node: " +
131 actual.getKey(), expValue );
132 assertEquals( "Text content for \"error-info\" child node " +
133 actual.getKey(), expValue, actual.getValue() );
136 if( !mutableExpMap.isEmpty() ) {
137 fail( "Missing \"error-info\" child nodes: " + mutableExpMap );
142 static class SimpleErrorInfoVerifier implements ErrorInfoVerifier {
144 String expTextContent;
146 public SimpleErrorInfoVerifier( final String expErrorInfo ) {
147 this.expTextContent = expErrorInfo;
150 void verifyContent( final String actualContent ) {
151 assertNotNull( "Actual \"error-info\" text content is null", actualContent );
152 assertTrue( "", actualContent.contains( expTextContent ) );
156 public void verifyXML( final Node errorInfoNode ) {
157 verifyContent( errorInfoNode.getTextContent() );
161 public void verifyJson( final JsonElement errorInfoElement ) {
162 verifyContent( errorInfoElement.getAsString() );
166 static RestconfService mockRestConf = mock( RestconfService.class );
168 static XPath XPATH = XPathFactory.newInstance().newXPath();
169 static XPathExpression ERROR_LIST;
170 static XPathExpression ERROR_TYPE;
171 static XPathExpression ERROR_TAG;
172 static XPathExpression ERROR_MESSAGE;
173 static XPathExpression ERROR_APP_TAG;
174 static XPathExpression ERROR_INFO;
177 public static void init() throws Exception {
178 ControllerContext.getInstance().setGlobalSchema( TestUtils.loadSchemaContext("/modules") );
180 NamespaceContext nsContext = new NamespaceContext() {
182 public Iterator<?> getPrefixes( final String namespaceURI ) {
187 public String getPrefix( final String namespaceURI ) {
192 public String getNamespaceURI( final String prefix ) {
193 return "ietf-restconf".equals( prefix ) ? Draft02.RestConfModule.NAMESPACE : null;
197 XPATH.setNamespaceContext( nsContext );
198 ERROR_LIST = XPATH.compile( "ietf-restconf:errors/ietf-restconf:error" );
199 ERROR_TYPE = XPATH.compile( "ietf-restconf:error-type" );
200 ERROR_TAG = XPATH.compile( "ietf-restconf:error-tag" );
201 ERROR_MESSAGE = XPATH.compile( "ietf-restconf:error-message" );
202 ERROR_APP_TAG = XPATH.compile( "ietf-restconf:error-app-tag" );
203 ERROR_INFO = XPATH.compile( "ietf-restconf:error-info" );
208 public void setUp() throws Exception {
209 reset( mockRestConf );
214 protected Application configure() {
215 ResourceConfig resourceConfig = new ResourceConfig();
216 resourceConfig = resourceConfig.registerInstances( mockRestConf, StructuredDataToXmlProvider.INSTANCE,
217 StructuredDataToJsonProvider.INSTANCE );
218 resourceConfig.registerClasses( RestconfDocumentedExceptionMapper.class );
219 return resourceConfig;
222 void stageMockEx( final RestconfDocumentedException ex ) {
223 reset( mockRestConf );
224 when( mockRestConf.readOperationalData( any( String.class ), any( UriInfo.class ) ) ).thenThrow( ex );
227 void testJsonResponse( final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
228 final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag,
229 final ErrorInfoVerifier errorInfoVerifier ) throws Exception {
233 Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
235 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_JSON, expStatus );
237 verifyJsonResponseBody( stream, expErrorType, expErrorTag, expErrorMessage,
238 expErrorAppTag, errorInfoVerifier );
242 public void testToJsonResponseWithMessageOnly() throws Exception {
244 testJsonResponse( new RestconfDocumentedException( "mock error" ), Status.INTERNAL_SERVER_ERROR,
245 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null );
247 // To test verification code
251 // " error-tag : \"operation-failed\"" +
252 // " ,error-type : \"application\"" +
253 // " ,error-message : \"An error occurred\"" +
254 // " ,error-info : {" +
255 // " session-id: \"123\"" +
256 // " ,address: \"1.2.3.4\"" +
262 // verifyJsonResponseBody( new java.io.StringBufferInputStream(json ), ErrorType.APPLICATION,
263 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
264 // com.google.common.collect.ImmutableMap.of( "session-id", "123", "address", "1.2.3.4" ) );
268 public void testToJsonResponseWithInUseErrorTag() throws Exception {
270 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
272 Status.CONFLICT, ErrorType.PROTOCOL,
273 ErrorTag.IN_USE, "mock error", null, null );
277 public void testToJsonResponseWithInvalidValueErrorTag() throws Exception {
279 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.RPC,
280 ErrorTag.INVALID_VALUE ),
281 Status.BAD_REQUEST, ErrorType.RPC,
282 ErrorTag.INVALID_VALUE, "mock error", null, null );
287 public void testToJsonResponseWithTooBigErrorTag() throws Exception {
289 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.TRANSPORT,
291 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT,
292 ErrorTag.TOO_BIG, "mock error", null, null );
297 public void testToJsonResponseWithMissingAttributeErrorTag() throws Exception {
299 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
300 ErrorTag.MISSING_ATTRIBUTE ),
301 Status.BAD_REQUEST, ErrorType.PROTOCOL,
302 ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null );
306 public void testToJsonResponseWithBadAttributeErrorTag() throws Exception {
308 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
309 ErrorTag.BAD_ATTRIBUTE ),
310 Status.BAD_REQUEST, ErrorType.PROTOCOL,
311 ErrorTag.BAD_ATTRIBUTE, "mock error", null, null );
314 public void testToJsonResponseWithUnknownAttributeErrorTag() throws Exception {
316 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
317 ErrorTag.UNKNOWN_ATTRIBUTE ),
318 Status.BAD_REQUEST, ErrorType.PROTOCOL,
319 ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null );
323 public void testToJsonResponseWithBadElementErrorTag() throws Exception {
325 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
326 ErrorTag.BAD_ELEMENT ),
328 ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null );
332 public void testToJsonResponseWithUnknownElementErrorTag() throws Exception {
334 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
335 ErrorTag.UNKNOWN_ELEMENT ),
336 Status.BAD_REQUEST, ErrorType.PROTOCOL,
337 ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null );
341 public void testToJsonResponseWithUnknownNamespaceErrorTag() throws Exception {
343 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
344 ErrorTag.UNKNOWN_NAMESPACE ),
345 Status.BAD_REQUEST, ErrorType.PROTOCOL,
346 ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null );
350 public void testToJsonResponseWithMalformedMessageErrorTag() throws Exception {
352 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
353 ErrorTag.MALFORMED_MESSAGE ),
354 Status.BAD_REQUEST, ErrorType.PROTOCOL,
355 ErrorTag.MALFORMED_MESSAGE, "mock error", null, null );
359 public void testToJsonResponseWithAccessDeniedErrorTag() throws Exception {
361 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
362 ErrorTag.ACCESS_DENIED ),
363 Status.FORBIDDEN, ErrorType.PROTOCOL,
364 ErrorTag.ACCESS_DENIED, "mock error", null, null );
368 public void testToJsonResponseWithLockDeniedErrorTag() throws Exception {
370 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
371 ErrorTag.LOCK_DENIED ),
372 Status.CONFLICT, ErrorType.PROTOCOL,
373 ErrorTag.LOCK_DENIED, "mock error", null, null );
377 public void testToJsonResponseWithResourceDeniedErrorTag() throws Exception {
379 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
380 ErrorTag.RESOURCE_DENIED ),
381 Status.CONFLICT, ErrorType.PROTOCOL,
382 ErrorTag.RESOURCE_DENIED, "mock error", null, null );
386 public void testToJsonResponseWithRollbackFailedErrorTag() throws Exception {
388 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
389 ErrorTag.ROLLBACK_FAILED ),
390 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
391 ErrorTag.ROLLBACK_FAILED, "mock error", null, null );
395 public void testToJsonResponseWithDataExistsErrorTag() throws Exception {
397 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
398 ErrorTag.DATA_EXISTS ),
399 Status.CONFLICT, ErrorType.PROTOCOL,
400 ErrorTag.DATA_EXISTS, "mock error", null, null );
404 public void testToJsonResponseWithDataMissingErrorTag() throws Exception {
406 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
407 ErrorTag.DATA_MISSING ),
408 Status.CONFLICT, ErrorType.PROTOCOL,
409 ErrorTag.DATA_MISSING, "mock error", null, null );
413 public void testToJsonResponseWithOperationNotSupportedErrorTag() throws Exception {
415 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
416 ErrorTag.OPERATION_NOT_SUPPORTED ),
417 Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
418 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null );
422 public void testToJsonResponseWithOperationFailedErrorTag() throws Exception {
424 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
425 ErrorTag.OPERATION_FAILED ),
426 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
427 ErrorTag.OPERATION_FAILED, "mock error", null, null );
431 public void testToJsonResponseWithPartialOperationErrorTag() throws Exception {
433 testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
434 ErrorTag.PARTIAL_OPERATION ),
435 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
436 ErrorTag.PARTIAL_OPERATION, "mock error", null, null );
440 public void testToJsonResponseWithErrorAppTag() throws Exception {
442 testJsonResponse( new RestconfDocumentedException( new RestconfError(
443 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
444 "mock error", "mock-app-tag" ) ),
445 Status.BAD_REQUEST, ErrorType.APPLICATION,
446 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null );
450 public void testToJsonResponseWithMultipleErrors() throws Exception {
452 List<RestconfError> errorList = Arrays.asList(
453 new RestconfError( ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1" ),
454 new RestconfError( ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2" ) );
455 stageMockEx( new RestconfDocumentedException( errorList ) );
457 Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
459 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_JSON, Status.CONFLICT );
461 JsonArray arrayElement = parseJsonErrorArrayElement( stream );
463 assertEquals( "\"error\" Json array element length", 2, arrayElement.size() );
465 verifyJsonErrorNode( arrayElement.get( 0 ), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
466 "mock error1", null, null );
468 verifyJsonErrorNode( arrayElement.get( 1 ), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED,
469 "mock error2", null, null );
473 public void testToJsonResponseWithErrorInfo() throws Exception {
475 String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
476 testJsonResponse( new RestconfDocumentedException( new RestconfError(
477 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
478 "mock error", "mock-app-tag", errorInfo ) ),
479 Status.BAD_REQUEST, ErrorType.APPLICATION,
480 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
481 new ComplexErrorInfoVerifier( ImmutableMap.of(
482 "session-id", "123", "address", "1.2.3.4" ) ) );
486 public void testToJsonResponseWithExceptionCause() throws Exception {
488 Exception cause = new Exception( "mock exception cause" );
489 testJsonResponse( new RestconfDocumentedException( "mock error", cause ),
490 Status.INTERNAL_SERVER_ERROR, ErrorType.APPLICATION,
491 ErrorTag.OPERATION_FAILED, "mock error", null,
492 new SimpleErrorInfoVerifier( cause.getMessage() ) );
495 void testXMLResponse( final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
496 final ErrorTag expErrorTag, final String expErrorMessage,
497 final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier ) throws Exception
501 Response resp = target("/operational/foo").request( MediaType.APPLICATION_XML ).get();
503 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_XML, expStatus );
505 verifyXMLResponseBody( stream, expErrorType, expErrorTag, expErrorMessage,
506 expErrorAppTag, errorInfoVerifier );
510 public void testToXMLResponseWithMessageOnly() throws Exception {
512 testXMLResponse( new RestconfDocumentedException( "mock error" ), Status.INTERNAL_SERVER_ERROR,
513 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null );
515 // To test verification code
517 // "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">"+
519 // " <error-type>application</error-type>"+
520 // " <error-tag>operation-failed</error-tag>"+
521 // " <error-message>An error occurred</error-message>"+
523 // " <session-id>123</session-id>" +
524 // " <address>1.2.3.4</address>" +
525 // " </error-info>" +
529 // verifyXMLResponseBody( new java.io.StringBufferInputStream(xml), ErrorType.APPLICATION,
530 // ErrorTag.OPERATION_FAILED, "An error occurred", null,
531 // com.google.common.collect.ImmutableMap.of( "session-id", "123", "address", "1.2.3.4" ) );
535 public void testToXMLResponseWithInUseErrorTag() throws Exception {
537 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
539 Status.CONFLICT, ErrorType.PROTOCOL,
540 ErrorTag.IN_USE, "mock error", null, null );
544 public void testToXMLResponseWithInvalidValueErrorTag() throws Exception {
546 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.RPC,
547 ErrorTag.INVALID_VALUE ),
548 Status.BAD_REQUEST, ErrorType.RPC,
549 ErrorTag.INVALID_VALUE, "mock error", null, null );
553 public void testToXMLResponseWithTooBigErrorTag() throws Exception {
555 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.TRANSPORT,
557 Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT,
558 ErrorTag.TOO_BIG, "mock error", null, null );
562 public void testToXMLResponseWithMissingAttributeErrorTag() throws Exception {
564 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
565 ErrorTag.MISSING_ATTRIBUTE ),
566 Status.BAD_REQUEST, ErrorType.PROTOCOL,
567 ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null );
571 public void testToXMLResponseWithBadAttributeErrorTag() throws Exception {
573 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
574 ErrorTag.BAD_ATTRIBUTE ),
575 Status.BAD_REQUEST, ErrorType.PROTOCOL,
576 ErrorTag.BAD_ATTRIBUTE, "mock error", null, null );
579 public void testToXMLResponseWithUnknownAttributeErrorTag() throws Exception {
581 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
582 ErrorTag.UNKNOWN_ATTRIBUTE ),
583 Status.BAD_REQUEST, ErrorType.PROTOCOL,
584 ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null );
588 public void testToXMLResponseWithBadElementErrorTag() throws Exception {
590 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
591 ErrorTag.BAD_ELEMENT ),
592 Status.BAD_REQUEST, ErrorType.PROTOCOL,
593 ErrorTag.BAD_ELEMENT, "mock error", null, null );
597 public void testToXMLResponseWithUnknownElementErrorTag() throws Exception {
599 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
600 ErrorTag.UNKNOWN_ELEMENT ),
601 Status.BAD_REQUEST, ErrorType.PROTOCOL,
602 ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null );
606 public void testToXMLResponseWithUnknownNamespaceErrorTag() throws Exception {
608 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
609 ErrorTag.UNKNOWN_NAMESPACE ),
610 Status.BAD_REQUEST, ErrorType.PROTOCOL,
611 ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null );
615 public void testToXMLResponseWithMalformedMessageErrorTag() throws Exception {
617 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
618 ErrorTag.MALFORMED_MESSAGE ),
619 Status.BAD_REQUEST, ErrorType.PROTOCOL,
620 ErrorTag.MALFORMED_MESSAGE, "mock error", null, null );
624 public void testToXMLResponseWithAccessDeniedErrorTag() throws Exception {
626 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
627 ErrorTag.ACCESS_DENIED ),
628 Status.FORBIDDEN, ErrorType.PROTOCOL,
629 ErrorTag.ACCESS_DENIED, "mock error", null, null );
633 public void testToXMLResponseWithLockDeniedErrorTag() throws Exception {
635 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
636 ErrorTag.LOCK_DENIED ),
637 Status.CONFLICT, ErrorType.PROTOCOL,
638 ErrorTag.LOCK_DENIED, "mock error", null, null );
642 public void testToXMLResponseWithResourceDeniedErrorTag() throws Exception {
644 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
645 ErrorTag.RESOURCE_DENIED ),
646 Status.CONFLICT, ErrorType.PROTOCOL,
647 ErrorTag.RESOURCE_DENIED, "mock error", null, null );
651 public void testToXMLResponseWithRollbackFailedErrorTag() throws Exception {
653 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
654 ErrorTag.ROLLBACK_FAILED ),
655 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
656 ErrorTag.ROLLBACK_FAILED, "mock error", null, null );
660 public void testToXMLResponseWithDataExistsErrorTag() throws Exception {
662 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
663 ErrorTag.DATA_EXISTS ),
664 Status.CONFLICT, ErrorType.PROTOCOL,
665 ErrorTag.DATA_EXISTS, "mock error", null, null );
669 public void testToXMLResponseWithDataMissingErrorTag() throws Exception {
671 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
672 ErrorTag.DATA_MISSING ),
673 Status.CONFLICT, ErrorType.PROTOCOL,
674 ErrorTag.DATA_MISSING, "mock error", null, null );
678 public void testToXMLResponseWithOperationNotSupportedErrorTag() throws Exception {
680 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
681 ErrorTag.OPERATION_NOT_SUPPORTED ),
682 Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
683 ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null );
687 public void testToXMLResponseWithOperationFailedErrorTag() throws Exception {
689 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
690 ErrorTag.OPERATION_FAILED ),
691 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
692 ErrorTag.OPERATION_FAILED, "mock error", null, null );
696 public void testToXMLResponseWithPartialOperationErrorTag() throws Exception {
698 testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
699 ErrorTag.PARTIAL_OPERATION ),
700 Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
701 ErrorTag.PARTIAL_OPERATION, "mock error", null, null );
705 public void testToXMLResponseWithErrorAppTag() throws Exception {
707 testXMLResponse( new RestconfDocumentedException( new RestconfError(
708 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
709 "mock error", "mock-app-tag" ) ),
710 Status.BAD_REQUEST, ErrorType.APPLICATION,
711 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null );
715 public void testToXMLResponseWithErrorInfo() throws Exception {
717 String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
718 testXMLResponse( new RestconfDocumentedException( new RestconfError(
719 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
720 "mock error", "mock-app-tag", errorInfo ) ),
721 Status.BAD_REQUEST, ErrorType.APPLICATION,
722 ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
723 new ComplexErrorInfoVerifier( ImmutableMap.of(
724 "session-id", "123", "address", "1.2.3.4" ) ) );
728 public void testToXMLResponseWithExceptionCause() throws Exception {
730 Exception cause = new Exception( "mock exception cause" );
731 testXMLResponse( new RestconfDocumentedException( "mock error", cause ),
732 Status.INTERNAL_SERVER_ERROR, ErrorType.APPLICATION,
733 ErrorTag.OPERATION_FAILED, "mock error", null,
734 new SimpleErrorInfoVerifier( cause.getMessage() ) );
738 public void testToXMLResponseWithMultipleErrors() throws Exception {
740 List<RestconfError> errorList = Arrays.asList(
741 new RestconfError( ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1" ),
742 new RestconfError( ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2" ) );
743 stageMockEx( new RestconfDocumentedException( errorList ) );
745 Response resp = target("/operational/foo").request( MediaType.APPLICATION_XML ).get();
747 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_XML, Status.CONFLICT );
749 Document doc = parseXMLDocument( stream );
751 NodeList children = getXMLErrorList( doc, 2 );
753 verifyXMLErrorNode( children.item( 0 ), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
754 "mock error1", null, null );
756 verifyXMLErrorNode( children.item( 1 ), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED,
757 "mock error2", null, null );
761 public void testToResponseWithAcceptHeader() throws Exception {
763 stageMockEx( new RestconfDocumentedException( "mock error" ) );
765 Response resp = target("/operational/foo")
766 .request().header( "Accept", MediaType.APPLICATION_JSON ).get();
768 InputStream stream = verifyResponse( resp, MediaType.APPLICATION_JSON,
769 Status.INTERNAL_SERVER_ERROR );
771 verifyJsonResponseBody( stream, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error",
776 public void testToResponseWithStatusOnly() throws Exception {
778 // The StructuredDataToJsonProvider should throw a RestconfDocumentedException with no data
780 when( mockRestConf.readOperationalData( any( String.class ), any( UriInfo.class ) ) )
781 .thenReturn( new StructuredData( null, null, null ) );
783 Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
785 verifyResponse( resp, MediaType.TEXT_PLAIN, Status.NOT_FOUND );
788 InputStream verifyResponse( final Response resp, final String expMediaType, final Status expStatus ) {
789 assertEquals( "getMediaType", MediaType.valueOf( expMediaType ), resp.getMediaType() );
790 assertEquals( "getStatus", expStatus.getStatusCode(), resp.getStatus() );
792 Object entity = resp.getEntity();
793 assertEquals( "Response entity", true, entity instanceof InputStream );
794 InputStream stream = (InputStream)entity;
798 void verifyJsonResponseBody( final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
799 final String expErrorMessage, final String expErrorAppTag,
800 final ErrorInfoVerifier errorInfoVerifier ) throws Exception {
802 JsonArray arrayElement = parseJsonErrorArrayElement( stream );
804 assertEquals( "\"error\" Json array element length", 1, arrayElement.size() );
806 verifyJsonErrorNode( arrayElement.get( 0 ), expErrorType, expErrorTag, expErrorMessage,
807 expErrorAppTag, errorInfoVerifier );
810 private JsonArray parseJsonErrorArrayElement( final InputStream stream ) throws IOException {
811 ByteArrayOutputStream bos = new ByteArrayOutputStream();
812 ByteStreams.copy( stream, bos );
814 System.out.println("JSON: "+bos.toString());
816 JsonParser parser = new JsonParser();
817 JsonElement rootElement;
820 rootElement = parser.parse(
821 new InputStreamReader( new ByteArrayInputStream( bos.toByteArray() ) ) );
823 catch( Exception e ) {
824 throw new IllegalArgumentException( "Invalid JSON response:\n" + bos.toString(), e );
827 assertTrue( "Root element of Json is not an Object", rootElement.isJsonObject() );
829 Set<Entry<String, JsonElement>> errorsEntrySet = rootElement.getAsJsonObject().entrySet();
830 assertEquals( "Json Object element set count", 1, errorsEntrySet.size() );
832 Entry<String, JsonElement> errorsEntry = errorsEntrySet.iterator().next();
833 JsonElement errorsElement = errorsEntry.getValue();
834 assertEquals( "First Json element name", "errors", errorsEntry.getKey() );
835 assertTrue( "\"errors\" Json element is not an Object", errorsElement.isJsonObject() );
837 Set<Entry<String, JsonElement>> errorListEntrySet = errorsElement.getAsJsonObject().entrySet();
838 assertEquals( "Root \"errors\" element child count", 1, errorListEntrySet.size() );
840 JsonElement errorListElement = errorListEntrySet.iterator().next().getValue();
841 assertEquals( "\"errors\" child Json element name", "error",
842 errorListEntrySet.iterator().next().getKey() );
843 assertTrue( "\"error\" Json element is not an Array", errorListElement.isJsonArray() );
845 return errorListElement.getAsJsonArray();
848 void verifyJsonErrorNode( final JsonElement errorEntryElement, final ErrorType expErrorType, final ErrorTag expErrorTag,
849 final String expErrorMessage, final String expErrorAppTag,
850 final ErrorInfoVerifier errorInfoVerifier ) {
852 JsonElement errorInfoElement = null;
853 Map<String, String> actualErrorInfo = null;
854 Map<String, String> leafMap = Maps.newHashMap();
855 for( Entry<String, JsonElement> entry: errorEntryElement.getAsJsonObject().entrySet() ) {
856 String leafName = entry.getKey();
857 JsonElement leafElement = entry.getValue();
859 if( "error-info".equals( leafName ) ) {
860 assertNotNull( "Found unexpected \"error-info\" element", errorInfoVerifier );
861 errorInfoElement = leafElement;
864 assertTrue( "\"error\" leaf Json element " + leafName +
865 " is not a Primitive", leafElement.isJsonPrimitive() );
867 leafMap.put( leafName, leafElement.getAsString() );
871 assertEquals( "error-type", expErrorType.getErrorTypeTag(), leafMap.remove( "error-type" ) );
872 assertEquals( "error-tag", expErrorTag.getTagValue(), leafMap.remove( "error-tag" ) );
874 verifyOptionalJsonLeaf( leafMap.remove( "error-message" ), expErrorMessage, "error-message" );
875 verifyOptionalJsonLeaf( leafMap.remove( "error-app-tag" ), expErrorAppTag, "error-app-tag" );
877 if( !leafMap.isEmpty() ) {
878 fail( "Found unexpected Json leaf elements for \"error\" element: " + leafMap );
881 if( errorInfoVerifier != null ) {
882 assertNotNull( "Missing \"error-info\" element", errorInfoElement );
883 errorInfoVerifier.verifyJson( errorInfoElement );
887 void verifyOptionalJsonLeaf( final String actualValue, final String expValue, final String tagName ) {
888 if( expValue != null ) {
889 assertEquals( tagName, expValue, actualValue );
892 assertNull( "Found unexpected \"error\" leaf entry for: " + tagName, actualValue );
896 void verifyXMLResponseBody( final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag,
897 final String expErrorMessage, final String expErrorAppTag,
898 final ErrorInfoVerifier errorInfoVerifier )
901 Document doc = parseXMLDocument( stream );
903 NodeList children = getXMLErrorList( doc, 1 );
905 verifyXMLErrorNode( children.item( 0 ), expErrorType, expErrorTag, expErrorMessage,
906 expErrorAppTag, errorInfoVerifier );
909 private Document parseXMLDocument( final InputStream stream ) throws IOException {
910 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
911 factory.setNamespaceAware(true);
912 factory.setCoalescing(true);
913 factory.setIgnoringElementContentWhitespace(true);
914 factory.setIgnoringComments(true);
916 ByteArrayOutputStream bos = new ByteArrayOutputStream();
917 ByteStreams.copy( stream, bos );
919 System.out.println("XML: "+bos.toString());
923 doc = factory.newDocumentBuilder().parse( new ByteArrayInputStream( bos.toByteArray() ) );
925 catch( Exception e ) {
926 throw new IllegalArgumentException( "Invalid XML response:\n" + bos.toString(), e );
931 void verifyXMLErrorNode( final Node errorNode, final ErrorType expErrorType, final ErrorTag expErrorTag,
932 final String expErrorMessage, final String expErrorAppTag,
933 final ErrorInfoVerifier errorInfoVerifier ) throws Exception {
935 String errorType = (String)ERROR_TYPE.evaluate( errorNode, XPathConstants.STRING );
936 assertEquals( "error-type", expErrorType.getErrorTypeTag(), errorType );
938 String errorTag = (String)ERROR_TAG.evaluate( errorNode, XPathConstants.STRING );
939 assertEquals( "error-tag", expErrorTag.getTagValue(), errorTag );
941 verifyOptionalXMLLeaf( errorNode, ERROR_MESSAGE, expErrorMessage, "error-message" );
942 verifyOptionalXMLLeaf( errorNode, ERROR_APP_TAG, expErrorAppTag, "error-app-tag" );
944 Node errorInfoNode = (Node)ERROR_INFO.evaluate( errorNode, XPathConstants.NODE );
945 if( errorInfoVerifier != null ) {
946 assertNotNull( "Missing \"error-info\" node", errorInfoNode );
948 errorInfoVerifier.verifyXML( errorInfoNode );
951 assertNull( "Found unexpected \"error-info\" node", errorInfoNode );
955 void verifyOptionalXMLLeaf( final Node fromNode, final XPathExpression xpath, final String expValue,
956 final String tagName ) throws Exception {
957 if( expValue != null ) {
958 String actual = (String)xpath.evaluate( fromNode, XPathConstants.STRING );
959 assertEquals( tagName, expValue, actual );
962 assertNull( "Found unexpected \"error\" leaf entry for: " + tagName,
963 xpath.evaluate( fromNode, XPathConstants.NODE ) );
967 NodeList getXMLErrorList( final Node fromNode, final int count ) throws Exception {
968 NodeList errorList = (NodeList)ERROR_LIST.evaluate( fromNode, XPathConstants.NODESET );
969 assertNotNull( "Root errors node is empty", errorList );
970 assertEquals( "Root errors node child count", count, errorList.getLength() );