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