c6c5a1eb565586024c9b3386c7994b787f0d4cde
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / rfc8040 / rests / transactions / AbstractRestconfStrategyTest.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech, s.r.o. 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.transactions;
9
10 import static org.hamcrest.CoreMatchers.containsString;
11 import static org.hamcrest.MatcherAssert.assertThat;
12 import static org.junit.jupiter.api.Assertions.assertEquals;
13 import static org.junit.jupiter.api.Assertions.assertFalse;
14 import static org.junit.jupiter.api.Assertions.assertNull;
15 import static org.junit.jupiter.api.Assertions.assertTrue;
16 import static org.mockito.Mockito.verify;
17
18 import com.google.common.collect.ImmutableList;
19 import java.util.List;
20 import javax.ws.rs.core.UriInfo;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.junit.jupiter.api.BeforeEach;
24 import org.junit.jupiter.api.Test;
25 import org.mockito.ArgumentCaptor;
26 import org.mockito.Captor;
27 import org.mockito.Mock;
28 import org.opendaylight.restconf.api.ApiPath;
29 import org.opendaylight.restconf.api.query.ContentParam;
30 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
31 import org.opendaylight.restconf.common.patch.PatchContext;
32 import org.opendaylight.restconf.common.patch.PatchEntity;
33 import org.opendaylight.restconf.nb.rfc8040.AbstractJukeboxTest;
34 import org.opendaylight.restconf.server.api.AbstractServerRequest;
35 import org.opendaylight.restconf.server.api.DataPatchResult;
36 import org.opendaylight.restconf.server.api.DataPostResult;
37 import org.opendaylight.restconf.server.api.DataPutResult;
38 import org.opendaylight.restconf.server.api.DataYangPatchResult;
39 import org.opendaylight.restconf.server.api.DatabindContext;
40 import org.opendaylight.restconf.server.api.PatchStatusContext;
41 import org.opendaylight.restconf.server.api.PatchStatusEntity;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.patch.rev170222.yang.patch.yang.patch.Edit.Operation;
43 import org.opendaylight.yangtools.yang.common.Empty;
44 import org.opendaylight.yangtools.yang.common.ErrorTag;
45 import org.opendaylight.yangtools.yang.common.ErrorType;
46 import org.opendaylight.yangtools.yang.common.QName;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
49 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
50 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
55 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
57 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
58 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
59 import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode;
60 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
61 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
62 import org.w3c.dom.DOMException;
63
64 abstract class AbstractRestconfStrategyTest extends AbstractJukeboxTest {
65     static final ContainerNode JUKEBOX_WITH_BANDS = ImmutableNodes.newContainerBuilder()
66         .withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME))
67         .withChild(ImmutableNodes.newSystemMapBuilder()
68             .withNodeIdentifier(new NodeIdentifier(PLAYLIST_QNAME))
69             .withChild(BAND_ENTRY)
70             .withChild(ImmutableNodes.newMapEntryBuilder()
71                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(PLAYLIST_QNAME, NAME_QNAME, "name of band 2"))
72                 .withChild(ImmutableNodes.leafNode(NAME_QNAME, "name of band 2"))
73                 .withChild(ImmutableNodes.leafNode(DESCRIPTION_QNAME, "band description 2"))
74                 .build())
75             .build())
76         .build();
77     static final ContainerNode JUKEBOX_WITH_PLAYLIST = ImmutableNodes.newContainerBuilder()
78         .withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME))
79         .withChild(ImmutableNodes.newSystemMapBuilder()
80             .withNodeIdentifier(new NodeIdentifier(PLAYLIST_QNAME))
81             .withChild(ImmutableNodes.newMapEntryBuilder()
82                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(PLAYLIST_QNAME, NAME_QNAME, "MyFavoriteBand-A"))
83                 .withChild(ImmutableNodes.leafNode(NAME_QNAME, "MyFavoriteBand-A"))
84                 .withChild(ImmutableNodes.leafNode(DESCRIPTION_QNAME, "band description A"))
85                 .build())
86             .withChild(ImmutableNodes.newMapEntryBuilder()
87                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(PLAYLIST_QNAME, NAME_QNAME, "MyFavoriteBand-B"))
88                 .withChild(ImmutableNodes.leafNode(NAME_QNAME, "MyFavoriteBand-B"))
89                 .withChild(ImmutableNodes.leafNode(DESCRIPTION_QNAME, "band description B"))
90                 .build())
91             .build())
92         .build();
93     static final MapNode PLAYLIST = ImmutableNodes.newSystemMapBuilder()
94         .withNodeIdentifier(new NodeIdentifier(PLAYLIST_QNAME))
95         .withChild(BAND_ENTRY)
96         .build();
97     // instance identifier for accessing container node "player"
98     static final YangInstanceIdentifier PLAYER_IID = YangInstanceIdentifier.of(JUKEBOX_QNAME, PLAYER_QNAME);
99     static final YangInstanceIdentifier ARTIST_IID = YangInstanceIdentifier.builder()
100         .node(JUKEBOX_QNAME)
101         .node(LIBRARY_QNAME)
102         .node(ARTIST_QNAME)
103         .nodeWithKey(ARTIST_QNAME, NAME_QNAME, "name of artist")
104         .build();
105     // FIXME: this looks weird
106     static final YangInstanceIdentifier CREATE_AND_DELETE_TARGET = GAP_IID.node(PLAYER_QNAME).node(GAP_QNAME);
107
108     // Read mock data
109     static final QName BASE = QName.create("ns", "2016-02-28", "base");
110     private static final QName LIST_KEY_QNAME = QName.create(BASE, "list-key");
111     private static final QName LEAF_LIST_QNAME = QName.create(BASE, "leaf-list");
112     private static final QName LIST_QNAME = QName.create(BASE, "list");
113     static final QName CONT_QNAME = QName.create(BASE, "cont");
114
115     private static final NodeIdentifierWithPredicates NODE_WITH_KEY =
116         NodeIdentifierWithPredicates.of(LIST_QNAME, LIST_KEY_QNAME, "keyValue");
117     private static final NodeIdentifierWithPredicates NODE_WITH_KEY_2 =
118         NodeIdentifierWithPredicates.of(LIST_QNAME, LIST_KEY_QNAME, "keyValue2");
119
120     private static final LeafNode<?> CONTENT = ImmutableNodes.leafNode(QName.create(BASE, "leaf-content"), "content");
121     private static final LeafNode<?> CONTENT_2 =
122         ImmutableNodes.leafNode(QName.create(BASE, "leaf-content-different"), "content-different");
123     static final YangInstanceIdentifier PATH = YangInstanceIdentifier.builder()
124         .node(CONT_QNAME)
125         .node(LIST_QNAME)
126         .node(NODE_WITH_KEY)
127         .build();
128     static final YangInstanceIdentifier PATH_2 = YangInstanceIdentifier.builder()
129         .node(CONT_QNAME)
130         .node(LIST_QNAME)
131         .node(NODE_WITH_KEY_2)
132         .build();
133     static final YangInstanceIdentifier PATH_3 = YangInstanceIdentifier.of(CONT_QNAME, LIST_QNAME);
134     private static final MapEntryNode DATA = ImmutableNodes.newMapEntryBuilder()
135         .withNodeIdentifier(NODE_WITH_KEY)
136         .withChild(CONTENT)
137         .build();
138     static final MapEntryNode DATA_2 = ImmutableNodes.newMapEntryBuilder()
139         .withNodeIdentifier(NODE_WITH_KEY)
140         .withChild(CONTENT_2)
141         .build();
142     private static final LeafNode<?> CONTENT_LEAF = ImmutableNodes.leafNode(QName.create(BASE, "content"), "test");
143     private static final LeafNode<?> CONTENT_LEAF_2 = ImmutableNodes.leafNode(QName.create(BASE, "content2"), "test2");
144     static final ContainerNode DATA_3 = ImmutableNodes.newContainerBuilder()
145         .withNodeIdentifier(new NodeIdentifier(QName.create(BASE, "container")))
146         .withChild(CONTENT_LEAF)
147         .build();
148     static final ContainerNode DATA_4 = ImmutableNodes.newContainerBuilder()
149         .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(BASE, "container2")))
150         .withChild(CONTENT_LEAF_2)
151         .build();
152     static final MapNode LIST_DATA = ImmutableNodes.newSystemMapBuilder()
153         .withNodeIdentifier(new NodeIdentifier(QName.create(LIST_QNAME, "list")))
154         .withChild(DATA)
155         .build();
156     static final MapNode LIST_DATA_2 = ImmutableNodes.newSystemMapBuilder()
157         .withNodeIdentifier(new NodeIdentifier(QName.create(LIST_QNAME, "list")))
158         .withChild(DATA)
159         .withChild(DATA_2)
160         .build();
161     static final UserMapNode ORDERED_MAP_NODE_1 = ImmutableNodes.newUserMapBuilder()
162         .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
163         .withChild(DATA)
164         .build();
165     static final UserMapNode ORDERED_MAP_NODE_2 = ImmutableNodes.newUserMapBuilder()
166         .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
167         .withChild(DATA)
168         .withChild(DATA_2)
169         .build();
170     private static final MapEntryNode CHECK_DATA = ImmutableNodes.newMapEntryBuilder()
171         .withNodeIdentifier(NODE_WITH_KEY)
172         .withChild(CONTENT_2)
173         .withChild(CONTENT)
174         .build();
175     static final LeafSetNode<String> LEAF_SET_NODE_1 = ImmutableNodes.<String>newSystemLeafSetBuilder()
176         .withNodeIdentifier(new NodeIdentifier(LEAF_LIST_QNAME))
177         .withChildValue("one")
178         .withChildValue("two")
179         .build();
180     static final LeafSetNode<String> LEAF_SET_NODE_2 = ImmutableNodes.<String>newSystemLeafSetBuilder()
181         .withNodeIdentifier(new NodeIdentifier(LEAF_LIST_QNAME))
182         .withChildValue("three")
183         .build();
184     static final LeafSetNode<String> ORDERED_LEAF_SET_NODE_1 = ImmutableNodes.<String>newUserLeafSetBuilder()
185         .withNodeIdentifier(new NodeIdentifier(LEAF_LIST_QNAME))
186         .withChildValue("one")
187         .withChildValue("two")
188         .build();
189     static final LeafSetNode<String> ORDERED_LEAF_SET_NODE_2 = ImmutableNodes.<String>newUserLeafSetBuilder()
190         .withNodeIdentifier(new NodeIdentifier(LEAF_LIST_QNAME))
191         .withChildValue("three")
192         .withChildValue("four")
193         .build();
194     static final YangInstanceIdentifier LEAF_SET_NODE_PATH = YangInstanceIdentifier.builder()
195         .node(CONT_QNAME)
196         .node(LEAF_LIST_QNAME)
197         .build();
198     private static final UnkeyedListEntryNode UNKEYED_LIST_ENTRY_NODE_1 = ImmutableNodes.newUnkeyedListEntryBuilder()
199         .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
200         .withChild(CONTENT)
201         .build();
202     private static final UnkeyedListEntryNode UNKEYED_LIST_ENTRY_NODE_2 = ImmutableNodes.newUnkeyedListEntryBuilder()
203         .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
204         .withChild(CONTENT_2)
205         .build();
206     static final UnkeyedListNode UNKEYED_LIST_NODE_1 = ImmutableNodes.newUnkeyedListBuilder()
207         .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
208         .withChild(UNKEYED_LIST_ENTRY_NODE_1)
209         .build();
210     static final UnkeyedListNode UNKEYED_LIST_NODE_2 = ImmutableNodes.newUnkeyedListBuilder()
211         .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(LIST_QNAME))
212         .withChild(UNKEYED_LIST_ENTRY_NODE_2)
213         .build();
214     private static final NodeIdentifier NODE_IDENTIFIER =
215         new NodeIdentifier(QName.create("ns", "2016-02-28", "container"));
216
217     @Mock
218     private EffectiveModelContext mockSchemaContext;
219     @Mock
220     private UriInfo uriInfo;
221     @Mock
222     private AbstractServerRequest<Empty> dataDeleteRequest;
223     @Mock
224     private AbstractServerRequest<DataPatchResult> dataPatchRequest;
225     @Captor
226     private ArgumentCaptor<DataPatchResult> dataPatchCaptor;
227     @Mock
228     private AbstractServerRequest<DataPostResult> dataPostRequest;
229     @Captor
230     private ArgumentCaptor<DataPostResult> dataPostCaptor;
231     @Mock
232     private AbstractServerRequest<DataYangPatchResult> dataYangPatchRequest;
233     @Captor
234     private ArgumentCaptor<DataYangPatchResult> dataYangPatchCaptor;
235     @Mock
236     AbstractServerRequest<DataPutResult> dataPutRequest;
237
238     private DatabindContext mockDatabind;
239
240     @BeforeEach
241     void initMockDatabind() {
242         mockDatabind = DatabindContext.ofModel(mockSchemaContext);
243     }
244
245     abstract @NonNull RestconfStrategy newStrategy(DatabindContext databind);
246
247     final @NonNull RestconfStrategy jukeboxStrategy() {
248         return newStrategy(JUKEBOX_DATABIND);
249     }
250
251     final @NonNull RestconfStrategy mockStrategy() {
252         return newStrategy(mockDatabind);
253     }
254
255     /**
256      * Test of successful DELETE operation.
257      */
258     @Test
259     final void testDeleteData() throws Exception {
260         testDeleteDataStrategy().dataDELETE(dataDeleteRequest, ApiPath.empty());
261         verify(dataDeleteRequest).completeWith(Empty.value());
262     }
263
264     abstract @NonNull RestconfStrategy testDeleteDataStrategy();
265
266     /**
267      * Negative test for DELETE operation when data to delete does not exist. Error DATA_MISSING is expected.
268      */
269     @Test
270     final void testNegativeDeleteData() {
271         testNegativeDeleteDataStrategy().dataDELETE(dataDeleteRequest, ApiPath.empty());
272
273         final var captor = ArgumentCaptor.forClass(RestconfDocumentedException.class);
274         verify(dataDeleteRequest).completeWith(captor.capture());
275
276         final var errors = captor.getValue().getErrors();
277         assertEquals(1, errors.size());
278         final var error = errors.get(0);
279         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
280         assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
281     }
282
283     abstract @NonNull RestconfStrategy testNegativeDeleteDataStrategy();
284
285     @Test
286     final void testPostContainerData() {
287         testPostContainerDataStrategy().postData(dataPostRequest, JUKEBOX_IID, EMPTY_JUKEBOX, null);
288     }
289
290     abstract @NonNull RestconfStrategy testPostContainerDataStrategy();
291
292     @Test
293     final void testPostListData() {
294         testPostListDataStrategy(BAND_ENTRY, PLAYLIST_IID.node(BAND_ENTRY.name()))
295             .postData(dataPostRequest, PLAYLIST_IID, PLAYLIST, null);
296     }
297
298     abstract @NonNull RestconfStrategy testPostListDataStrategy(MapEntryNode entryNode, YangInstanceIdentifier node);
299
300     @Test
301     final void testPostDataFail() {
302         final var domException = new DOMException((short) 414, "Post request failed");
303         testPostDataFailStrategy(domException).postData(dataPostRequest, JUKEBOX_IID, EMPTY_JUKEBOX, null);
304
305         final var captor = ArgumentCaptor.forClass(RestconfDocumentedException.class);
306         verify(dataPostRequest).completeWith(captor.capture());
307
308         final var errors = captor.getValue().getErrors();
309         assertEquals(1, errors.size());
310         assertThat(errors.get(0).getErrorInfo(), containsString(domException.getMessage()));
311     }
312
313     abstract @NonNull RestconfStrategy testPostDataFailStrategy(DOMException domException);
314
315     @Test
316     final void testPatchContainerData() {
317         testPatchContainerDataStrategy().merge(dataPatchRequest, JUKEBOX_IID, EMPTY_JUKEBOX);
318         verify(dataPatchRequest).completeWith(dataPatchCaptor.capture());
319     }
320
321     abstract @NonNull RestconfStrategy testPatchContainerDataStrategy();
322
323     @Test
324     final void testPatchLeafData() {
325         testPatchLeafDataStrategy().merge(dataPatchRequest, GAP_IID, GAP_LEAF);
326         verify(dataPatchRequest).completeWith(dataPatchCaptor.capture());
327     }
328
329     abstract @NonNull RestconfStrategy testPatchLeafDataStrategy();
330
331     @Test
332     final void testPatchListData() {
333         testPatchListDataStrategy().merge(dataPatchRequest, JUKEBOX_IID, JUKEBOX_WITH_PLAYLIST);
334         verify(dataPatchRequest).completeWith(dataPatchCaptor.capture());
335     }
336
337     abstract @NonNull RestconfStrategy testPatchListDataStrategy();
338
339     @Test
340     final void testPatchDataReplaceMergeAndRemove() {
341         final var buildArtistList = ImmutableNodes.newSystemMapBuilder()
342             .withNodeIdentifier(new NodeIdentifier(ARTIST_QNAME))
343             .withChild(ImmutableNodes.newMapEntryBuilder()
344                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(ARTIST_QNAME, NAME_QNAME, "name of artist"))
345                 .withChild(ImmutableNodes.leafNode(NAME_QNAME, "name of artist"))
346                 .withChild(ImmutableNodes.leafNode(DESCRIPTION_QNAME, "description of artist"))
347                 .build())
348             .build();
349
350         patch(new PatchContext("patchRMRm",
351             List.of(new PatchEntity("edit1", Operation.Replace, ARTIST_IID, buildArtistList),
352                 new PatchEntity("edit2", Operation.Merge, ARTIST_IID, buildArtistList),
353                 new PatchEntity("edit3", Operation.Remove, ARTIST_IID))),
354             testPatchDataReplaceMergeAndRemoveStrategy(), false);
355     }
356
357     abstract @NonNull RestconfStrategy testPatchDataReplaceMergeAndRemoveStrategy();
358
359     @Test
360     final void testPatchDataCreateAndDelete() {
361         patch(new PatchContext("patchCD", List.of(
362             new PatchEntity("edit1", Operation.Create, PLAYER_IID, EMPTY_JUKEBOX),
363             new PatchEntity("edit2", Operation.Delete, CREATE_AND_DELETE_TARGET))),
364             testPatchDataCreateAndDeleteStrategy(), true);
365     }
366
367     abstract @NonNull RestconfStrategy testPatchDataCreateAndDeleteStrategy();
368
369     @Test
370     final void testPatchMergePutContainer() {
371         patch(new PatchContext("patchM", List.of(new PatchEntity("edit1", Operation.Merge, PLAYER_IID, EMPTY_JUKEBOX))),
372             testPatchMergePutContainerStrategy(), false);
373     }
374
375     abstract @NonNull RestconfStrategy testPatchMergePutContainerStrategy();
376
377     @Test
378     final void testDeleteNonexistentData() {
379         deleteNonexistentDataTestStrategy().patchData(dataYangPatchRequest,
380             new PatchContext("patchD", List.of(new PatchEntity("edit", Operation.Delete, CREATE_AND_DELETE_TARGET))));
381
382         verify(dataYangPatchRequest).completeWith(dataYangPatchCaptor.capture());
383         final var status = dataYangPatchCaptor.getValue().status();
384         assertEquals("patchD", status.patchId());
385         assertFalse(status.ok());
386         final var edits = status.editCollection();
387         assertEquals(1, edits.size());
388         final var edit = edits.get(0);
389         assertEquals("edit", edit.getEditId());
390         assertTestDeleteNonexistentData(status, edit);
391     }
392
393     abstract @NonNull RestconfStrategy deleteNonexistentDataTestStrategy();
394
395     abstract void assertTestDeleteNonexistentData(@NonNull PatchStatusContext status, @NonNull PatchStatusEntity edit);
396
397     @Test
398     final void readDataConfigTest() {
399         assertEquals(DATA_3, readData(ContentParam.CONFIG, PATH, readDataConfigTestStrategy()));
400     }
401
402     abstract @NonNull RestconfStrategy readDataConfigTestStrategy();
403
404     @Test
405     final void readAllHavingOnlyConfigTest() {
406         assertEquals(DATA_3, readData(ContentParam.ALL, PATH, readAllHavingOnlyConfigTestStrategy()));
407     }
408
409     abstract @NonNull RestconfStrategy readAllHavingOnlyConfigTestStrategy();
410
411     @Test
412     final void readAllHavingOnlyNonConfigTest() {
413         assertEquals(DATA_2, readData(ContentParam.ALL, PATH_2, readAllHavingOnlyNonConfigTestStrategy()));
414     }
415
416     abstract @NonNull RestconfStrategy readAllHavingOnlyNonConfigTestStrategy();
417
418     @Test
419     final void readDataNonConfigTest() {
420         assertEquals(DATA_2, readData(ContentParam.NONCONFIG, PATH_2, readDataNonConfigTestStrategy()));
421     }
422
423     abstract @NonNull RestconfStrategy readDataNonConfigTestStrategy();
424
425     @Test
426     final void readContainerDataAllTest() {
427         assertEquals(ImmutableNodes.newContainerBuilder()
428             .withNodeIdentifier(NODE_IDENTIFIER)
429             .withChild(CONTENT_LEAF)
430             .withChild(CONTENT_LEAF_2)
431             .build(), readData(ContentParam.ALL, PATH, readContainerDataAllTestStrategy()));
432     }
433
434     abstract @NonNull RestconfStrategy readContainerDataAllTestStrategy();
435
436     @Test
437     final void readContainerDataConfigNoValueOfContentTest() {
438         assertEquals(ImmutableNodes.newContainerBuilder()
439             .withNodeIdentifier(NODE_IDENTIFIER)
440             .withChild(CONTENT_LEAF)
441             .withChild(CONTENT_LEAF_2)
442             .build(), readData(ContentParam.ALL, PATH, readContainerDataConfigNoValueOfContentTestStrategy()));
443     }
444
445     abstract @NonNull RestconfStrategy readContainerDataConfigNoValueOfContentTestStrategy();
446
447     @Test
448     final void readListDataAllTest() {
449         assertEquals(ImmutableNodes.newSystemMapBuilder()
450             .withNodeIdentifier(new NodeIdentifier(QName.create("ns", "2016-02-28", "list")))
451             .withChild(CHECK_DATA)
452             .build(), readData(ContentParam.ALL, PATH_3, readListDataAllTestStrategy()));
453     }
454
455     abstract @NonNull RestconfStrategy readListDataAllTestStrategy();
456
457     @Test
458     final void readOrderedListDataAllTest() {
459         assertEquals(ImmutableNodes.newUserMapBuilder()
460             .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
461             .withChild(CHECK_DATA)
462             .build(), readData(ContentParam.ALL, PATH_3, readOrderedListDataAllTestStrategy()));
463     }
464
465     abstract @NonNull RestconfStrategy readOrderedListDataAllTestStrategy();
466
467     @Test
468     void readUnkeyedListDataAllTest() {
469         assertEquals(ImmutableNodes.newUnkeyedListBuilder()
470             .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
471             .withChild(ImmutableNodes.newUnkeyedListEntryBuilder()
472                 .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
473                 .withChild(UNKEYED_LIST_ENTRY_NODE_1.body().iterator().next())
474                 .withChild(UNKEYED_LIST_ENTRY_NODE_2.body().iterator().next())
475                 .build())
476             .build(), readData(ContentParam.ALL, PATH_3, readUnkeyedListDataAllTestStrategy()));
477     }
478
479     abstract @NonNull RestconfStrategy readUnkeyedListDataAllTestStrategy();
480
481     @Test
482     final void readLeafListDataAllTest() {
483         assertEquals(ImmutableNodes.<String>newSystemLeafSetBuilder()
484             .withNodeIdentifier(new NodeIdentifier(LEAF_LIST_QNAME))
485             .withValue(ImmutableList.<LeafSetEntryNode<String>>builder()
486                 .addAll(LEAF_SET_NODE_1.body())
487                 .addAll(LEAF_SET_NODE_2.body())
488                 .build())
489             .build(), readData(ContentParam.ALL, LEAF_SET_NODE_PATH, readLeafListDataAllTestStrategy()));
490     }
491
492     abstract @NonNull RestconfStrategy readLeafListDataAllTestStrategy();
493
494     @Test
495     final void readOrderedLeafListDataAllTest() {
496         assertEquals(ImmutableNodes.<String>newUserLeafSetBuilder()
497             .withNodeIdentifier(new NodeIdentifier(LEAF_LIST_QNAME))
498             .withValue(ImmutableList.<LeafSetEntryNode<String>>builder()
499                 .addAll(ORDERED_LEAF_SET_NODE_1.body())
500                 .addAll(ORDERED_LEAF_SET_NODE_2.body())
501                 .build())
502             .build(), readData(ContentParam.ALL, LEAF_SET_NODE_PATH, readOrderedLeafListDataAllTestStrategy()));
503     }
504
505     abstract @NonNull RestconfStrategy readOrderedLeafListDataAllTestStrategy();
506
507     @Test
508     void readDataWrongPathOrNoContentTest() {
509         assertNull(readData(ContentParam.CONFIG, PATH_2, readDataWrongPathOrNoContentTestStrategy()));
510     }
511
512     abstract @NonNull RestconfStrategy readDataWrongPathOrNoContentTestStrategy();
513
514     /**
515      * Read specific type of data from data store via transaction.
516      *
517      * @param content        type of data to read (config, state, all)
518      * @param strategy       {@link RestconfStrategy} - wrapper for variables
519      * @return {@link NormalizedNode}
520      */
521     private static @Nullable NormalizedNode readData(final @NonNull ContentParam content,
522             final YangInstanceIdentifier path, final @NonNull RestconfStrategy strategy) {
523         return strategy.readData(content, path, null);
524     }
525
526     private void patch(final PatchContext patchContext, final RestconfStrategy strategy, final boolean failed) {
527         strategy.patchData(dataYangPatchRequest, patchContext);
528         verify(dataYangPatchRequest).completeWith(dataYangPatchCaptor.capture());
529         final var patchStatusContext = dataYangPatchCaptor.getValue().status();
530
531         for (var entity : patchStatusContext.editCollection()) {
532             if (failed) {
533                 assertTrue(entity.isOk(), "Edit " + entity.getEditId() + " failed");
534             } else {
535                 assertTrue(entity.isOk());
536             }
537         }
538         assertTrue(patchStatusContext.ok());
539     }
540 }