/*
* Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
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.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import com.google.common.util.concurrent.Futures;
import java.util.List;
import java.util.Optional;
import javax.ws.rs.core.MultivaluedHashMap;
import org.junit.jupiter.api.BeforeEach;
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.DOMNotificationService;
import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
import org.opendaylight.mdsal.dom.api.DOMRpcResult;
import org.opendaylight.mdsal.dom.api.DOMRpcService;
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.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
@ExtendWith(MockitoExtension.class)
class RestconfOperationsPostTest extends AbstractRestconfTest {
private static final QName RPC = QName.create("invoke:rpc:module", "2013-12-03", "rpc-test");
private static final ContainerNode INPUT = ImmutableNodes.newContainerBuilder()
.withNodeIdentifier(new NodeIdentifier(QName.create(RPC, "input")))
.withChild(ImmutableNodes.newContainerBuilder()
.withNodeIdentifier(new NodeIdentifier(QName.create(RPC, "cont")))
.withChild(ImmutableNodes.leafNode(QName.create(RPC, "lf"), "test"))
.build())
.build();
private static final ContainerNode OUTPUT = ImmutableNodes.newContainerBuilder()
.withNodeIdentifier(new NodeIdentifier(QName.create(RPC, "output")))
.withChild(ImmutableNodes.newContainerBuilder()
.withNodeIdentifier(new NodeIdentifier(QName.create(RPC, "cont-out")))
.withChild(ImmutableNodes.leafNode(QName.create(RPC, "lf-out"), "operation result"))
.build())
.build();
private static final EffectiveModelContext MODEL_CONTEXT =
YangParserTestUtils.parseYangResourceDirectory("/invoke-rpc");
@Mock
private DOMNotificationService notificationService;
@Override
EffectiveModelContext modelContext() {
return MODEL_CONTEXT;
}
@BeforeEach
void setupUriInfo() {
doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
}
@Test
void testInvokeRpcWithNonEmptyOutput() {
final var result = mock(ContainerNode.class);
doReturn(false).when(result).isEmpty();
prepNNC(result);
assertSame(result, assertOperationOutput(200, ar -> restconf.operationsXmlPOST(
apiPath("invoke-rpc-module:rpc-test"), stringInputStream("""
"""), uriInfo, ar)));
}
@Test
void testInvokeRpcWithEmptyOutput() {
final var result = mock(ContainerNode.class);
doReturn(true).when(result).isEmpty();
prepNNC(result);
assertNull(assertEntity(204, ar -> restconf.operationsJsonPOST(apiPath("invoke-rpc-module:rpc-test"),
stringInputStream("""
{
"invoke-rpc-module:input" : {
}
}"""), uriInfo, ar)));
}
@Test
void invokeRpcTest() {
doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(OUTPUT, List.of()))).when(rpcService)
.invokeRpc(RPC, INPUT);
assertEquals(OUTPUT, assertOperationOutput(200, ar -> restconf.operationsXmlPOST(
apiPath("invoke-rpc-module:rpc-test"), stringInputStream("""
test
"""), uriInfo, ar)));
}
@Test
void invokeRpcErrorsAndCheckTestTest() throws Exception {
final var exception = new DOMRpcImplementationNotAvailableException(
"No implementation of RPC " + RPC + " available.");
doReturn(Futures.immediateFailedFuture(exception)).when(rpcService).invokeRpc(RPC, INPUT);
final var error = assertError(ar -> restconf.operationsJsonPOST(apiPath("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());
assertEquals(ErrorTag.OPERATION_FAILED, error.getErrorTag());
}
@Test
void invokeRpcViaMountPointTest() throws Exception {
doReturn(Optional.of(new FixedDOMSchemaService(MODEL_CONTEXT))).when(mountPoint)
.getService(DOMSchemaService.class);
doReturn(Optional.of(rpcService)).when(mountPoint).getService(DOMRpcService.class);
doReturn(Optional.empty()).when(mountPoint).getService(DOMActionService.class);
doReturn(Optional.empty()).when(mountPoint).getService(DOMMountPointService.class);
doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(YangInstanceIdentifier.of(
QName.create("urn:ietf:params:xml:ns:yang:ietf-yang-library", "2019-01-04", "modules-state")));
doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(OUTPUT, List.of()))).when(rpcService)
.invokeRpc(RPC, INPUT);
assertEquals(OUTPUT, assertOperationOutput(200,
ar -> restconf.operationsJsonPOST(
apiPath("ietf-yang-library:modules-state/yang-ext:mount/invoke-rpc-module:rpc-test"),
stringInputStream("""
{
"invoke-rpc-module:input" : {
"cont" : {
"lf" : "test"
}
}
}"""), uriInfo, ar)));
}
@Test
void invokeRpcMissingMountPointServiceTest() {
doReturn(Optional.of(new FixedDOMSchemaService(MODEL_CONTEXT))).when(mountPoint)
.getService(DOMSchemaService.class);
doReturn(Optional.empty()).when(mountPoint).getService(DOMRpcService.class);
doReturn(Optional.empty()).when(mountPoint).getService(DOMActionService.class);
doReturn(Optional.empty()).when(mountPoint).getService(DOMMountPointService.class);
doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(YangInstanceIdentifier.of(
QName.create("urn:ietf:params:xml:ns:yang:ietf-yang-library", "2019-01-04", "modules-state")));
final var error = assertError(
ar -> restconf.operationsJsonPOST(
apiPath("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());
}
@Test
void checkResponseTest() throws Exception {
doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(OUTPUT, List.of())))
.when(rpcService).invokeRpc(RPC, INPUT);
final var body = assertOperationOutputBody(200, ar -> restconf.operationsJsonPOST(
apiPath("invoke-rpc-module:rpc-test"),
stringInputStream("""
{
"invoke-rpc-module:input" : {
"cont" : {
"lf" : "test"
}
}
}"""), uriInfo, ar));
assertEquals(OUTPUT, body.data());
assertJson("""
{"invoke-rpc-module:output":{"cont-out":{"lf-out":"operation result"}}}""", body);
assertXml("""
""", body);
}
private void prepNNC(final ContainerNode result) {
final var qname = QName.create("invoke:rpc:module", "2013-12-03", "rpc-test");
final var domRpcResult = mock(DOMRpcResult.class);
doReturn(Futures.immediateFuture(domRpcResult)).when(rpcService).invokeRpc(eq(qname), any(ContainerNode.class));
doReturn(result).when(domRpcResult).value();
}
}