Fix serializePercentEncodingTest()
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / rfc8040 / utils / parser / YangInstanceIdentifierSerializerTest.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.restconf.nb.rfc8040.utils.parser;
9
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
12 import static org.junit.jupiter.api.Assertions.assertThrows;
13
14 import com.google.common.collect.ImmutableMap;
15 import java.text.ParseException;
16 import java.util.Map;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.junit.jupiter.api.Test;
19 import org.opendaylight.restconf.api.ApiPath;
20 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
21 import org.opendaylight.restconf.common.errors.RestconfError;
22 import org.opendaylight.restconf.server.api.DatabindContext;
23 import org.opendaylight.restconf.server.spi.ApiPathNormalizer;
24 import org.opendaylight.restconf.server.spi.ApiPathNormalizer.Path.Data;
25 import org.opendaylight.yangtools.yang.common.ErrorTag;
26 import org.opendaylight.yangtools.yang.common.ErrorType;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.common.Uint32;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
32 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
33
34 /**
35  * Unit tests for {@link YangInstanceIdentifierSerializer}.
36  */
37 class YangInstanceIdentifierSerializerTest {
38     private static final @NonNull DatabindContext DATABIND = DatabindContext.ofModel(
39         YangParserTestUtils.parseYangResourceDirectory("/restconf/parser/serializer"));
40     private static final YangInstanceIdentifierSerializer SERIALIZER = new YangInstanceIdentifierSerializer(DATABIND);
41
42     /**
43      * Positive test of serialization of <code>YangInstanceIdentifier</code> containing container node to
44      * <code>String</code>. Returned <code>String</code> is compared to have expected value.
45      */
46     @Test
47     void serializeContainerTest() {
48         assertEquals("serializer-test:contA", SERIALIZER.serializePath(
49             YangInstanceIdentifier.of(QName.create("serializer:test", "2016-06-06", "contA"))));
50     }
51
52     /**
53      * Positive test of serialization of <code>YangInstanceIdentifier</code> containing container with leaf node to
54      * <code>String</code>. Returned <code>String</code> is compared to have expected value.
55      */
56     @Test
57     void serializeContainerWithLeafTest() {
58         assertEquals("serializer-test:contA/leaf-A", SERIALIZER.serializePath(
59             YangInstanceIdentifier.of(
60                 QName.create("serializer:test", "2016-06-06", "contA"),
61                 QName.create("serializer:test", "2016-06-06", "leaf-A"))));
62     }
63
64     /**
65      * Positive test of serialization of <code>YangInstanceIdentifier</code> containing container with list with leaf
66      * list node to <code>String</code>. Returned <code>String</code> is compared to have expected value.
67      */
68     @Test
69     void serializeContainerWithListWithLeafListTest() {
70         final var list = QName.create("serializer:test", "2016-06-06", "list-A");
71         final var leafList = QName.create("serializer:test", "2016-06-06", "leaf-list-AA");
72
73         assertEquals("serializer-test:contA/list-A=100/leaf-list-AA=instance", SERIALIZER.serializePath(
74             YangInstanceIdentifier.builder()
75                 .node(QName.create("serializer:test", "2016-06-06", "contA"))
76                 .node(list)
77                 .node(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), 100))
78                 .node(leafList)
79                 .node(new NodeWithValue<>(leafList, "instance"))
80                 .build()));
81     }
82
83     /**
84      * Positive test of serialization of <code>YangInstanceIdentifier</code> to <code>String</code> when serialized
85      * <code>YangInstanceIdentifier</code> contains list with no keys. Returned <code>String</code> is compared to have
86      * expected value.
87      */
88     @Test
89     void serializeListWithNoKeysTest() {
90         assertEquals("serializer-test:list-no-key", SERIALIZER.serializePath(
91             YangInstanceIdentifier.of(
92                 QName.create("serializer:test", "2016-06-06", "list-no-key"),
93                 QName.create("serializer:test", "2016-06-06", "list-no-key"))));
94     }
95
96     /**
97      * Positive test of serialization of <code>YangInstanceIdentifier</code> to <code>String</code> when serialized
98      * <code>YangInstanceIdentifier</code> contains a keyed list, but the path argument does not specify them. Returned
99      * <code>String</code> is compared to have expected value.
100      */
101     @Test
102     void serializeMapWithNoKeysTest() {
103         assertEquals("serializer-test:list-one-key", SERIALIZER.serializePath(
104             YangInstanceIdentifier.builder()
105                 .node(QName.create("serializer:test", "2016-06-06", "list-one-key"))
106                 .nodeWithKey(QName.create("serializer:test", "2016-06-06", "list-one-key"), Map.of())
107                 .build()));
108     }
109
110     /**
111      * Positive test of serialization of <code>YangInstanceIdentifier</code> to <code>String</code> when serialized
112      * <code>YangInstanceIdentifier</code> contains list with one key. Returned <code>String</code> is compared to have
113      * expected value.
114      */
115     @Test
116     void serializeMapWithOneKeyTest() {
117         assertEquals("serializer-test:list-one-key=value", SERIALIZER.serializePath(
118             YangInstanceIdentifier.builder()
119                 .node(QName.create("serializer:test", "2016-06-06", "list-one-key"))
120                 .nodeWithKey(QName.create("serializer:test", "2016-06-06", "list-one-key"),
121                     QName.create("serializer:test", "2016-06-06", "list-one-key"), "value")
122                 .build()));
123     }
124
125     /**
126      * Positive test of serialization of <code>YangInstanceIdentifier</code> to <code>String</code> when serialized
127      * <code>YangInstanceIdentifier</code> contains list with multiple keys. Returned <code>String</code> is compared
128      * to have expected value.
129      */
130     @Test
131     void serializeMapWithMultipleKeysTest() {
132         final var list = QName.create("serializer:test", "2016-06-06", "list-multiple-keys");
133
134         assertEquals("serializer-test:list-multiple-keys=value-1,2,true", SERIALIZER.serializePath(
135             YangInstanceIdentifier.builder()
136                 .node(list)
137                 .nodeWithKey(list, ImmutableMap.of(
138                     QName.create(list, "name"), "value-1",
139                     QName.create(list, "number"), "2",
140                     QName.create(list, "enabled"), "true"))
141                 .build()));
142     }
143
144     /**
145      * Positive test of serialization of <code>YangInstanceIdentifier</code> to <code>String</code> when serialized
146      * <code>YangInstanceIdentifier</code> contains leaf node. Returned <code>String</code> is compared to have
147      * expected value.
148      */
149     @Test
150     void serializeLeafTest() {
151         assertEquals("serializer-test:leaf-0", SERIALIZER.serializePath(
152             YangInstanceIdentifier.of(QName.create("serializer:test", "2016-06-06", "leaf-0"))));
153     }
154
155     /**
156      * Positive test of serialization of <code>YangInstanceIdentifier</code> to <code>String</code> when serialized
157      * <code>YangInstanceIdentifier</code> contains leaf list node. Returned <code>String</code> is compared to have
158      * expected value.
159      */
160     @Test
161     void serializeLeafListTest() {
162         assertEquals("serializer-test:leaf-list-0=true", SERIALIZER.serializePath(
163             YangInstanceIdentifier.builder()
164                 .node(QName.create("serializer:test", "2016-06-06", "leaf-list-0"))
165                 .node(new NodeWithValue<>(QName.create("serializer:test", "2016-06-06", "leaf-list-0"), Boolean.TRUE))
166                 .build()));
167     }
168
169     /**
170      * Negative test of serialization <code>YangInstanceIdentifier</code> to <code>String</code> when
171      * <code>SchemaContext</code> is <code>null</code>. Test is expected to fail with
172      * <code>NullPointerException</code>.
173      */
174     @Test
175     void serializeNullSchemaContextNegativeTest() {
176         assertThrows(NullPointerException.class, () -> new YangInstanceIdentifierSerializer(null));
177     }
178
179     /**
180      * Negative test of serialization <code>YangInstanceIdentifier</code> to <code>String</code> when supplied
181      * <code>YangInstanceIdentifier</code> is <code>null</code>. Test is expected to fail with
182      * <code>NullPointerException</code>.
183      */
184     @Test
185     void serializeNullDataNegativeTest() {
186         assertThrows(NullPointerException.class, () -> SERIALIZER.serializePath(null));
187     }
188
189     /**
190      * Test of serialization <code>YangInstanceIdentifier</code> to <code>String</code> when supplied
191      * <code>YangInstanceIdentifier</code> is <code>YangInstanceIdentifier.EMPTY</code>.
192      * Empty <code>String</code> is expected as a return value.
193      */
194     @Test
195     void serializeEmptyDataTest() {
196         assertEquals("", SERIALIZER.serializePath(YangInstanceIdentifier.of()));
197     }
198
199     /**
200      * Negative test when it is not possible to find child node of current node. Test is expected to fail with
201      * {@link RestconfDocumentedException} and error message is compared to expected error message.
202      */
203     @Test
204     void serializeChildNodeNotFoundNegativeTest() {
205         final var error = assertError(YangInstanceIdentifier.of(
206             QName.create("serializer:test", "2016-06-06", "contA"),
207             QName.create("serializer:test", "2016-06-06", "not-existing-leaf")));
208         assertEquals("""
209             Invalid input '/(serializer:test?revision=2016-06-06)contA/not-existing-leaf': schema for argument \
210             '(serializer:test?revision=2016-06-06)not-existing-leaf' (after 'serializer-test:contA') not found""",
211             error.getErrorMessage());
212         assertEquals(ErrorType.APPLICATION, error.getErrorType());
213         assertEquals(ErrorTag.UNKNOWN_ELEMENT, error.getErrorTag());
214     }
215
216     /**
217      * Test if URIs with percent encoded characters are all correctly serialized.
218      */
219     @Test
220     void serializePercentEncodingTest() {
221         assertEquals("serializer-test:list-one-key=foo%3Afoo bar%2Ffoo%2Cbar%2F%27bar%27", SERIALIZER.serializePath(
222             YangInstanceIdentifier.builder()
223                 .node(QName.create("serializer:test", "2016-06-06", "list-one-key"))
224                 .nodeWithKey(QName.create("serializer:test", "2016-06-06", "list-one-key"),
225                     QName.create("serializer:test", "2016-06-06", "name"), "foo:foo bar/foo,bar/'bar'")
226                 .build()));
227     }
228
229     /**
230      * Test if URIs with no percent encoded characters are correctly serialized. Input should be untouched.
231      */
232     @Test
233     void serializeNoPercentEncodingTest() {
234         assertEquals("serializer-test:list-one-key=foo\"b\"bar", SERIALIZER.serializePath(
235             YangInstanceIdentifier.builder()
236                 .node(QName.create("serializer:test", "2016-06-06", "list-one-key"))
237                 .nodeWithKey(QName.create("serializer:test", "2016-06-06", "list-one-key"),
238                     QName.create("serializer:test", "2016-06-06", "list-one-key"), "foo\"b\"bar")
239             .build()));
240     }
241
242     /**
243      * Test of serialization when nodes in input <code>YangInstanceIdentifier</code> are defined in two different
244      * modules by using augmentation.
245      */
246     @Test
247     void serializeIncludedNodesTest() {
248         final var list = QName.create("serializer:test:included", "2016-06-06", "augmented-list");
249         final var child = QName.create("serializer:test", "2016-06-06", "augmented-leaf");
250
251         assertEquals("serializer-test-included:augmented-list=100/serializer-test:augmented-leaf",
252             SERIALIZER.serializePath(YangInstanceIdentifier.builder()
253                 .node(list)
254                 .node(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), 100))
255                 .node(child)
256                 .build()));
257     }
258
259     /**
260      * Test of serialization when nodes in input <code>YangInstanceIdentifier</code> are defined in two different
261      * modules by using augmentation. Augmented node in data supplied for serialization has wrong namespace.
262      * <code>RestconfDocumentedException</code> is expected because augmented node is defined in other module than its
263      * parent and will not be found.
264      */
265     @Test
266     void serializeIncludedNodesSerializationTest() {
267         final var list = QName.create("serializer:test:included", "2016-06-06", "augmented-list");
268
269         final var error = assertError(YangInstanceIdentifier.builder()
270             .node(list)
271             .node(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), 100))
272             // child should has different namespace
273             .node(QName.create("serializer:test:included", "2016-06-06", "augmented-leaf"))
274             .build());
275         assertEquals("""
276             Invalid input '/(serializer:test:included?revision=2016-06-06)augmented-list/augmented-list[{(\
277             serializer:test:included?revision=2016-06-06)list-key=100}]/augmented-leaf': schema for argument \
278             '(serializer:test:included?revision=2016-06-06)augmented-leaf' (after \
279             'serializer-test-included:augmented-list=100') not found""", error.getErrorMessage());
280         assertEquals(ErrorType.APPLICATION, error.getErrorType());
281         assertEquals(ErrorTag.UNKNOWN_ELEMENT, error.getErrorTag());
282     }
283
284     /**
285      * Positive test of deserialization URI <code>String</code> to <code>YangInstanceIdentifier</code> and
286      * serialization of <code>YangInstanceIdentifier</code> to <code>String</code> when original <code>String</code>
287      * URI contains list identifier and leaf identifier.
288      */
289     @Test
290     void codecListAndLeafTest() {
291         final var dataYangII = assertNormalized("list-test:top/list1=%2C%27\"%3A\"%20%2F,,foo/list2=a,b/result");
292         final var list1 = QName.create("list:test", "2016-04-29", "list1");
293         final var list2 = QName.create("list:test", "2016-04-29", "list2");
294         assertEquals(YangInstanceIdentifier.builder()
295             .node(QName.create("list:test", "2016-04-29", "top"))
296             .node(list1)
297             .nodeWithKey(list1, Map.<QName, Object>of(
298                 QName.create(list1, "key1"), ",'\":\" /",
299                 QName.create(list1, "key2"), "",
300                 QName.create(list1, "key3"), "foo"))
301             .node(list2)
302             .nodeWithKey(list2, Map.<QName, Object>of(
303                 QName.create(list2, "key4"), "a",
304                 QName.create(list2, "key5"), "b"))
305             .node(QName.create("list:test", "2016-04-29", "result"))
306             .build(), dataYangII);
307         assertEquals("list-test:top/list1=%2C%27\"%3A\" %2F,,foo/list2=a,b/result",
308             SERIALIZER.serializePath(dataYangII));
309     }
310
311     /**
312      * Positive test of deserialization URI <code>String</code> to <code>YangInstanceIdentifier</code> and
313      * serialization of <code>YangInstanceIdentifier</code> to <code>String</code> when original <code>String</code>
314      * URI contains leaf list identifier.
315      */
316     @Test
317     void codecLeafListTest() {
318         final var str = "list-test:top/Y=4";
319         final var dataYangII = assertNormalized(str);
320         final var y = QName.create("list:test", "2016-04-29", "Y");
321         assertEquals(YangInstanceIdentifier.builder()
322             .node(QName.create("list:test", "2016-04-29", "top"))
323             .node(y)
324             .node(new NodeWithValue<>(y, Uint32.valueOf(4)))
325             .build(), dataYangII);
326         assertEquals(str, SERIALIZER.serializePath(dataYangII));
327     }
328
329     /**
330      * Positive test of serialization of an empty {@link YangInstanceIdentifier}.
331      */
332     @Test
333     void codecDeserializeAndSerializeEmptyTest() {
334         assertEquals("", SERIALIZER.serializePath(YangInstanceIdentifier.of()));
335     }
336
337     private static YangInstanceIdentifier assertNormalized(final String str) {
338         try {
339             return assertInstanceOf(Data.class, new ApiPathNormalizer(DATABIND).normalizePath(ApiPath.parse(str)))
340                 .instance();
341         } catch (ParseException e) {
342             throw new AssertionError(e);
343         }
344     }
345
346     private static RestconfError assertError(final YangInstanceIdentifier path) {
347         final var ex = assertThrows(RestconfDocumentedException.class, () -> SERIALIZER.serializePath(path));
348         final var errors = ex.getErrors();
349         assertEquals(1, errors.size());
350         return errors.get(0);
351     }
352 }