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