43b9b218a9d15565320eb60702ff22357e9d6bae
[netconf.git] /
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
9 package org.opendaylight.restconf.parser.builder;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertTrue;
13 import static org.junit.Assert.fail;
14
15 import com.google.common.collect.Iterables;
16 import com.google.common.collect.Sets;
17 import java.util.Iterator;
18 import java.util.LinkedHashMap;
19 import java.util.Map;
20 import org.junit.Before;
21 import org.junit.Ignore;
22 import org.junit.Rule;
23 import org.junit.Test;
24 import org.junit.rules.ExpectedException;
25 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
26 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
27 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
31 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
32
33 /**
34  * Unit tests for {@link YangInstanceIdentifierDeserializer}
35  */
36 public class YangInstanceIdentifierDeserializerTest {
37
38     @Rule
39     public ExpectedException thrown = ExpectedException.none();
40
41     // schema context
42     private SchemaContext schemaContext;
43
44     @Before
45     public void init() throws Exception {
46         schemaContext = TestRestconfUtils.loadSchemaContext("/restconf/parser/deserializer");
47     }
48
49     /**
50      * Test of deserialization <code>String</code> URI with container to
51      * <code>Iterable<YangInstanceIdentifier.PathArgument></code>.
52      */
53     @Test
54     public void deserializeContainerTest() {
55         final Iterable<YangInstanceIdentifier.PathArgument> result = YangInstanceIdentifierDeserializer
56                 .create(schemaContext, "deserializer-test:contA");
57
58         assertEquals("Result does not contains expected number of path arguments", 1, Iterables.size(result));
59         assertEquals("Not expected path argument",
60                 YangInstanceIdentifier.NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")),
61                 result.iterator().next());
62     }
63
64     /**
65      * Test of deserialization <code>String</code> URI with container containing leaf to
66      * <code>Iterable<YangInstanceIdentifier.PathArgument></code>.
67      */
68     @Test
69     public void deserializeContainerWithLeafTest() {
70         final Iterable<YangInstanceIdentifier.PathArgument> result = YangInstanceIdentifierDeserializer
71                 .create(schemaContext, "deserializer-test:contA/leaf-A");
72
73         assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
74
75         final Iterator<YangInstanceIdentifier.PathArgument> iterator = result.iterator();
76         assertEquals("Not expected path argument",
77                 YangInstanceIdentifier.NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")),
78                 iterator.next());
79         assertEquals("Not expected path argument",
80                 YangInstanceIdentifier.NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "leaf-A")),
81                 iterator.next());
82     }
83
84     /**
85      * Test of deserialization <code>String</code> URI with container containing list with leaf list to
86      * <code>Iterable<YangInstanceIdentifier.PathArgument></code>.
87      */
88     @Test
89     public void deserializeContainerWithListWithLeafListTest() {
90         final Iterable<YangInstanceIdentifier.PathArgument> result = YangInstanceIdentifierDeserializer
91                 .create(schemaContext, "deserializer-test:contA/list-A=100/leaf-list-AA=instance");
92
93         assertEquals("Result does not contains expected number of path arguments", 5, Iterables.size(result));
94
95         final Iterator<YangInstanceIdentifier.PathArgument> iterator = result.iterator();
96
97         // container
98         assertEquals("Not expected path argument",
99                 YangInstanceIdentifier.NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")),
100                 iterator.next());
101
102         // list
103         final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
104         assertEquals("Not expected path argument",
105                 YangInstanceIdentifier.NodeIdentifier.create(list),
106                 iterator.next());
107         assertEquals("Not expected path argument",
108                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
109                         list, QName.create(list, "list-key"), 100).toString(),
110                 iterator.next().toString());
111
112         // leaf list
113         final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-AA");
114         assertEquals("Not expected path argument",
115                 YangInstanceIdentifier.NodeIdentifier.create(leafList),
116                 iterator.next());
117         assertEquals("Not expected path argument",
118                 new YangInstanceIdentifier.NodeWithValue(leafList, "instance"),
119                 iterator.next());
120     }
121
122     /**
123      * Test of deserialization <code>String</code> URI containing list with no keys to
124      * <code>Iterable<YangInstanceIdentifier.PathArgument></code>.
125      */
126     @Test
127     public void deserializeListWithNoKeysTest() {
128         final Iterable<YangInstanceIdentifier.PathArgument> result = YangInstanceIdentifierDeserializer
129                 .create(schemaContext, "deserializer-test:list-no-key");
130
131         assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
132
133         final Iterator<YangInstanceIdentifier.PathArgument> iterator = result.iterator();
134         final QName list = QName.create("deserializer:test", "2016-06-06", "list-no-key");
135
136         assertEquals("Not expected path argument",
137                 YangInstanceIdentifier.NodeIdentifier.create(list),
138                 iterator.next());
139         assertEquals("Not expected path argument",
140                 YangInstanceIdentifier.NodeIdentifier.create(list),
141                 iterator.next());
142     }
143
144     /**
145      * Test of deserialization <code>String</code> URI containing list with one key to
146      * <code>Iterable<YangInstanceIdentifier.PathArgument></code>.
147      */
148     @Test
149     public void deserializeListWithOneKeyTest() {
150         final Iterable<YangInstanceIdentifier.PathArgument> result = YangInstanceIdentifierDeserializer
151                 .create(schemaContext, "deserializer-test:list-one-key=value");
152
153         assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
154
155         final Iterator<YangInstanceIdentifier.PathArgument> iterator = result.iterator();
156         final QName list = QName.create("deserializer:test", "2016-06-06", "list-one-key");
157
158         assertEquals("Not expected path argument",
159                 YangInstanceIdentifier.NodeIdentifier.create(list),
160                 iterator.next());
161         assertEquals("Not expected path argument",
162                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(list, QName.create(list, "name"), "value"),
163                 iterator.next());
164     }
165
166     /**
167      * Test of deserialization <code>String</code> URI containing list with multiple keys to
168      * <code>Iterable<YangInstanceIdentifier.PathArgument></code>.
169      */
170     @Test
171     public void deserializeListWithMultipleKeysTest() {
172         final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
173         final Map<QName, Object> values = new LinkedHashMap<>();
174         values.put(QName.create(list, "name"), "value");
175         values.put(QName.create(list, "number"), 100);
176         values.put(QName.create(list, "enabled"), false);
177
178         final Iterable<YangInstanceIdentifier.PathArgument> result = YangInstanceIdentifierDeserializer
179                 .create(schemaContext, "deserializer-test:list-multiple-keys=value,100,false");
180
181         assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
182
183         final Iterator<YangInstanceIdentifier.PathArgument> iterator = result.iterator();
184
185         assertEquals("Not expected path argument",
186                 YangInstanceIdentifier.NodeIdentifier.create(list),
187                 iterator.next());
188         assertEquals("Not expected path argument",
189                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(list, values).toString(),
190                 iterator.next().toString());
191     }
192
193     /**
194      * Test of deserialization <code>String</code> URI containing leaf list to
195      * <code>Iterable<YangInstanceIdentifier.PathArgument></code>.
196      */
197     @Test
198     public void deserializeLeafListTest() {
199         final Iterable<YangInstanceIdentifier.PathArgument> result = YangInstanceIdentifierDeserializer
200                 .create(schemaContext, "deserializer-test:leaf-list-0=true");
201
202         assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
203
204         final Iterator<YangInstanceIdentifier.PathArgument> iterator = result.iterator();
205         final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-0");
206
207         assertEquals("Not expected path argument",
208                 new YangInstanceIdentifier.NodeIdentifier(leafList),
209                 iterator.next());
210         assertEquals("Not expected path argument",
211                 new YangInstanceIdentifier.NodeWithValue(leafList, true).toString(),
212                 iterator.next().toString());
213     }
214
215     /**
216      * Test when empty <code>String</code> is supplied as an input. Test is expected to return empty result.
217      */
218     @Test
219     public void deserializeEmptyDataTest() {
220         final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(schemaContext, "");
221         assertTrue("Empty result expected", Iterables.isEmpty(result));
222     }
223
224     /**
225      * Negative test when supplied <code>SchemaContext</code> is null. Test is expected to fail with
226      * <code>NullPointerException</code>.
227      */
228     @Test
229     public void deserializeNullSchemaContextNegativeTest() {
230         thrown.expect(NullPointerException.class);
231         YangInstanceIdentifierDeserializer.create(null, "deserializer-test:contA");
232     }
233
234     /**
235      * Negative test when supplied <code>String</code> data to deserialize is null. Test is expected to fail with
236      * <code>NullPointerException</code>.
237      */
238     @Test
239     public void nullDataNegativeNegativeTest() {
240         thrown.expect(NullPointerException.class);
241         YangInstanceIdentifierDeserializer.create(schemaContext, null);
242     }
243
244     /**
245      * Negative test when identifier is not followed by slash or equals. Test is expected to fail with
246      * <code>IllegalArgumentException</code>.
247      */
248     @Test
249     public void deserializeBadCharMissingSlashOrEqualNegativeTest() {
250         thrown.expect(IllegalArgumentException.class);
251         YangInstanceIdentifierDeserializer.create(schemaContext, "deserializer-test:cont*leaf-A");
252     }
253
254     /**
255      * Negative test of validating identifier when there is a slash after container without next identifier. Test
256      * is expected to fail with <code>IllegalArgumentException</code>.
257      */
258     @Test
259     public void validArgIdentifierContainerEndsWithSlashNegativeTest() {
260         thrown.expect(IllegalArgumentException.class);
261         YangInstanceIdentifierDeserializer.create(schemaContext, "deserializer-test:contA/");
262     }
263
264     /**
265      * Negative test of validating identifier when there is a slash after list key values without next identifier. Test
266      * is expected to fail with <code>IllegalArgumentException</code>.
267      */
268     @Test
269     public void validArgIdentifierListEndsWithSlashLNegativeTest() {
270         thrown.expect(IllegalArgumentException.class);
271         YangInstanceIdentifierDeserializer.create(schemaContext, "deserializer-test:list-one-key=value/");
272     }
273
274     /**
275      * Negative test of creating <code>QName</code> when identifier is empty (example: '/'). Test is expected to fail
276      * with <code>IllegalArgumentException</code>.
277      */
278     @Test
279     public void prepareQnameEmptyIdentifierNegativeTest() {
280         thrown.expect(IllegalArgumentException.class);
281         YangInstanceIdentifierDeserializer.create(schemaContext, "/");
282     }
283
284     /**
285      * Negative test of creating <code>QName</code> when two identifiers are separated by two slashes. Test is
286      * expected to fail with <code>IllegalArgumentException</code>.
287      */
288     @Test
289     public void prepareQnameTwoSlashesNegativeTest() {
290         thrown.expect(IllegalArgumentException.class);
291         YangInstanceIdentifierDeserializer.create(schemaContext, "deserializer-test:contA//leaf-A");
292     }
293
294     /**
295      * Negative test of creating <code>QName</code> when in identifier there is another sign than colon or equals.
296      * Test is expected to fail with <code>IllegalArgumentException</code>.
297      */
298     @Test
299     public void prepareQnameBuildPathNegativeTest() {
300         thrown.expect(IllegalArgumentException.class);
301         YangInstanceIdentifierDeserializer.create(schemaContext, "deserializer-test*contA");
302     }
303
304     /**
305      * Negative test of creating <code>QName</code> when it is not possible to find module for specified prefix. Test is
306      * expected to fail with <code>IllegalArgumentException</code>.
307      */
308     @Test
309     public void prepareQnameNotExistingPrefixNegativeTest() {
310         thrown.expect(IllegalArgumentException.class);
311         YangInstanceIdentifierDeserializer.create(schemaContext, "not-existing:contA");
312     }
313
314     /**
315      * Negative test of creating <code>QName</code> when after prefix and colon there is not parsable identifier as
316      * local name. Test is expected to fail with <code>IllegalArgumentException</code>.
317      */
318     @Test
319     public void prepareQnameNotValidPrefixAndLocalNameNegativeTest() {
320         thrown.expect(IllegalArgumentException.class);
321         YangInstanceIdentifierDeserializer.create(schemaContext, "deserializer-test:*not-parsable-identifier");
322     }
323
324     /**
325      * Negative test of creating <code>QName</code> when data ends after prefix and colon. Test is expected to fail
326      * with <code>StringIndexOutOfBoundsException</code>.
327      */
328     @Test
329     public void prepareQnameErrorParsingNegativeTest() {
330         thrown.expect(StringIndexOutOfBoundsException.class);
331         YangInstanceIdentifierDeserializer.create(schemaContext, "deserializer-test:");
332     }
333
334     /**
335      * Negative test of creating <code>QName</code> when after identifier and colon there is node name of unknown
336      * node in current container. Test is expected to fail with <code>RestconfDocumentedException</code> and error
337      * type, error tag and error status code are compared to expected values.
338      */
339     @Test
340     public void prepareQnameNotValidContainerNameNegativeTest() {
341         try {
342             YangInstanceIdentifierDeserializer.create(schemaContext, "deserializer-test:contA/leafB");
343             fail("Test should fail due to unknown child node in container");
344         } catch (final RestconfDocumentedException e) {
345             assertEquals("Not expected error type",
346                     RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
347             assertEquals("Not expected error tag",
348                     RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
349             assertEquals("Not expected error status code",
350                     404, e.getErrors().get(0).getErrorTag().getStatusCode());
351         }
352     }
353
354     /**
355      * Negative test of creating <code>QName</code> when after identifier and equals there is node name of unknown
356      * node in current list. Test is expected to fail with <code>RestconfDocumentedException</code> and error
357      * type, error tag and error status code are compared to expected values.
358      */
359     @Test
360     public void prepareQnameNotValidListNameNegativeTest() {
361         try {
362             YangInstanceIdentifierDeserializer.create(schemaContext, "deserializer-test:list-no-key/disabled=false");
363             fail("Test should fail due to unknown child node in list");
364         } catch (final RestconfDocumentedException e) {
365             assertEquals("Not expected error type",
366                     RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
367             assertEquals("Not expected error tag",
368                     RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
369             assertEquals("Not expected error status code",
370                     404, e.getErrors().get(0).getErrorTag().getStatusCode());
371         }
372     }
373
374     /**
375      * Negative test of getting next identifier when current node is keyed entry. Test is expected to
376      * fail with <code>IllegalArgumentException</code>.
377      */
378     @Test
379     public void prepareIdentifierNotKeyedEntryNegativeTest() {
380         thrown.expect(IllegalArgumentException.class);
381         YangInstanceIdentifierDeserializer.create(schemaContext, "deserializer-test:list-one-key");
382     }
383
384     /**
385      * Negative test when there is a comma also after the last key. Test is expected to fail with
386      * <code>IllegalArgumentException</code>.
387      */
388     @Test
389     public void deserializeKeysEndsWithComaNegativeTest() {
390         thrown.expect(IllegalArgumentException.class);
391         YangInstanceIdentifierDeserializer.create( schemaContext,
392                 "deserializer-test:list-multiple-keys=value,100,false,");
393     }
394
395     /**
396      * Positive when not all keys of list are encoded. The missing keys should be considered to has empty
397      * <code>String</code> values. Also value of next leaf must not be considered to be missing key value.
398      */
399     @Test
400     public void notAllListKeysEncodedPositiveTest() {
401         final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
402         final Map<QName, Object> values = new LinkedHashMap<>();
403         values.put(QName.create(list, "name"), ":foo");
404         values.put(QName.create(list, "number"), "");
405         values.put(QName.create(list, "enabled"), "");
406
407         final Iterable<YangInstanceIdentifier.PathArgument> result = YangInstanceIdentifierDeserializer.create(
408                 schemaContext, "deserializer-test:list-multiple-keys=%3Afoo,,/string-value");
409
410         assertEquals("Result does not contains expected number of path arguments", 3, Iterables.size(result));
411
412         final Iterator<YangInstanceIdentifier.PathArgument> iterator = result.iterator();
413
414         // list
415         assertEquals("Not expected path argument",
416                 YangInstanceIdentifier.NodeIdentifier.create(list),
417                 iterator.next());
418         assertEquals("Not expected path argument",
419                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(list, values).toString(),
420                 iterator.next().toString());
421
422         // leaf
423         assertEquals("Not expected path argument",
424                 new YangInstanceIdentifier.NodeIdentifier(
425                         QName.create("deserializer:test", "2016-06-06", "string-value")),
426                 iterator.next());
427     }
428
429     /**
430      * Negative test when not all keys of list are encoded and it is not possible to consider missing keys to be empty.
431      * Test is expected to fail with <code>RestconfDocumentedException</code> and error type, error tag and error
432      * status code are compared to expected values.
433      */
434     @Test
435     public void notAllListKeysEncodedNegativeTest() {
436         try {
437             YangInstanceIdentifierDeserializer.create(
438                     schemaContext, "deserializer-test:list-multiple-keys=%3Afoo/string-value");
439             fail("Test should fail due to missing list key values");
440         } catch (final RestconfDocumentedException e) {
441             assertEquals("Not expected error type",
442                     RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
443             assertEquals("Not expected error tag",
444                     RestconfError.ErrorTag.MISSING_ATTRIBUTE, e.getErrors().get(0).getErrorTag());
445             assertEquals("Not expected error status code",
446                     400, e.getErrors().get(0).getErrorTag().getStatusCode());
447         }
448     }
449
450     /**
451      * Test URI with list where key value starts with, ends with or contains percent encoded characters.The encoded
452      * value should be complete also with not percent-encoded parts.
453      */
454     @Test
455     public void percentEncodedKeyEndsWithNoPercentEncodedChars() {
456         final String URI = "deserializer-test:list-multiple-keys=%3Afoo,bar%3A,foo%3Abar";
457         final YangInstanceIdentifier result = YangInstanceIdentifier.create(
458                 YangInstanceIdentifierDeserializer.create(schemaContext, URI));
459
460         final Iterator<Map.Entry<QName, Object>> resultListKeys = ((YangInstanceIdentifier.NodeIdentifierWithPredicates)
461                 result.getLastPathArgument()).getKeyValues().entrySet().iterator();
462
463         assertEquals(":foo", resultListKeys.next().getValue());
464         assertEquals("bar:", resultListKeys.next().getValue());
465         assertEquals("foo:bar", resultListKeys.next().getValue());
466     }
467
468     /**
469      * Positive test when all keys of list can be considered to be empty <code>String</code>.
470      */
471     @Test
472     public void deserializeAllKeysEmptyTest() {
473         final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
474         final Map<QName, Object> values = new LinkedHashMap<>();
475         values.put(QName.create(list, "name"), "");
476         values.put(QName.create(list, "number"), "");
477         values.put(QName.create(list, "enabled"), "");
478
479         final Iterable<YangInstanceIdentifier.PathArgument> result = YangInstanceIdentifierDeserializer
480                 .create(schemaContext, "deserializer-test:list-multiple-keys=,,");
481
482         assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
483
484         final Iterator<YangInstanceIdentifier.PathArgument> iterator = result.iterator();
485
486         assertEquals("Not expected path argument",
487                 YangInstanceIdentifier.NodeIdentifier.create(list),
488                 iterator.next());
489         assertEquals("Not expected path argument",
490                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(list, values).toString(),
491                 iterator.next().toString());
492     }
493
494     /**
495      * Negative test of deserialization when for leaf list there is no specified instance value.
496      * <code>RestconfDocumentedException</code> is expected and error type, error tag and error status code are
497      * compared to expected values.
498      */
499     @Test
500     public void leafListMissingKeyNegativeTest() {
501         try {
502             YangInstanceIdentifierDeserializer.create(schemaContext, "deserializer-test:leaf-list-0=");
503             fail("Test should fail due to missing instance value");
504         } catch (final RestconfDocumentedException e) {
505             assertEquals("Not expected error type",
506                     RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
507             assertEquals("Not expected error tag",
508                     RestconfError.ErrorTag.MISSING_ATTRIBUTE, e.getErrors().get(0).getErrorTag());
509             assertEquals("Not expected error status code",
510                     400, e.getErrors().get(0).getErrorTag().getStatusCode());
511         }
512     }
513
514     /**
515      * Positive test of deserialization when parts of input URI <code>String</code> are defined in another module.
516      */
517     @Test
518     public void deserializePartInOtherModuleTest() {
519         final Iterable<YangInstanceIdentifier.PathArgument> result = YangInstanceIdentifierDeserializer.create(
520                 schemaContext, "deserializer-test-included:augmented-list=100/augmented-leaf");
521
522         assertEquals("Result does not contains expected number of path arguments", 4, Iterables.size(result));
523
524         final Iterator<YangInstanceIdentifier.PathArgument> iterator = result.iterator();
525         final QName list = QName.create("deserializer:test:included", "2016-06-06", "augmented-list");
526         final QName child = QName.create("deserializer:test", "2016-06-06", "augmented-leaf");
527
528         // list
529         assertEquals("Not expected path argument",
530                 YangInstanceIdentifier.NodeIdentifier.create(list),
531                 iterator.next());
532
533         assertEquals("Not expected path argument",
534                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(list, QName.create(list, "list-key"), 100)
535                         .toString(),
536                 iterator.next()
537                         .toString());
538
539         // augmented leaf
540         assertEquals("Not expected path argument",
541                 new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(child)),
542                 iterator.next());
543
544         assertEquals("Not expected path argument",
545                 YangInstanceIdentifier.NodeIdentifier.create(child),
546                 iterator.next());
547     }
548 }