Prevent NPE for Credentials
[netconf.git] / plugins / netconf-client-mdsal / src / test / java / org / opendaylight / netconf / client / mdsal / impl / NetconfBaseOpsTest.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.netconf.client.mdsal.impl;
9
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertThrows;
12 import static org.junit.jupiter.api.Assertions.assertTrue;
13 import static org.mockito.ArgumentMatchers.any;
14 import static org.mockito.ArgumentMatchers.argThat;
15 import static org.mockito.Mockito.verify;
16 import static org.mockito.Mockito.when;
17
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.net.InetSocketAddress;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Optional;
24 import java.util.Set;
25 import java.util.concurrent.ExecutionException;
26 import org.custommonkey.xmlunit.Diff;
27 import org.custommonkey.xmlunit.XMLUnit;
28 import org.junit.jupiter.api.BeforeEach;
29 import org.junit.jupiter.api.Test;
30 import org.junit.jupiter.api.extension.ExtendWith;
31 import org.mockito.ArgumentMatcher;
32 import org.mockito.Mock;
33 import org.mockito.junit.jupiter.MockitoExtension;
34 import org.opendaylight.netconf.api.EffectiveOperation;
35 import org.opendaylight.netconf.api.messages.NetconfMessage;
36 import org.opendaylight.netconf.api.xml.XmlUtil;
37 import org.opendaylight.netconf.client.mdsal.AbstractTestModelTest;
38 import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
39 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceCommunicator;
40 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
41 import org.opendaylight.netconf.client.mdsal.spi.NetconfDeviceRpc;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.get.config.output.Data;
43 import org.opendaylight.yangtools.yang.common.QName;
44 import org.opendaylight.yangtools.yang.common.QNameModule;
45 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.schema.MountPointContext;
49 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
50 import org.w3c.dom.Document;
51 import org.w3c.dom.Element;
52 import org.w3c.dom.NamedNodeMap;
53 import org.xml.sax.SAXException;
54
55 @ExtendWith(MockitoExtension.class)
56 class NetconfBaseOpsTest extends AbstractTestModelTest {
57     private static final QNameModule TEST_MODULE = QNameModule.ofRevision("test:namespace", "2013-07-22");
58
59     private static final QName CONTAINER_C_QNAME = QName.create(TEST_MODULE, "c");
60     private static final NodeIdentifier CONTAINER_C_NID = NodeIdentifier.create(CONTAINER_C_QNAME);
61     private static final QName LEAF_A_QNAME = QName.create(TEST_MODULE, "a");
62     private static final NodeIdentifier LEAF_A_NID = NodeIdentifier.create(LEAF_A_QNAME);
63     private static final QName LEAF_B_QNAME = QName.create(TEST_MODULE, "b");
64     private static final NodeIdentifier LEAF_B_NID = NodeIdentifier.create(LEAF_B_QNAME);
65     private static final QName CONTAINER_D_QNAME = QName.create(TEST_MODULE, "d");
66     private static final NodeIdentifier CONTAINER_D_NID = NodeIdentifier.create(CONTAINER_D_QNAME);
67     private static final QName LEAF_X_QNAME = QName.create(TEST_MODULE, "x");
68     private static final NodeIdentifier LEAF_X_NID = NodeIdentifier.create(LEAF_X_QNAME);
69
70     private static final QName CONTAINER_E_QNAME = QName.create(TEST_MODULE, "e");
71     private static final NodeIdentifier CONTAINER_E_NID = NodeIdentifier.create(CONTAINER_E_QNAME);
72     private static final QName LEAF_Z_QNAME = QName.create(TEST_MODULE, "z");
73     private static final NodeIdentifier LEAF_Z_NID = NodeIdentifier.create(LEAF_Z_QNAME);
74     private static final NetconfMessage NETCONF_DATA_MESSAGE;
75
76     static {
77         final var dataStream = NetconfBaseOpsTest.class.getResourceAsStream("/netconfMessages/rpc-reply_get.xml");
78         try {
79             NETCONF_DATA_MESSAGE = new NetconfMessage(XmlUtil.readXmlToDocument(dataStream));
80         } catch (SAXException | IOException e) {
81             throw new ExceptionInInitializerError(e);
82         }
83         XMLUnit.setIgnoreWhitespace(true);
84         XMLUnit.setIgnoreComments(true);
85     }
86
87     @Mock
88     private RemoteDeviceCommunicator listener;
89     private NetconfRpcFutureCallback callback;
90     private NetconfBaseOps baseOps;
91
92     @BeforeEach
93     void setUp() {
94         final var rpc = new NetconfDeviceRpc(SCHEMA_CONTEXT, listener, new NetconfMessageTransformer(
95             MountPointContext.of(SCHEMA_CONTEXT), true,
96             BASE_SCHEMAS.baseSchemaForCapabilities(NetconfSessionPreferences.fromStrings(Set.of()))));
97         callback = new NetconfRpcFutureCallback("prefix",
98             new RemoteDeviceId("device-1", InetSocketAddress.createUnresolved("localhost", 17830)));
99         baseOps = new NetconfBaseOps(rpc, MountPointContext.of(SCHEMA_CONTEXT));
100     }
101
102     @Test
103     void testLock() {
104         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
105         baseOps.lock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID);
106         verifyMessageSent("lock");
107     }
108
109     @Test
110     void testLockCandidate() {
111         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
112         baseOps.lockCandidate(callback);
113         verifyMessageSent("lock");
114     }
115
116     @Test
117     void testUnlock() {
118         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
119         baseOps.unlock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID);
120         verifyMessageSent("unlock");
121     }
122
123     @Test
124     void testUnlockCandidate() {
125         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
126         baseOps.unlockCandidate(callback);
127         verifyMessageSent("unlock");
128     }
129
130     @Test
131     void testLockRunning() {
132         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
133         baseOps.lockRunning(callback);
134         verifyMessageSent("lock-running");
135     }
136
137     @Test
138     void testUnlockRunning() {
139         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
140         baseOps.unlockRunning(callback);
141         verifyMessageSent("unlock-running");
142     }
143
144     @Test
145     void testDiscardChanges() {
146         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
147         baseOps.discardChanges(callback);
148         verifyMessageSent("discardChanges");
149     }
150
151     @Test
152     void testCommit() {
153         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
154         baseOps.commit(callback);
155         verifyMessageSent("commit");
156     }
157
158     @Test
159     void testValidateCandidate() {
160         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
161         baseOps.validateCandidate(callback);
162         verifyMessageSent("validate");
163     }
164
165     @Test
166     void testValidateRunning() {
167         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
168         baseOps.validateRunning(callback);
169         verifyMessageSent("validate-running");
170     }
171
172
173     @Test
174     void testCopyConfig() {
175         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
176         baseOps.copyConfig(callback, NetconfMessageTransformUtil.NETCONF_RUNNING_NODEID,
177                 NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID);
178         verifyMessageSent("copy-config");
179     }
180
181     @Test
182     void testCopyRunningToCandidate() {
183         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
184         baseOps.copyRunningToCandidate(callback);
185         verifyMessageSent("copy-config");
186     }
187
188     @Test
189     void testGetConfigRunningData() throws Exception {
190         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
191         final var dataOpt = baseOps.getConfigRunningData(callback, Optional.of(YangInstanceIdentifier.of())).get();
192         assertTrue(dataOpt.isPresent());
193         assertEquals(Data.QNAME, dataOpt.orElseThrow().name().getNodeType());
194     }
195
196     @Test
197     void testGetData() throws Exception {
198         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
199         final var dataOpt = baseOps.getData(callback, Optional.of(YangInstanceIdentifier.of())).get();
200         assertTrue(dataOpt.isPresent());
201         assertEquals(Data.QNAME, dataOpt.orElseThrow().name().getNodeType());
202     }
203
204     @Test
205     void testGetConfigRunning() {
206         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
207         baseOps.getConfigRunning(callback, Optional.empty());
208         verifyMessageSent("getConfig");
209     }
210
211     @Test
212     void testGetConfigCandidate() {
213         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
214         baseOps.getConfigCandidate(callback, Optional.empty());
215         verifyMessageSent("getConfig_candidate");
216     }
217
218     @Test
219     void testGetConfigCandidateWithFilter() {
220         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
221         baseOps.getConfigCandidate(callback, Optional.of(YangInstanceIdentifier.of(CONTAINER_C_QNAME)));
222         verifyMessageSent("getConfig_candidate-filter");
223     }
224
225     @Test
226     void testGet() {
227         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
228         baseOps.get(callback, Optional.empty());
229         verifyMessageSent("get");
230     }
231
232     @Test
233     void testEditConfigCandidate() {
234         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
235         baseOps.editConfigCandidate(callback, baseOps.createEditConfigStructure(
236             Optional.of(ImmutableNodes.leafNode(LEAF_A_NID, "leaf-value")),
237             Optional.of(EffectiveOperation.REPLACE), YangInstanceIdentifier.builder()
238             .node(CONTAINER_C_QNAME)
239             .node(LEAF_A_NID)
240             .build()), true);
241         verifyMessageSent("edit-config-test-module");
242     }
243
244     @Test
245     void testDeleteContainerNodeCandidate() {
246         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
247         baseOps.editConfigCandidate(callback, baseOps.createEditConfigStructure(Optional.empty(),
248             Optional.of(EffectiveOperation.DELETE), YangInstanceIdentifier.of(CONTAINER_C_QNAME)), true);
249         verifyMessageSent("edit-config-delete-container-node-candidate");
250     }
251
252     @Test
253     void testDeleteLeafNodeCandidate() {
254         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
255         baseOps.editConfigCandidate(callback, baseOps.createEditConfigStructure(Optional.empty(),
256             Optional.of(EffectiveOperation.DELETE),
257             YangInstanceIdentifier.builder().node(CONTAINER_C_QNAME).node(LEAF_A_NID).build()), true);
258         verifyMessageSent("edit-config-delete-leaf-node-candidate");
259     }
260
261     @Test
262     void testEditConfigRunning() {
263         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
264         baseOps.editConfigRunning(callback, baseOps.createEditConfigStructure(
265             Optional.of(ImmutableNodes.leafNode(LEAF_A_NID, "leaf-value")),
266             Optional.of(EffectiveOperation.REPLACE),
267             YangInstanceIdentifier.builder().node(CONTAINER_C_NID).node(LEAF_A_NID).build()),
268             EffectiveOperation.MERGE, true);
269         verifyMessageSent("edit-config-test-module-running");
270     }
271
272     @Test
273     void testGetWithFields() throws Exception {
274         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
275         final YangInstanceIdentifier path = YangInstanceIdentifier.of(CONTAINER_C_NID);
276         final YangInstanceIdentifier leafAField = YangInstanceIdentifier.of(LEAF_A_NID);
277         final YangInstanceIdentifier leafBField = YangInstanceIdentifier.of(LEAF_B_NID);
278
279         baseOps.getData(callback, Optional.of(path), List.of(leafAField, leafBField)).get();
280         verify(listener).sendRequest(msg("/netconfMessages/get-fields-request.xml"));
281     }
282
283     @Test
284     void testGetConfigWithFields() throws Exception {
285         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
286         final YangInstanceIdentifier path = YangInstanceIdentifier.of(CONTAINER_C_NID);
287         final YangInstanceIdentifier leafAField = YangInstanceIdentifier.of(LEAF_A_NID);
288         final YangInstanceIdentifier leafBField = YangInstanceIdentifier.of(LEAF_B_NID);
289
290         baseOps.getConfigRunningData(callback, Optional.of(path), List.of(leafAField, leafBField)).get();
291         verify(listener).sendRequest(msg("/netconfMessages/get-config-fields-request.xml"));
292     }
293
294     @Test
295     void testGetDataWithoutFields() {
296         assertThrows(ExecutionException.class, () -> baseOps.getData(callback,
297                 Optional.of(YangInstanceIdentifier.of()), List.of()).get());
298     }
299
300     @Test
301     void getConfigRunningDataWithoutFields() {
302         assertThrows(ExecutionException.class, () -> baseOps.getConfigRunningData(callback,
303                 Optional.of(YangInstanceIdentifier.of()), List.of()).get());
304     }
305
306     @Test
307     void testGetWithFieldsAndEmptyParentPath() throws Exception {
308         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
309         final YangInstanceIdentifier leafAField = YangInstanceIdentifier.of(CONTAINER_C_NID, LEAF_A_NID);
310         final YangInstanceIdentifier leafXField = YangInstanceIdentifier.of(
311                 CONTAINER_C_NID, CONTAINER_D_NID, LEAF_X_NID);
312         final YangInstanceIdentifier leafZField = YangInstanceIdentifier.of(CONTAINER_E_NID, LEAF_Z_NID);
313
314         baseOps.getData(callback, Optional.of(YangInstanceIdentifier.of()),
315                 List.of(leafAField, leafXField, leafZField)).get();
316         verify(listener).sendRequest(msg("/netconfMessages/get-with-multiple-subtrees.xml"));
317     }
318
319     @Test
320     void testGetConfigWithFieldsAndEmptyParentPath() throws Exception {
321         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
322         final YangInstanceIdentifier leafAField = YangInstanceIdentifier.of(CONTAINER_C_NID, LEAF_A_NID);
323         final YangInstanceIdentifier leafXField = YangInstanceIdentifier.of(
324                 CONTAINER_C_NID, CONTAINER_D_NID, LEAF_X_NID);
325         final YangInstanceIdentifier leafZField = YangInstanceIdentifier.of(CONTAINER_E_NID, LEAF_Z_NID);
326
327         baseOps.getConfigRunningData(callback, Optional.of(YangInstanceIdentifier.of()),
328                 List.of(leafAField, leafXField, leafZField)).get();
329         verify(listener).sendRequest(msg("/netconfMessages/get-config-with-multiple-subtrees.xml"));
330     }
331
332     @Test
333     void testGetWithRootFieldsAndEmptyParentPath() throws Exception {
334         when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
335         final YangInstanceIdentifier contCField = YangInstanceIdentifier.of(CONTAINER_C_NID);
336         final YangInstanceIdentifier contDField = YangInstanceIdentifier.of(CONTAINER_E_NID);
337
338         baseOps.getData(callback, Optional.of(YangInstanceIdentifier.of()), List.of(contCField, contDField)).get();
339         verify(listener).sendRequest(msg("/netconfMessages/get-with-multiple-root-subtrees.xml"));
340     }
341
342     private void verifyMessageSent(final String fileName) {
343         final String path = "/netconfMessages/" + fileName + ".xml";
344         verify(listener).sendRequest(msg(path));
345     }
346
347     private static NetconfMessage msg(final String name) {
348         final InputStream stream = NetconfBaseOpsTest.class.getResourceAsStream(name);
349         try {
350             return argThat(new NetconfMessageMatcher(XmlUtil.readXmlToDocument(stream)));
351         } catch (SAXException | IOException e) {
352             throw new IllegalStateException("Failed to read xml file " + name, e);
353         }
354     }
355
356     private static class NetconfMessageMatcher implements ArgumentMatcher<NetconfMessage> {
357
358         private final Document expected;
359
360         NetconfMessageMatcher(final Document expected) {
361             this.expected = removeAttrs(expected);
362         }
363
364         @Override
365         public boolean matches(final NetconfMessage message) {
366             final Document actualDoc = removeAttrs(message.getDocument());
367             actualDoc.normalizeDocument();
368             expected.normalizeDocument();
369             final Diff diff = XMLUnit.compareXML(expected, actualDoc);
370             return diff.similar();
371         }
372
373         private static Document removeAttrs(final Document input) {
374             final Document copy = XmlUtil.newDocument();
375             copy.appendChild(copy.importNode(input.getDocumentElement(), true));
376             final Element element = copy.getDocumentElement();
377             final List<String> attrNames = new ArrayList<>();
378             final NamedNodeMap attributes = element.getAttributes();
379             for (int i = 0; i < attributes.getLength(); i++) {
380                 final String nodeName = attributes.item(i).getNodeName();
381                 if ("xmlns".equals(nodeName)) {
382                     continue;
383                 }
384                 attrNames.add(nodeName);
385             }
386             attrNames.forEach(element::removeAttribute);
387             return copy;
388         }
389     }
390 }