Codify operationsGET
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / rfc8040 / rests / services / impl / RestconfInvokeOperationsServiceImplTest.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
9
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
12 import static org.junit.jupiter.api.Assertions.assertSame;
13 import static org.junit.jupiter.api.Assertions.assertThrows;
14 import static org.mockito.ArgumentMatchers.any;
15 import static org.mockito.ArgumentMatchers.eq;
16 import static org.mockito.Mockito.doReturn;
17 import static org.mockito.Mockito.mock;
18 import static org.mockito.Mockito.verify;
19
20 import com.google.common.util.concurrent.Futures;
21 import java.io.ByteArrayInputStream;
22 import java.net.URI;
23 import java.nio.charset.StandardCharsets;
24 import java.util.List;
25 import java.util.Optional;
26 import java.util.concurrent.ExecutionException;
27 import javax.ws.rs.container.AsyncResponse;
28 import javax.ws.rs.core.Response;
29 import javax.ws.rs.core.UriInfo;
30 import org.junit.Before;
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 import org.mockito.ArgumentCaptor;
34 import org.mockito.Mock;
35 import org.mockito.junit.MockitoJUnitRunner;
36 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
37 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
38 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
39 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
40 import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
41 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
42 import org.opendaylight.mdsal.dom.api.DOMRpcService;
43 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
44 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
45 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
46 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
47 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
48 import org.opendaylight.restconf.server.spi.OperationInput;
49 import org.opendaylight.yangtools.yang.common.ErrorTag;
50 import org.opendaylight.yangtools.yang.common.ErrorType;
51 import org.opendaylight.yangtools.yang.common.QName;
52 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
53 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
54 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
55 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
56 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
57 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
58 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
59
60 @RunWith(MockitoJUnitRunner.StrictStubs.class)
61 public class RestconfInvokeOperationsServiceImplTest {
62     private static final URI RESTCONF_URI = URI.create("/restconf");
63     private static final QName RPC = QName.create("invoke:rpc:module", "2013-12-03", "rpc-test");
64     private static final ContainerNode INPUT = Builders.containerBuilder()
65         .withNodeIdentifier(new NodeIdentifier(QName.create(RPC, "input")))
66         .withChild(ImmutableNodes.leafNode(QName.create(RPC, "content"), "test"))
67         .build();
68     private static final ContainerNode OUTPUT = Builders.containerBuilder()
69         .withNodeIdentifier(new NodeIdentifier(QName.create(RPC, "output")))
70         .withChild(ImmutableNodes.leafNode(QName.create(RPC, "content"), "operation result"))
71         .build();
72     private static final DatabindContext CONTEXT =
73         DatabindContext.ofModel(YangParserTestUtils.parseYangResourceDirectory("/invoke-rpc"));
74     private static final OperationInput OPER_INPUT = new OperationInput(CONTEXT,
75             SchemaInferenceStack.of(CONTEXT.modelContext(), Absolute.of(RPC)).toInference(), INPUT);
76
77     @Mock
78     private DOMDataBroker dataBroker;
79     @Mock
80     private DOMRpcService rpcService;
81     @Mock
82     private DOMMountPoint mountPoint;
83     @Mock
84     private DOMMountPointService mountPointService;
85     @Mock
86     private DOMNotificationService notificationService;
87
88     private RestconfOperationsServiceImpl invokeOperationsService;
89     private MdsalRestconfServer server;
90
91     @Before
92     public void setup() {
93         server = new MdsalRestconfServer(() -> CONTEXT, dataBroker, rpcService, mountPointService);
94         invokeOperationsService = new RestconfOperationsServiceImpl(server);
95     }
96
97     @Test
98     public void testInvokeRpcWithNonEmptyOutput() {
99         final var result = mock(ContainerNode.class);
100         doReturn(false).when(result).isEmpty();
101
102         prepNNC(result);
103         final var ar = mock(AsyncResponse.class);
104         final var captor = ArgumentCaptor.forClass(Response.class);
105         invokeOperationsService.operationsPostXML("invoke-rpc-module:rpc-test", new ByteArrayInputStream("""
106             <input xmlns="invoke:rpc:module"/>
107             """.getBytes(StandardCharsets.UTF_8)), mock(UriInfo.class), ar);
108         verify(ar).resume(captor.capture());
109
110         final var response = captor.getValue();
111         assertEquals(200, response.getStatus());
112         final var entity = (NormalizedNodePayload) response.getEntity();
113         assertSame(result, entity.data());
114     }
115
116     @Test
117     public void testInvokeRpcWithEmptyOutput() {
118         final var result = mock(ContainerNode.class);
119         doReturn(true).when(result).isEmpty();
120
121         prepNNC(result);
122         final var ar = mock(AsyncResponse.class);
123         final var response = ArgumentCaptor.forClass(Response.class);
124         invokeOperationsService.operationsPostJSON("invoke-rpc-module:rpc-test", new ByteArrayInputStream("""
125             {
126               "invoke-rpc-module:input" : {
127               }
128             }
129             """.getBytes(StandardCharsets.UTF_8)), mock(UriInfo.class), ar);
130         verify(ar).resume(response.capture());
131
132         assertEquals(204, response.getValue().getStatus());
133     }
134
135     @Test
136     public void invokeRpcTest() throws Exception {
137         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(OUTPUT, List.of()))).when(rpcService)
138             .invokeRpc(RPC, INPUT);
139         assertEquals(OUTPUT,
140             Futures.getDone(
141                 server.getRestconfStrategy(CONTEXT.modelContext(), null).invokeRpc(RESTCONF_URI, RPC, OPER_INPUT))
142             .output());
143     }
144
145     @Test
146     public void invokeRpcErrorsAndCheckTestTest() throws Exception {
147         final var errorRpc = QName.create(RPC, "error-rpc");
148         final var exception = new DOMRpcImplementationNotAvailableException(
149                 "No implementation of RPC " + errorRpc + " available.");
150         doReturn(Futures.immediateFailedFuture(exception)).when(rpcService).invokeRpc(errorRpc, INPUT);
151         final var ex = assertInstanceOf(RestconfDocumentedException.class,
152             assertThrows(ExecutionException.class,
153                 () -> Futures.getDone(server.getRestconfStrategy(CONTEXT.modelContext(), null)
154                     .invokeRpc(RESTCONF_URI, errorRpc, OPER_INPUT))).getCause());
155         final var errorList = ex.getErrors();
156         assertEquals(1, errorList.size());
157         final var actual = errorList.iterator().next();
158         assertEquals("No implementation of RPC " + errorRpc + " available.", actual.getErrorMessage());
159         assertEquals(ErrorType.RPC, actual.getErrorType());
160         assertEquals(ErrorTag.OPERATION_FAILED, actual.getErrorTag());
161     }
162
163     @Test
164     public void invokeRpcViaMountPointTest() throws Exception {
165         doReturn(Optional.of(rpcService)).when(mountPoint).getService(DOMRpcService.class);
166         doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
167         doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
168         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(OUTPUT, List.of()))).when(rpcService)
169             .invokeRpc(RPC, INPUT);
170         assertEquals(OUTPUT,
171             Futures.getDone(
172                 server.getRestconfStrategy(CONTEXT.modelContext(), mountPoint).invokeRpc(RESTCONF_URI, RPC, OPER_INPUT))
173             .output());
174     }
175
176     @Test
177     public void invokeRpcMissingMountPointServiceTest() {
178         doReturn(Optional.empty()).when(mountPoint).getService(DOMRpcService.class);
179         doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
180         doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
181         final var strategy = server.getRestconfStrategy(CONTEXT.modelContext(), mountPoint);
182         final var ex = assertInstanceOf(RestconfDocumentedException.class,
183             assertThrows(ExecutionException.class,
184                 () -> Futures.getDone(strategy.invokeRpc(RESTCONF_URI, RPC, OPER_INPUT))).getCause());
185         final var errors = ex.getErrors();
186         assertEquals(1, errors.size());
187         final var error = errors.get(0);
188         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
189         assertEquals(ErrorTag.OPERATION_NOT_SUPPORTED, error.getErrorTag());
190         assertEquals("RPC invocation is not available", error.getErrorMessage());
191     }
192
193     @Test
194     public void checkResponseTest() throws Exception {
195         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(OUTPUT, List.of())))
196             .when(rpcService).invokeRpc(RPC, INPUT);
197         assertEquals(OUTPUT,
198             Futures.getDone(server.getRestconfStrategy(CONTEXT.modelContext(), null)
199                 .invokeRpc(RESTCONF_URI, RPC, OPER_INPUT))
200             .output());
201     }
202
203     private void prepNNC(final ContainerNode result) {
204         final var qname = QName.create("invoke:rpc:module", "2013-12-03", "rpc-test");
205         final var domRpcResult = mock(DOMRpcResult.class);
206         doReturn(Futures.immediateFuture(domRpcResult)).when(rpcService).invokeRpc(eq(qname), any(ContainerNode.class));
207         doReturn(result).when(domRpcResult).value();
208     }
209 }