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