RestconfServer is strictly asynchronous 26/109026/3
authorRobert Varga <robert.varga@pantheon.tech>
Sun, 19 Nov 2023 19:23:40 +0000 (20:23 +0100)
committerRobert Varga <nite@hq.sk>
Mon, 20 Nov 2023 08:55:35 +0000 (08:55 +0000)
Make sure all methods are returning RestconfFuture, meaning everything
is potentially asynchronous and every call fail in a well-documented
way.

Take the time to create an assertion framework in AbstractRestconfTest,
which makes testing easy and understandable.

JIRA: NETCONF-718
Change-Id: I852f78568ed14ba527284cf835bfdee5ec024452
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
13 files changed:
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/jaxrs/JaxRsRestconf.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/api/RestconfServer.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/mdsal/MdsalRestconfServer.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/jaxrs/AbstractRestconfTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/jaxrs/Netconf822Test.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/jaxrs/RestconfDataDeleteTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/jaxrs/RestconfDataGetTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/jaxrs/RestconfDataPatchTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/jaxrs/RestconfDataPostTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/jaxrs/RestconfDataPutTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/jaxrs/RestconfOperationsGetTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/jaxrs/RestconfOperationsPostTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/jaxrs/RestconfYangLibraryVersionGetTest.java

index d292b49fa71322aad0ffade72905f6c9683701e7..315c4717abf0676674abb3deeff6d05923ec4108 100644 (file)
@@ -14,11 +14,11 @@ import java.time.Clock;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.List;
+import java.util.function.Function;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.Encoded;
 import javax.ws.rs.GET;
-import javax.ws.rs.NotFoundException;
 import javax.ws.rs.PATCH;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
@@ -32,7 +32,6 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
-import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.restconf.api.MediaTypes;
 import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.errors.RestconfFuture;
@@ -570,59 +569,71 @@ public final class JaxRsRestconf {
     /**
      * List RPC and action operations in RFC7951 format.
      *
-     * @return A string containing a JSON document conforming to both RFC8040 and RFC7951.
+     * @param ar {@link AsyncResponse} which needs to be completed
      */
     @GET
     @Path("/operations")
     @Produces({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
-    public String operationsJsonGET() {
-        return server.operationsGET().toJSON();
+    public void operationsJsonGET(@Suspended final AsyncResponse ar) {
+        completeOperationsJsonGet(server.operationsGET(), ar);
     }
 
     /**
      * Retrieve list of operations and actions supported by the server or device in JSON format.
      *
      * @param operation path parameter to identify device and/or operation
-     * @return A string containing a JSON document conforming to both RFC8040 and RFC7951.
+     * @param ar {@link AsyncResponse} which needs to be completed
      */
     @GET
     @Path("/operations/{operation:.+}")
     @Produces({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
-    public String operationsJsonGET(@PathParam("operation") final String operation) {
-        return operationsGET(operation).toJSON();
+    public void operationsJsonGET(@PathParam("operation") final String operation, final AsyncResponse ar) {
+        completeOperationsGet(server.operationsGET(operation), ar, OperationsGetResult::toJSON);
+    }
+
+    private static void completeOperationsJsonGet(final RestconfFuture<OperationsGetResult> future,
+            final AsyncResponse ar) {
+        completeOperationsGet(future, ar, OperationsGetResult::toJSON);
     }
 
     /**
      * List RPC and action operations in RFC8040 XML format.
      *
-     * @return A string containing an XML document conforming to both RFC8040 section 11.3.1 and page 84.
+     * @param ar {@link AsyncResponse} which needs to be completed
      */
     @GET
     @Path("/operations")
     @Produces({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
-    public String operationsXmlGET() {
-        return server.operationsGET().toXML();
+    public void operationsXmlGET(@Suspended final AsyncResponse ar) {
+        completeOperationsXmlGet(server.operationsGET(), ar);
     }
 
     /**
      * Retrieve list of operations and actions supported by the server or device in XML format.
      *
      * @param operation path parameter to identify device and/or operation
-     * @return A string containing an XML document conforming to both RFC8040 section 11.3.1 and page 84.
+     * @param ar {@link AsyncResponse} which needs to be completed
      */
     @GET
     @Path("/operations/{operation:.+}")
     @Produces({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
-    public String operationsXmlGET(@PathParam("operation") final String operation) {
-        return operationsGET(operation).toXML();
+    public void operationsXmlGET(@PathParam("operation") final String operation, final AsyncResponse ar) {
+        completeOperationsXmlGet(server.operationsGET(operation), ar);
     }
 
-    private @NonNull OperationsGetResult operationsGET(final String operation) {
-        final var content = server.operationsGET(operation);
-        if (content == null) {
-            throw new NotFoundException();
-        }
-        return content;
+    private static void completeOperationsXmlGet(final RestconfFuture<OperationsGetResult> future,
+            final AsyncResponse ar) {
+        completeOperationsGet(future, ar, OperationsGetResult::toXML);
+    }
+
+    private static void completeOperationsGet(final RestconfFuture<OperationsGetResult> future, final AsyncResponse ar,
+            final Function<OperationsGetResult, String> toString) {
+        future.addCallback(new JaxRsRestconfCallback<OperationsGetResult>(ar) {
+            @Override
+            Response transform(final OperationsGetResult result) {
+                return Response.ok().entity(toString.apply(result)).build();
+            }
+        });
     }
 
     /**
@@ -700,7 +711,7 @@ public final class JaxRsRestconf {
     /**
      * Get revision of IETF YANG Library module.
      *
-     * @return {@link NormalizedNodePayload}
+     * @param ar {@link AsyncResponse} which needs to be completed
      */
     @GET
     @Path("/yang-library-version")
@@ -711,7 +722,12 @@ public final class JaxRsRestconf {
         MediaType.APPLICATION_XML,
         MediaType.TEXT_XML
     })
-    public NormalizedNodePayload yangLibraryVersionGET() {
-        return server.yangLibraryVersionGET();
+    public void yangLibraryVersionGET(@Suspended final AsyncResponse ar) {
+        server.yangLibraryVersionGET().addCallback(new JaxRsRestconfCallback<NormalizedNodePayload>(ar) {
+            @Override
+            Response transform(final NormalizedNodePayload result) {
+                return Response.ok().entity(result).build();
+            }
+        });
     }
 }
index 4b8212d9eb190d50cb25cb12f7a1138cb0ce3fe7..b5c6b7ad29b70a3b30e35db99d207c8d30b3dbac 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.restconf.server.api;
 import java.net.URI;
 import java.util.Map;
 import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.restconf.api.ApiPath;
 import org.opendaylight.restconf.common.errors.RestconfFuture;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
@@ -120,24 +119,24 @@ public interface RestconfServer {
     RestconfFuture<DataPutResult> dataPUT(String identifier, ResourceBody body, Map<String, String> queryParameters);
 
     /**
-     * Return the set of supported RPCs supported by {@link #operationsPOST(URI, String, OperationInputBody)}.
+     * Return the set of supported RPCs supported by {@link #operationsPOST(URI, String, OperationInputBody)},
+     * as expressed in the <a href="https://www.rfc-editor.org/rfc/rfc8040#page-84">ietf-restconf.yang</a>
+     * {@code container operations} statement.
      *
-     * @return An {@link OperationsGetResult}
+     * @return A {@link RestconfFuture} completing with an {@link OperationsGetResult}
      */
-    OperationsGetResult operationsGET();
+    RestconfFuture<OperationsGetResult> operationsGET();
 
     /*
      * Return the details about a particular operation supported by
      * {@link #operationsPOST(URI, String, OperationInputBody)}, as expressed in the
-     * <a href="https://www.rfc-editor.org/rfc/rfc8040#page-84>RFC8040<a> {@code container operations} statement.
+     * <a href="https://www.rfc-editor.org/rfc/rfc8040#page-84">ietf-restconfig.yang</a>
+     * {@code container operations} statement.
      *
      * @param operation An operation
-     * @return An {@link OperationsContent}, or {@code null} if {@code operation} does not point to an {@code rpc}
+     * @return A {@link RestconfFuture} completing with an {@link OperationsGetResult}
      */
-    // FIXME: 'operation' should really be an ApiIdentifier with non-null module, but we also support ang-ext:mount,
-    //        and hence it is a path right now
-    // FIXME: use ApiPath instead of String
-    @Nullable OperationsGetResult operationsGET(String operation);
+    RestconfFuture<OperationsGetResult> operationsGET(String operation);
 
     /**
      * Invoke an RPC operation, as defined in
@@ -146,7 +145,7 @@ public interface RestconfServer {
      * @param restconfURI Base URI of the request
      * @param operation {@code <operation>} path, really an {@link ApiPath} to an {@code rpc}
      * @param body RPC operation
-     * @return A {@link RestconfFuture} of the {@link OperationOutput operation result}
+     * @return A {@link RestconfFuture} completing with {@link OperationOutput}
      */
     // FIXME: 'operation' should really be an ApiIdentifier with non-null module, but we also support ang-ext:mount,
     //        and hence it is a path right now
@@ -157,10 +156,10 @@ public interface RestconfServer {
      * Return the revision of {@code ietf-yang-library} module implemented by this server, as defined in
      * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-3.3.3">RFC8040 {+restconf}/yang-library-version</a>.
      *
-     * @return A {@code yang-library-version} element
+     * @return A {@link RestconfFuture} completing with {@link NormalizedNodePayload} containing a single
+     *        {@code yang-library-version} leaf element.
      */
-    // FIXME: this is a simple coning-variadic return, similar to how OperationsContent is handled use a common
-    //        construct for both cases
-    // FIXME: RestconfFuture if we transition to being used by restconf-client implementation
-    NormalizedNodePayload yangLibraryVersionGET();
+    // FIXME: this is a simple encoding-variadic return, similar to how OperationsContent is handled use a common
+    //        construct for both cases -- in this case it carries a yang.common.Revision
+    RestconfFuture<NormalizedNodePayload> yangLibraryVersionGET();
 }
index c688ad00e896e8376dcaeb292de57372bd67d380..47599d5720e12588589c7120f5cbadfaeb29323f 100644 (file)
@@ -384,12 +384,12 @@ public final class MdsalRestconfServer implements RestconfServer {
     }
 
     @Override
-    public OperationsGetResult operationsGET() {
+    public RestconfFuture<OperationsGetResult> operationsGET() {
         return operationsGET(databindProvider.currentContext().modelContext());
     }
 
     @Override
-    public OperationsGetResult operationsGET(final String operation) {
+    public RestconfFuture<OperationsGetResult> operationsGET(final String operation) {
         // get current module RPCs/actions by RPC/action name
         final var inference = bindRequestPath(operation).inference();
         if (inference.isEmpty()) {
@@ -398,17 +398,19 @@ public final class MdsalRestconfServer implements RestconfServer {
 
         final var stmt = inference.toSchemaInferenceStack().currentStatement();
         if (stmt instanceof RpcEffectiveStatement rpc) {
-            return new OperationsGetResult.Leaf(inference.getEffectiveModelContext(), rpc.argument());
+            return RestconfFuture.of(
+                new OperationsGetResult.Leaf(inference.getEffectiveModelContext(), rpc.argument()));
         }
-        LOG.debug("Operation '{}' resulted in non-RPC {}", operation, stmt);
-        return null;
+        return RestconfFuture.failed(new RestconfDocumentedException("RPC not found",
+            ErrorType.PROTOCOL, ErrorTag.DATA_MISSING));
     }
 
-    private static @NonNull OperationsGetResult operationsGET(final EffectiveModelContext modelContext) {
+    private static @NonNull RestconfFuture<OperationsGetResult> operationsGET(
+            final EffectiveModelContext modelContext) {
         final var modules = modelContext.getModuleStatements();
         if (modules.isEmpty()) {
             // No modules, or defensive return empty content
-            return new OperationsGetResult.Container(modelContext, ImmutableSetMultimap.of());
+            return RestconfFuture.of(new OperationsGetResult.Container(modelContext, ImmutableSetMultimap.of()));
         }
 
         // RPCs by their XMLNamespace/Revision
@@ -432,7 +434,7 @@ public final class MdsalRestconfServer implements RestconfServer {
                 .findFirst()
                 .ifPresent(row -> rpcs.putAll(QNameModule.create(entry.getKey(), row.getKey()), row.getValue()));
         }
-        return new OperationsGetResult.Container(modelContext, rpcs.build());
+        return RestconfFuture.of(new OperationsGetResult.Container(modelContext, rpcs.build()));
     }
 
     @Override
@@ -456,13 +458,17 @@ public final class MdsalRestconfServer implements RestconfServer {
     }
 
     @Override
-    public NormalizedNodePayload yangLibraryVersionGET() {
+    public RestconfFuture<NormalizedNodePayload> yangLibraryVersionGET() {
         final var stack = SchemaInferenceStack.of(databindProvider.currentContext().modelContext());
-        stack.enterYangData(YangApi.NAME);
-        stack.enterDataTree(Restconf.QNAME);
-        stack.enterDataTree(YANG_LIBRARY_VERSION);
-        return new NormalizedNodePayload(stack.toInference(),
-            ImmutableNodes.leafNode(YANG_LIBRARY_VERSION, YANG_LIBRARY_REVISION));
+        try {
+            stack.enterYangData(YangApi.NAME);
+            stack.enterDataTree(Restconf.QNAME);
+            stack.enterDataTree(YANG_LIBRARY_VERSION);
+        } catch (IllegalArgumentException e) {
+            return RestconfFuture.failed(new RestconfDocumentedException("RESTCONF is not available"));
+        }
+        return RestconfFuture.of(new NormalizedNodePayload(stack.toInference(),
+            ImmutableNodes.leafNode(YANG_LIBRARY_VERSION, YANG_LIBRARY_REVISION)));
     }
 
     private @NonNull InstanceIdentifierContext bindRequestPath(final String identifier) {
index 50c32f33607cfba85c194d75c440412406aa0559..f666f0d4448de7d55db813b77b2878e26ec0fafb 100644 (file)
@@ -7,14 +7,22 @@
  */
 package org.opendaylight.restconf.nb.jaxrs;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.util.List;
+import java.util.function.Consumer;
 import javax.ws.rs.container.AsyncResponse;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
-import org.eclipse.jdt.annotation.NonNull;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.opendaylight.mdsal.dom.api.DOMActionService;
@@ -23,9 +31,12 @@ import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.nb.rfc8040.AbstractJukeboxTest;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
+import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.restconf.server.mdsal.MdsalRestconfServer;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 @ExtendWith(MockitoExtension.class)
@@ -33,8 +44,6 @@ abstract class AbstractRestconfTest extends AbstractJukeboxTest {
     @Mock
     UriInfo uriInfo;
     @Mock
-    AsyncResponse asyncResponse;
-    @Mock
     DOMDataBroker dataBroker;
     @Mock
     DOMActionService actionService;
@@ -44,10 +53,6 @@ abstract class AbstractRestconfTest extends AbstractJukeboxTest {
     DOMMountPointService mountPointService;
     @Mock
     DOMMountPoint mountPoint;
-    @Captor
-    ArgumentCaptor<Response> responseCaptor;
-    @Captor
-    ArgumentCaptor<RestconfDocumentedException> exceptionCaptor;
 
     JaxRsRestconf restconf;
 
@@ -57,7 +62,48 @@ abstract class AbstractRestconfTest extends AbstractJukeboxTest {
             rpcService, actionService, mountPointService));
     }
 
-    @NonNull EffectiveModelContext modelContext() {
+    EffectiveModelContext modelContext() {
         return JUKEBOX_SCHEMA;
     }
+
+    static final NormalizedNode assertNormalizedNode(final int status, final Consumer<AsyncResponse> invocation) {
+        return assertEntity(NormalizedNodePayload.class, status, invocation).data();
+    }
+
+    static final <T> T assertEntity(final Class<T> expectedType, final int expectedStatus,
+            final Consumer<AsyncResponse> invocation) {
+        return assertInstanceOf(expectedType, assertEntity(expectedStatus, invocation));
+    }
+
+    static final Object assertEntity(final int expectedStatus, final Consumer<AsyncResponse> invocation) {
+        return assertResponse(expectedStatus, invocation).getEntity();
+    }
+
+    static final RestconfError assertError(final Consumer<AsyncResponse> invocation) {
+        final var errors = assertErrors(invocation);
+        assertEquals(1, errors.size());
+        final var error = errors.get(0);
+        assertNotNull(error);
+        return error;
+    }
+
+    static final List<RestconfError> assertErrors(final Consumer<AsyncResponse> invocation) {
+        final var ar = mock(AsyncResponse.class);
+        final var captor = ArgumentCaptor.forClass(RestconfDocumentedException.class);
+        doReturn(true).when(ar).resume(captor.capture());
+        invocation.accept(ar);
+        verify(ar).resume(any(RestconfDocumentedException.class));
+        return captor.getValue().getErrors();
+    }
+
+    static final Response assertResponse(final int expectedStatus, final Consumer<AsyncResponse> invocation) {
+        final var ar = mock(AsyncResponse.class);
+        final var captor = ArgumentCaptor.forClass(Response.class);
+        doReturn(true).when(ar).resume(captor.capture());
+        invocation.accept(ar);
+        verify(ar).resume(any(Response.class));
+        final var response = captor.getValue();
+        assertEquals(expectedStatus, response.getStatus());
+        return response;
+    }
 }
index ae746bfaffefe2d7c05cd533418231d5ad23ee2c..3c41d6b89dacf4612f2ca2422b2b4df88572d3b3 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.restconf.nb.jaxrs;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import org.eclipse.jdt.annotation.NonNull;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -18,11 +17,11 @@ import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
 @ExtendWith(MockitoExtension.class)
 class Netconf822Test extends AbstractRestconfTest {
-    private static final @NonNull EffectiveModelContext MODEL_CONTEXT =
+    private static final EffectiveModelContext MODEL_CONTEXT =
         YangParserTestUtils.parseYangResourceDirectory("/nc822");
 
     @Override
-    @NonNull EffectiveModelContext modelContext() {
+    EffectiveModelContext modelContext() {
         return MODEL_CONTEXT;
     }
 
@@ -34,19 +33,19 @@ class Netconf822Test extends AbstractRestconfTest {
                 "foo:new" : [null],
                 "foo:new1" : [null]
               }
-            }""", restconf.operationsJsonGET());
+            }""", assertEntity(200, ar -> restconf.operationsJsonGET(ar)));
         assertEquals("""
             <operations xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf">
               <new xmlns="foo"/>
               <new1 xmlns="foo"/>
-            </operations>""", restconf.operationsXmlGET());
+            </operations>""", assertEntity(200, ar -> restconf.operationsXmlGET(ar)));
     }
 
     @Test
     void testOperationsContentByIdentifier() {
         assertEquals("""
-            { "foo:new1" : [null] }""", restconf.operationsJsonGET("foo:new1"));
+            { "foo:new1" : [null] }""", assertEntity(200, ar -> restconf.operationsJsonGET("foo:new1", ar)));
         assertEquals("""
-            <new1 xmlns="foo"/>""", restconf.operationsXmlGET("foo:new1"));
+            <new1 xmlns="foo"/>""", assertEntity(200, ar -> restconf.operationsXmlGET("foo:new1", ar)));
     }
 }
index dddd4ab45bc19ebc9f995fc09ea0849a8a3f7df0..510a6bdeb0d9a212e7ad6e185aa1aeaa27e738f9 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.restconf.nb.jaxrs;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -48,10 +49,8 @@ class RestconfDataDeleteTest extends AbstractRestconfTest {
         doReturn(immediateTrueFluentFuture())
                 .when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
         doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.dataDELETE("example-jukebox:jukebox", asyncResponse);
 
-        assertEquals(204, responseCaptor.getValue().getStatus());
+        assertNull(assertEntity(204, ar -> restconf.dataDELETE("example-jukebox:jukebox", ar)));
     }
 
     @Test
@@ -60,12 +59,7 @@ class RestconfDataDeleteTest extends AbstractRestconfTest {
                 .when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
         doReturn(true).when(tx).cancel();
 
-        doReturn(true).when(asyncResponse).resume(exceptionCaptor.capture());
-        restconf.dataDELETE("example-jukebox:jukebox", asyncResponse);
-
-        final var errors = exceptionCaptor.getValue().getErrors();
-        assertEquals(1, errors.size());
-        final var error = errors.get(0);
+        final var error = assertError(ar -> restconf.dataDELETE("example-jukebox:jukebox", ar));
         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
         assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
     }
@@ -86,9 +80,8 @@ class RestconfDataDeleteTest extends AbstractRestconfTest {
         doReturn(immediateTrueFluentFuture())
                 .when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
         doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.dataDELETE("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", asyncResponse);
 
-        assertEquals(204, responseCaptor.getValue().getStatus());
+        assertNull(assertEntity(204,
+            ar -> restconf.dataDELETE("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", ar)));
     }
 }
index e154f9ea972969ddb1cd9f1bb1d1392a775ee04c..c60f8dfb6034297127d98606325392f07ef5e038 100644 (file)
@@ -15,7 +15,6 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
 
-import java.util.List;
 import java.util.Optional;
 import javax.ws.rs.core.MultivaluedHashMap;
 import org.junit.jupiter.api.BeforeEach;
@@ -30,7 +29,6 @@ import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
-import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -74,11 +72,8 @@ class RestconfDataGetTest extends AbstractRestconfTest {
         doReturn(immediateFluentFuture(Optional.empty()))
                 .when(tx).read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.dataGET("example-jukebox:jukebox", uriInfo, asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(200, response.getStatus());
-        assertEquals(EMPTY_JUKEBOX, ((NormalizedNodePayload) response.getEntity()).data());
+        assertEquals(EMPTY_JUKEBOX,
+            assertNormalizedNode(200, ar -> restconf.dataGET("example-jukebox:jukebox", uriInfo, ar)));
     }
 
     @Test
@@ -91,12 +86,8 @@ class RestconfDataGetTest extends AbstractRestconfTest {
                 .when(tx)
                 .read(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.of());
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.dataGET(uriInfo, asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(200, response.getStatus());
-
-        final var data = assertInstanceOf(ContainerNode.class, ((NormalizedNodePayload) response.getEntity()).data());
+        final var data = assertInstanceOf(ContainerNode.class,
+            assertNormalizedNode(200, ar -> restconf.dataGET(uriInfo, ar)));
         final var rootNodes = data.body();
         assertEquals(1, rootNodes.size());
         final var allDataChildren = assertInstanceOf(ContainerNode.class, rootNodes.iterator().next()).body();
@@ -130,13 +121,10 @@ class RestconfDataGetTest extends AbstractRestconfTest {
         doReturn(Optional.of(rpcService)).when(mountPoint).getService(DOMRpcService.class);
         doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.dataGET("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", uriInfo, asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(200, response.getStatus());
-
         // response must contain all child nodes from config and operational containers merged in one container
-        final var data = assertInstanceOf(ContainerNode.class, ((NormalizedNodePayload) response.getEntity()).data());
+        final var data = assertInstanceOf(ContainerNode.class,
+            assertNormalizedNode(200, ar ->
+                restconf.dataGET("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", uriInfo, ar)));
         assertEquals(3, data.size());
         assertNotNull(data.childByArg(CONT_PLAYER.name()));
         assertNotNull(data.childByArg(LIBRARY_NID));
@@ -151,12 +139,7 @@ class RestconfDataGetTest extends AbstractRestconfTest {
         doReturn(immediateFluentFuture(Optional.empty()))
                 .when(tx).read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
 
-        doReturn(true).when(asyncResponse).resume(exceptionCaptor.capture());
-        restconf.dataGET("example-jukebox:jukebox", uriInfo, asyncResponse);
-
-        final var errors = exceptionCaptor.getValue().getErrors();
-        assertEquals(1, errors.size());
-        final var error = errors.get(0);
+        final var error = assertError(ar -> restconf.dataGET("example-jukebox:jukebox", uriInfo, ar));
         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
         assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
         assertEquals("Request could not be completed because the relevant data model content does not exist",
@@ -168,25 +151,19 @@ class RestconfDataGetTest extends AbstractRestconfTest {
      */
     @Test
     void testReadDataConfigTest() {
-        final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
-        parameters.put("content", List.of("config"));
+        final var parameters = new MultivaluedHashMap<String, String>();
+        parameters.putSingle("content", "config");
 
         doReturn(parameters).when(uriInfo).getQueryParameters();
         doReturn(immediateFluentFuture(Optional.of(CONFIG_JUKEBOX))).when(tx)
                 .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.dataGET("example-jukebox:jukebox", uriInfo, asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(200, response.getStatus());
-
         // response must contain only config data
-        final var data = assertInstanceOf(ContainerNode.class, ((NormalizedNodePayload) response.getEntity()).data());
-
+        final var data = assertInstanceOf(ContainerNode.class,
+            assertNormalizedNode(200, ar -> restconf.dataGET("example-jukebox:jukebox", uriInfo, ar)));
         // config data present
         assertNotNull(data.childByArg(CONT_PLAYER.name()));
         assertNotNull(data.childByArg(LIBRARY_NID));
-
         // state data absent
         assertNull(data.childByArg(PLAYLIST_NID));
     }
@@ -203,15 +180,9 @@ class RestconfDataGetTest extends AbstractRestconfTest {
         doReturn(immediateFluentFuture(Optional.of(OPER_JUKEBOX))).when(tx)
                 .read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.dataGET("example-jukebox:jukebox", uriInfo, asyncResponse);
-        final var response = responseCaptor.getValue();
-
-        assertEquals(200, response.getStatus());
-
         // response must contain only operational data
-        final var data = assertInstanceOf(ContainerNode.class, ((NormalizedNodePayload) response.getEntity()).data());
-
+        final var data = assertInstanceOf(ContainerNode.class,
+            assertNormalizedNode(200, ar -> restconf.dataGET("example-jukebox:jukebox", uriInfo, ar)));
         // state data present
         assertNotNull(data.childByArg(CONT_PLAYER.name()));
         assertNotNull(data.childByArg(PLAYLIST_NID));
index 9c79d786bf0857b6e0191d8ab5308e7a98234bae..3f624e6ab082e1f80d00704c90daa6b11463c15e 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.restconf.nb.jaxrs;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.doNothing;
@@ -45,46 +44,44 @@ class RestconfDataPatchTest extends AbstractRestconfTest {
         doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
         doReturn(immediateTrueFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
         doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.dataYangJsonPATCH(stringInputStream("""
-            {
-              "ietf-yang-patch:yang-patch" : {
-                "patch-id" : "test patch id",
-                "edit" : [
-                  {
-                    "edit-id" : "create data",
-                    "operation" : "create",
-                    "target" : "/example-jukebox:jukebox",
-                    "value" : {
-                      "jukebox" : {
-                        "player" : {
-                          "gap" : "0.2"
+        final var status = assertEntity(PatchStatusContext.class, 200,
+            ar -> restconf.dataYangJsonPATCH(stringInputStream("""
+                {
+                  "ietf-yang-patch:yang-patch" : {
+                    "patch-id" : "test patch id",
+                    "edit" : [
+                      {
+                        "edit-id" : "create data",
+                        "operation" : "create",
+                        "target" : "/example-jukebox:jukebox",
+                        "value" : {
+                          "jukebox" : {
+                            "player" : {
+                              "gap" : "0.2"
+                            }
+                          }
                         }
-                      }
-                    }
-                  },
-                  {
-                    "edit-id" : "replace data",
-                    "operation" : "replace",
-                    "target" : "/example-jukebox:jukebox",
-                    "value" : {
-                      "jukebox" : {
-                        "player" : {
-                          "gap" : "0.3"
+                      },
+                      {
+                        "edit-id" : "replace data",
+                        "operation" : "replace",
+                        "target" : "/example-jukebox:jukebox",
+                        "value" : {
+                          "jukebox" : {
+                            "player" : {
+                              "gap" : "0.3"
+                            }
+                          }
                         }
+                      },
+                      {
+                        "edit-id" : "delete data",
+                        "operation" : "delete",
+                        "target" : "/example-jukebox:jukebox/player/gap"
                       }
-                    }
-                  },
-                  {
-                    "edit-id" : "delete data",
-                    "operation" : "delete",
-                    "target" : "/example-jukebox:jukebox/player/gap"
+                    ]
                   }
-                ]
-              }"""), asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(200, response.getStatus());
-        final var status = assertInstanceOf(PatchStatusContext.class, response.getEntity());
+                }"""), ar));
         assertTrue(status.ok());
         final var edits = status.editCollection();
         assertEquals(3, edits.size());
@@ -100,40 +97,37 @@ class RestconfDataPatchTest extends AbstractRestconfTest {
         doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
         doReturn(true).when(tx).cancel();
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.dataYangJsonPATCH(stringInputStream("""
-            {
-              "ietf-yang-patch:yang-patch" : {
-                "patch-id" : "test patch id",
-                "edit" : [
-                  {
-                    "edit-id" : "create data",
-                    "operation" : "create",
-                    "target" : "/example-jukebox:jukebox",
-                    "value" : {
-                      "jukebox" : {
-                        "player" : {
-                          "gap" : "0.2"
+        final var status = assertEntity(PatchStatusContext.class, 409, ar -> restconf.dataYangJsonPATCH(
+            stringInputStream("""
+                {
+                  "ietf-yang-patch:yang-patch" : {
+                    "patch-id" : "test patch id",
+                    "edit" : [
+                      {
+                        "edit-id" : "create data",
+                        "operation" : "create",
+                        "target" : "/example-jukebox:jukebox",
+                        "value" : {
+                          "jukebox" : {
+                            "player" : {
+                              "gap" : "0.2"
+                            }
+                          }
                         }
+                      },
+                      {
+                        "edit-id" : "remove data",
+                        "operation" : "remove",
+                        "target" : "/example-jukebox:jukebox/player/gap"
+                      },
+                      {
+                        "edit-id" : "delete data",
+                        "operation" : "delete",
+                        "target" : "/example-jukebox:jukebox/player/gap"
                       }
-                    }
-                  },
-                  {
-                    "edit-id" : "remove data",
-                    "operation" : "remove",
-                    "target" : "/example-jukebox:jukebox/player/gap"
-                  },
-                  {
-                    "edit-id" : "delete data",
-                    "operation" : "delete",
-                    "target" : "/example-jukebox:jukebox/player/gap"
+                    ]
                   }
-                ]
-              }
-            }"""), asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(409, response.getStatus());
-        final var status = assertInstanceOf(PatchStatusContext.class, response.getEntity());
+                }"""), ar));
         assertFalse(status.ok());
         final var edits = status.editCollection();
         assertEquals(3, edits.size());
@@ -157,43 +151,40 @@ class RestconfDataPatchTest extends AbstractRestconfTest {
         doReturn(immediateTrueFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
         doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.dataYangXmlPATCH(stringInputStream("""
-            <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
-              <patch-id>test patch id</patch-id>
-              <edit>
-                <edit-id>create data</edit-id>
-                <operation>create</operation>
-                <target>/example-jukebox:jukebox</target>
-                <value>
-                  <jukebox xmlns="http://example.com/ns/example-jukebox">
-                    <player>
-                      <gap>0.2</gap>
-                    </player>
-                  </jukebox>
-                </value>
-              </edit>
-              <edit>
-                <edit-id>replace data</edit-id>
-                <operation>replace</operation>
-                <target>/example-jukebox:jukebox</target>
-                <value>
-                  <jukebox xmlns="http://example.com/ns/example-jukebox">
-                    <player>
-                      <gap>0.3</gap>
-                    </player>
-                  </jukebox>
-                </value>
-              </edit>
-              <edit>
-                <edit-id>delete data</edit-id>
-                <operation>delete</operation>
-                <target>/example-jukebox:jukebox/player/gap</target>
-              </edit>
-            </yang-patch>"""), asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(200, response.getStatus());
-        final var status = assertInstanceOf(PatchStatusContext.class, response.getEntity());
+        final var status = assertEntity(PatchStatusContext.class, 200,
+            ar -> restconf.dataYangXmlPATCH(stringInputStream("""
+                <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
+                  <patch-id>test patch id</patch-id>
+                  <edit>
+                    <edit-id>create data</edit-id>
+                    <operation>create</operation>
+                    <target>/example-jukebox:jukebox</target>
+                    <value>
+                      <jukebox xmlns="http://example.com/ns/example-jukebox">
+                        <player>
+                          <gap>0.2</gap>
+                        </player>
+                      </jukebox>
+                    </value>
+                  </edit>
+                  <edit>
+                    <edit-id>replace data</edit-id>
+                    <operation>replace</operation>
+                    <target>/example-jukebox:jukebox</target>
+                    <value>
+                      <jukebox xmlns="http://example.com/ns/example-jukebox">
+                        <player>
+                          <gap>0.3</gap>
+                        </player>
+                      </jukebox>
+                    </value>
+                  </edit>
+                  <edit>
+                    <edit-id>delete data</edit-id>
+                    <operation>delete</operation>
+                    <target>/example-jukebox:jukebox/player/gap</target>
+                  </edit>
+                </yang-patch>"""), ar));
         assertTrue(status.ok());
         assertNull(status.globalErrors());
         final var edits = status.editCollection();
index 2314377cbfab957f21b675ed99b2b7c1e55b3e1a..9fcbc667137dd52501fb9398692e42dea3b82102 100644 (file)
@@ -45,15 +45,12 @@ class RestconfDataPostTest extends AbstractRestconfTest {
             Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME)).build());
         doReturn(UriBuilder.fromUri("http://localhost:8181/rests/")).when(uriInfo).getBaseUriBuilder();
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.postDataJSON(stringInputStream("""
-            {
-              "example-jukebox:jukebox" : {
-              }
-            }"""), uriInfo, asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(201, response.getStatus());
-        assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox"), response.getLocation());
+        assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox"),
+            assertResponse(201, ar -> restconf.postDataJSON(stringInputStream("""
+                {
+                  "example-jukebox:jukebox" : {
+                  }
+                }"""), uriInfo, ar)).getLocation());
     }
 
     @Test
@@ -64,17 +61,13 @@ class RestconfDataPostTest extends AbstractRestconfTest {
         doNothing().when(tx).put(LogicalDatastoreType.CONFIGURATION, node, BAND_ENTRY);
         doReturn(UriBuilder.fromUri("http://localhost:8181/rests/")).when(uriInfo).getBaseUriBuilder();
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.postDataJSON("example-jukebox:jukebox", stringInputStream("""
-            {
-              "example-jukebox:playlist" : {
-                "name" : "name of band",
-                "description" : "band description"
-              }
-            }"""), uriInfo, asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(201, response.getStatus());
         assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox/playlist=name%20of%20band"),
-            response.getLocation());
+            assertResponse(201, ar -> restconf.postDataJSON("example-jukebox:jukebox", stringInputStream("""
+                {
+                  "example-jukebox:playlist" : {
+                    "name" : "name of band",
+                    "description" : "band description"
+                  }
+                }"""), uriInfo, ar)).getLocation());
     }
 }
index 87733a661b709a93c806ea352126eae836a7b220..034a14e2cdc9d61e4754a0f5412e5f502678350f 100644 (file)
@@ -7,7 +7,7 @@
  */
 package org.opendaylight.restconf.nb.jaxrs;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -16,7 +16,6 @@ import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediate
 import java.util.Optional;
 import javax.ws.rs.core.MultivaluedHashMap;
 import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -54,17 +53,15 @@ class RestconfDataPutTest extends AbstractRestconfTest {
         doReturn(immediateTrueFluentFuture()).when(readTx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
         doNothing().when(rwTx).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, EMPTY_JUKEBOX);
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.dataJsonPUT("example-jukebox:jukebox", uriInfo, stringInputStream("""
-            {
-              "example-jukebox:jukebox" : {
-                 "player": {
-                   "gap": "0.2"
-                 }
-              }
-            }"""), asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
+        assertNull(assertEntity(204, ar -> restconf.dataJsonPUT("example-jukebox:jukebox", uriInfo,
+            stringInputStream("""
+                {
+                  "example-jukebox:jukebox" : {
+                    "player": {
+                      "gap": "0.2"
+                    }
+                  }
+                }"""), ar)));
     }
 
     @Test
@@ -78,15 +75,12 @@ class RestconfDataPutTest extends AbstractRestconfTest {
         doReturn(Optional.of(rpcService)).when(mountPoint).getService(DOMRpcService.class);
         doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.dataXmlPUT("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox",
-            uriInfo, stringInputStream("""
-                <jukebox xmlns="http://example.com/ns/example-jukebox">
-                  <player>
-                    <gap>0.2</gap>
-                  </player>
-                </jukebox>"""), asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
+        assertNull(assertEntity(204, ar -> restconf.dataXmlPUT(
+            "example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", uriInfo, stringInputStream("""
+            <jukebox xmlns="http://example.com/ns/example-jukebox">
+              <player>
+                <gap>0.2</gap>
+              </player>
+            </jukebox>"""), ar)));
     }
 }
index 7907bfd2b5d2d5bf6b182326965ce4734808da15..f2141fb3d3590b2514c0c7106ff41e230a6cdf48 100644 (file)
@@ -10,11 +10,11 @@ package org.opendaylight.restconf.nb.jaxrs;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 
 import java.util.Optional;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.opendaylight.mdsal.binding.runtime.spi.BindingRuntimeHelpers;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
@@ -48,9 +48,6 @@ class RestconfOperationsGetTest extends AbstractRestconfTest {
     private static final EffectiveModelContext MODEL_CONTEXT = BindingRuntimeHelpers.createRuntimeContext(
         Module1Data.class, Module2Data.class, NetworkTopology.class).getEffectiveModelContext();
 
-    @Mock
-    private DOMSchemaService schemaService;
-
     @Override
     EffectiveModelContext modelContext() {
         return MODEL_CONTEXT;
@@ -58,17 +55,16 @@ class RestconfOperationsGetTest extends AbstractRestconfTest {
 
     @Test
     void testOperationsJson() {
-        final var operationsJSON = restconf.operationsJsonGET();
-        assertEquals(EXPECTED_JSON, operationsJSON);
+        assertEquals(EXPECTED_JSON, assertEntity(200, ar -> restconf.operationsJsonGET(ar)));
     }
 
     @Test
     void testOperationsXml() {
-        final var operationsXML = restconf.operationsXmlGET();
-        assertEquals(EXPECTED_XML, operationsXML);
+        assertEquals(EXPECTED_XML, assertEntity(200, ar -> restconf.operationsXmlGET(ar)));
     }
 
     private void mockMountPoint() {
+        final var schemaService = mock(DOMSchemaService.class);
         doReturn(MODEL_CONTEXT).when(schemaService).getGlobalContext();
         doReturn(Optional.of(schemaService)).when(mountPoint).getService(DOMSchemaService.class);
         doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(any());
@@ -77,30 +73,28 @@ class RestconfOperationsGetTest extends AbstractRestconfTest {
     @Test
     void testMountPointOperationsJson() {
         mockMountPoint();
-        final var operationJSON = restconf.operationsJsonGET(DEVICE_ID);
-        assertEquals(EXPECTED_JSON, operationJSON);
+        assertEquals(EXPECTED_JSON, assertEntity(200, ar -> restconf.operationsJsonGET(DEVICE_ID, ar)));
     }
 
     @Test
     void testMountPointOperationsXml() {
         mockMountPoint();
-        final var operationXML = restconf.operationsXmlGET(DEVICE_ID);
-        assertEquals(EXPECTED_XML, operationXML);
+        assertEquals(EXPECTED_XML, assertEntity(200, ar -> restconf.operationsXmlGET(DEVICE_ID, ar)));
     }
 
     @Test
     void testMountPointSpecificOperationsJson() {
         mockMountPoint();
-        final var operationJSON = restconf.operationsJsonGET(DEVICE_RPC1_MODULE1_ID);
         assertEquals("""
-            { "module1:dummy-rpc1-module1" : [null] }""", operationJSON);
+            { "module1:dummy-rpc1-module1" : [null] }""",
+            assertEntity(200, ar -> restconf.operationsJsonGET(DEVICE_RPC1_MODULE1_ID, ar)));
     }
 
     @Test
     void testMountPointSpecificOperationsXml() {
         mockMountPoint();
-        final var operationXML = restconf.operationsXmlGET(DEVICE_RPC1_MODULE1_ID);
         assertEquals("""
-            <dummy-rpc1-module1 xmlns="module:1"/>""", operationXML);
+            <dummy-rpc1-module1 xmlns="module:1"/>""",
+            assertEntity(200, ar -> restconf.operationsXmlGET(DEVICE_RPC1_MODULE1_ID, ar)));
     }
 }
index 9a098a6281e1fad77543e9351ca7587e6b3f5830..c14aae9cf1264005e2597eb2cc1e8e476d1a3ba2 100644 (file)
@@ -7,8 +7,8 @@
  */
 package org.opendaylight.restconf.nb.jaxrs;
 
+import static org.junit.Assert.assertNull;
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
@@ -31,7 +31,6 @@ import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
-import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -74,14 +73,9 @@ class RestconfOperationsPostTest extends AbstractRestconfTest {
         doReturn(false).when(result).isEmpty();
 
         prepNNC(result);
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.operationsXmlPOST("invoke-rpc-module:rpc-test", stringInputStream("""
-            <input xmlns="invoke:rpc:module"/>
-            """), uriInfo, asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(200, response.getStatus());
-        final var entity = (NormalizedNodePayload) response.getEntity();
-        assertSame(result, entity.data());
+        assertSame(result, assertNormalizedNode(200, ar -> restconf.operationsXmlPOST("invoke-rpc-module:rpc-test",
+            stringInputStream("""
+                <input xmlns="invoke:rpc:module"/>"""), uriInfo, ar)));
     }
 
     @Test
@@ -90,31 +84,26 @@ class RestconfOperationsPostTest extends AbstractRestconfTest {
         doReturn(true).when(result).isEmpty();
 
         prepNNC(result);
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.operationsJsonPOST("invoke-rpc-module:rpc-test", stringInputStream("""
-            {
-              "invoke-rpc-module:input" : {
-              }
-            }"""), uriInfo, asyncResponse);
-        assertEquals(204, responseCaptor.getValue().getStatus());
+        assertNull(assertEntity(204, ar -> restconf.operationsJsonPOST("invoke-rpc-module:rpc-test",
+            stringInputStream("""
+                {
+                  "invoke-rpc-module:input" : {
+                  }
+                }"""), uriInfo, ar)));
     }
 
     @Test
-    void invokeRpcTest() throws Exception {
+    void invokeRpcTest() {
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(OUTPUT, List.of()))).when(rpcService)
             .invokeRpc(RPC, INPUT);
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.operationsXmlPOST("invoke-rpc-module:rpc-test", stringInputStream("""
-            <input xmlns="invoke:rpc:module">
-              <cont>
-                <lf>test</lf>
-              </cont>
-            </input>"""), uriInfo, asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(200, response.getStatus());
-        final var payload = assertInstanceOf(NormalizedNodePayload.class, response.getEntity());
-        assertEquals(OUTPUT, payload.data());
+        assertEquals(OUTPUT, assertNormalizedNode(200, ar -> restconf.operationsXmlPOST("invoke-rpc-module:rpc-test",
+            stringInputStream("""
+                <input xmlns="invoke:rpc:module">
+                  <cont>
+                    <lf>test</lf>
+                  </cont>
+                </input>"""), uriInfo, ar)));
     }
 
     @Test
@@ -123,18 +112,15 @@ class RestconfOperationsPostTest extends AbstractRestconfTest {
                 "No implementation of RPC " + RPC + " available.");
         doReturn(Futures.immediateFailedFuture(exception)).when(rpcService).invokeRpc(RPC, INPUT);
 
-        doReturn(true).when(asyncResponse).resume(exceptionCaptor.capture());
-        restconf.operationsJsonPOST("invoke-rpc-module:rpc-test", stringInputStream("""
-            {
-              "invoke-rpc-module:input" : {
-                "cont" : {
-                  "lf" : "test"
-                }
-              }
-            }"""), uriInfo, asyncResponse);
-        final var errors = exceptionCaptor.getValue().getErrors();
-        assertEquals(1, errors.size());
-        final var error = errors.get(0);
+        final var error = assertError(ar -> restconf.operationsJsonPOST("invoke-rpc-module:rpc-test",
+            stringInputStream("""
+                {
+                  "invoke-rpc-module:input" : {
+                    "cont" : {
+                      "lf" : "test"
+                    }
+                  }
+                }"""), uriInfo, ar));
         assertEquals("No implementation of RPC (invoke:rpc:module?revision=2013-12-03)rpc-test available.",
             error.getErrorMessage());
         assertEquals(ErrorType.RPC, error.getErrorType());
@@ -153,8 +139,9 @@ class RestconfOperationsPostTest extends AbstractRestconfTest {
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(OUTPUT, List.of()))).when(rpcService)
             .invokeRpc(RPC, INPUT);
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.operationsJsonPOST("ietf-yang-library:modules-state/yang-ext:mount/invoke-rpc-module:rpc-test",
+        assertEquals(OUTPUT, assertNormalizedNode(200,
+            ar -> restconf.operationsJsonPOST(
+                "ietf-yang-library:modules-state/yang-ext:mount/invoke-rpc-module:rpc-test",
             stringInputStream("""
                 {
                   "invoke-rpc-module:input" : {
@@ -162,11 +149,7 @@ class RestconfOperationsPostTest extends AbstractRestconfTest {
                       "lf" : "test"
                     }
                   }
-                }"""), uriInfo, asyncResponse);
-        final var response = responseCaptor.getValue();
-        assertEquals(200, response.getStatus());
-        final var payload = assertInstanceOf(NormalizedNodePayload.class, response.getEntity());
-        assertEquals(OUTPUT, payload.data());
+                }"""), uriInfo, ar)));
     }
 
     @Test
@@ -179,16 +162,14 @@ class RestconfOperationsPostTest extends AbstractRestconfTest {
         doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(YangInstanceIdentifier.of(
             QName.create("urn:ietf:params:xml:ns:yang:ietf-yang-library", "2016-06-21", "modules-state")));
 
-        doReturn(true).when(asyncResponse).resume(exceptionCaptor.capture());
-        restconf.operationsJsonPOST("ietf-yang-library:modules-state/yang-ext:mount/invoke-rpc-module:rpc-test",
-            stringInputStream("""
-                {
-                  "invoke-rpc-module:input" : {
-                  }
-                }"""), uriInfo, asyncResponse);
-        final var errors = exceptionCaptor.getValue().getErrors();
-        assertEquals(1, errors.size());
-        final var error = errors.get(0);
+        final var error = assertError(
+            ar -> restconf.operationsJsonPOST(
+                "ietf-yang-library:modules-state/yang-ext:mount/invoke-rpc-module:rpc-test",
+                stringInputStream("""
+                    {
+                      "invoke-rpc-module:input" : {
+                      }
+                    }"""), uriInfo, ar));
         assertEquals("RPC invocation is not available", error.getErrorMessage());
         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
         assertEquals(ErrorTag.OPERATION_NOT_SUPPORTED, error.getErrorTag());
@@ -199,17 +180,15 @@ class RestconfOperationsPostTest extends AbstractRestconfTest {
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(OUTPUT, List.of())))
             .when(rpcService).invokeRpc(RPC, INPUT);
 
-        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
-        restconf.operationsJsonPOST("invoke-rpc-module:rpc-test", stringInputStream("""
-            {
-              "invoke-rpc-module:input" : {
-                "cont" : {
-                  "lf" : "test"
-                }
-              }
-            }"""), uriInfo, asyncResponse);
-        final var payload = assertInstanceOf(NormalizedNodePayload.class, responseCaptor.getValue().getEntity());
-        assertEquals(OUTPUT, payload.data());
+        assertEquals(OUTPUT, assertNormalizedNode(200, ar -> restconf.operationsJsonPOST("invoke-rpc-module:rpc-test",
+            stringInputStream("""
+                {
+                  "invoke-rpc-module:input" : {
+                    "cont" : {
+                      "lf" : "test"
+                    }
+                  }
+                }"""), uriInfo, ar)));
     }
 
     private void prepNNC(final ContainerNode result) {
index 1a6025bf44ccf54bf865c125e1a59d0097dd57af..3d2f36340efca969012cc58276b2b128edb53b0c 100644 (file)
@@ -8,38 +8,26 @@
 package org.opendaylight.restconf.nb.jaxrs;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
-import org.opendaylight.mdsal.dom.api.DOMActionService;
-import org.opendaylight.mdsal.dom.api.DOMDataBroker;
-import org.opendaylight.mdsal.dom.api.DOMMountPointService;
-import org.opendaylight.mdsal.dom.api.DOMRpcService;
-import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
-import org.opendaylight.restconf.server.mdsal.MdsalRestconfServer;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev170126.Restconf;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
 @ExtendWith(MockitoExtension.class)
-class RestconfYangLibraryVersionGetTest {
-    @Mock
-    private DOMDataBroker dataBroker;
-    @Mock
-    private DOMRpcService rpcService;
-    @Mock
-    private DOMActionService actionService;
-    @Mock
-    private DOMMountPointService mountPointService;
+class RestconfYangLibraryVersionGetTest extends AbstractRestconfTest {
+    @Override
+    EffectiveModelContext modelContext() {
+        return YangParserTestUtils.parseYangResourceDirectory("/restconf/impl");
+    }
 
     @Test
     void testLibraryVersion() {
-        final var context = DatabindContext.ofModel(YangParserTestUtils.parseYangResourceDirectory("/restconf/impl"));
-        final var restconfImpl = new JaxRsRestconf(new MdsalRestconfServer(() -> context, dataBroker, rpcService,
-            actionService, mountPointService));
-        final var libraryVersion = assertInstanceOf(LeafNode.class, restconfImpl.yangLibraryVersionGET().data());
-        assertEquals("2019-01-04", libraryVersion.body());
+        assertEquals(ImmutableNodes.leafNode(QName.create(Restconf.QNAME, "yang-library-version"), "2019-01-04"),
+            assertNormalizedNode(200, ar -> restconf.yangLibraryVersionGET(ar)));
     }
 }