2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
3 * Copyright (c) 2021 PANTHEON.tech, s.r.o.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.restconf.nb.rfc8040.utils.parser;
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.assertThrows;
16 import com.google.common.collect.ImmutableMap;
17 import java.io.FileNotFoundException;
18 import java.util.Iterator;
19 import java.util.List;
21 import java.util.Map.Entry;
23 import org.junit.AfterClass;
24 import org.junit.BeforeClass;
25 import org.junit.Test;
26 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
27 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
28 import org.opendaylight.yangtools.yang.common.ErrorTag;
29 import org.opendaylight.yangtools.yang.common.ErrorType;
30 import org.opendaylight.yangtools.yang.common.QName;
31 import org.opendaylight.yangtools.yang.common.Revision;
32 import org.opendaylight.yangtools.yang.common.Uint16;
33 import org.opendaylight.yangtools.yang.common.Uint8;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
38 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
40 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
41 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
44 * Unit tests for {@link YangInstanceIdentifierDeserializer}.
46 public class YangInstanceIdentifierDeserializerTest {
47 private static final QName ACTIONS_INTERFACES =
48 QName.create("https://example.com/ns/example-actions", "2016-07-07", "interfaces");
51 private static EffectiveModelContext SCHEMA_CONTEXT;
54 public static void beforeClass() throws FileNotFoundException {
56 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/restconf/parser/deserializer"));
60 public static void afterClass() {
61 SCHEMA_CONTEXT = null;
65 * Test of deserialization <code>String</code> URI with container to
66 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
69 public void deserializeContainerTest() {
70 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA")
71 .path.getPathArguments();
72 assertEquals(1, result.size());
73 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
77 * Test of deserialization <code>String</code> URI with container containing leaf to
78 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
81 public void deserializeContainerWithLeafTest() {
82 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leaf-A")
83 .path.getPathArguments();
84 assertEquals(2, result.size());
85 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
86 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "leaf-A")), result.get(1));
90 * Test of deserialization <code>String</code> URI with container containing list with leaf list to
91 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
94 public void deserializeContainerWithListWithLeafListTest() {
95 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
96 "deserializer-test:contA/list-A=100/leaf-list-AA=instance").path.getPathArguments();
97 assertEquals(5, result.size());
100 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
102 final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
103 assertEquals(NodeIdentifier.create(list), result.get(1));
104 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint8.valueOf(100)),
107 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-AA");
108 assertEquals(NodeIdentifier.create(leafList), result.get(3));
109 assertEquals(new NodeWithValue<>(leafList, "instance"), result.get(4));
113 * Test of deserialization <code>String</code> URI with container containing list with Action to
114 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
117 public void deserializeContainerWithListWithActionTest() {
118 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
119 "example-actions:interfaces/interface=eth0/reset").path.getPathArguments();
120 assertEquals(4, result.size());
122 assertEquals(NodeIdentifier.create(ACTIONS_INTERFACES), result.get(0));
124 final QName list = QName.create(ACTIONS_INTERFACES, "interface");
125 assertEquals(NodeIdentifier.create(list), result.get(1));
126 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "eth0"), result.get(2));
128 assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "reset")), result.get(3));
132 * Test of deserialization <code>String</code> URI with container containing choice node with Action to
133 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
136 public void deserializeContainerWithChoiceSchemaNodeWithActionTest() {
137 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
138 "example-actions:interfaces/typeA-gigabyte/interface=eth0/reboot").path.getPathArguments();
139 assertEquals(6, result.size());
142 assertEquals(NodeIdentifier.create(ACTIONS_INTERFACES), result.get(0));
144 assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "interface-type")), result.get(1));
146 assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "typeA-gigabyte")), result.get(2));
149 final QName list = QName.create(ACTIONS_INTERFACES, "interface");
150 assertEquals(NodeIdentifier.create(list), result.get(3));
151 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "eth0"), result.get(4));
154 assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "reboot")), result.get(5));
158 * Test of deserialization <code>String</code> URI with container containing choice node with Action to
159 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
162 public void deserializeContainerWithChoiceCaseSchemaNodeWithActionTest() {
163 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
164 "example-actions:interfaces/udp/reboot").path.getPathArguments();
165 assertEquals(4, result.size());
167 assertEquals(NodeIdentifier.create(ACTIONS_INTERFACES), result.get(0));
169 assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "protocol")), result.get(1));
171 assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "udp")), result.get(2));
173 assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "reboot")), result.get(3));
177 * Test of deserialization <code>String</code> URI containing list with no keys to
178 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
181 public void deserializeListWithNoKeysTest() {
182 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-no-key")
183 .path.getPathArguments();
184 assertEquals(2, result.size());
185 final QName list = QName.create("deserializer:test", "2016-06-06", "list-no-key");
186 assertEquals(NodeIdentifier.create(list), result.get(0));
187 assertEquals(NodeIdentifier.create(list), result.get(1));
191 * Test of deserialization <code>String</code> URI containing list with one key to
192 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
195 public void deserializeListWithOneKeyTest() {
196 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
197 "deserializer-test:list-one-key=value").path.getPathArguments();
198 assertEquals(2, result.size());
199 final QName list = QName.create("deserializer:test", "2016-06-06", "list-one-key");
200 assertEquals(NodeIdentifier.create(list), result.get(0));
201 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "value"), result.get(1));
205 * Test of deserialization <code>String</code> URI containing list with multiple keys to
206 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
209 public void deserializeListWithMultipleKeysTest() {
210 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
211 final Map<QName, Object> values = ImmutableMap.of(
212 QName.create(list, "name"), "value",
213 QName.create(list, "number"), Uint8.valueOf(100),
214 QName.create(list, "enabled"), false);
216 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
217 "deserializer-test:list-multiple-keys=value,100,false").path.getPathArguments();
218 assertEquals(2, result.size());
219 assertEquals(NodeIdentifier.create(list), result.get(0));
220 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
224 * Test of deserialization <code>String</code> URI containing leaf list to
225 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
228 public void deserializeLeafListTest() {
229 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
230 "deserializer-test:leaf-list-0=true").path.getPathArguments();
231 assertEquals(2, result.size());
233 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-0");
234 assertEquals(new NodeIdentifier(leafList), result.get(0));
235 assertEquals(new NodeWithValue<>(leafList, true), result.get(1));
239 * Test when empty <code>String</code> is supplied as an input. Test is expected to return empty result.
242 public void deserializeEmptyDataTest() {
243 assertEquals(List.of(), YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "").path.getPathArguments());
247 * Negative test when supplied <code>SchemaContext</code> is null. Test is expected to fail with
248 * <code>NullPointerException</code>.
251 public void deserializeNullSchemaContextNegativeTest() {
252 assertThrows(NullPointerException.class,
253 () -> YangInstanceIdentifierDeserializer.create(null, "deserializer-test:contA"));
257 * Negative test when supplied <code>String</code> data to deserialize is null. Test is expected to fail with
258 * <code>NullPointerException</code>.
261 public void nullDataNegativeNegativeTest() {
262 assertThrows(NullPointerException.class,
263 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, (String) null));
267 * Negative test when identifier is not followed by slash or equals. Test is expected to fail with
268 * <code>RestconfDocumentedException</code>.
271 public void deserializeBadCharMissingSlashOrEqualNegativeTest() {
272 final var ex = assertThrows(RestconfDocumentedException.class,
273 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:cont*leaf-A"));
274 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
275 + "error-message: Invalid path 'deserializer-test:cont*leaf-A' at offset 22, "
276 + "error-info: Expecting [a-zA-Z_.-], not '*']]", ex.getMessage());
277 final var errors = ex.getErrors();
278 assertEquals(1, errors.size());
279 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
280 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
281 assertEquals("Expecting [a-zA-Z_.-], not '*'", errors.get(0).getErrorInfo());
285 * Negative test of validating identifier when there is a slash after container without next identifier. Test
286 * is expected to fail with <code>RestconfDocumentedException</code>.
289 public void validArgIdentifierContainerEndsWithSlashNegativeTest() {
290 final var ex = assertThrows(RestconfDocumentedException.class,
291 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/"));
292 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
293 + "error-message: Invalid path 'deserializer-test:contA/' at offset 24, "
294 + "error-info: Identifier may not be empty]]", ex.getMessage());
295 final var errors = ex.getErrors();
296 assertEquals(1, errors.size());
297 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
298 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
299 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
303 * Negative test of validating identifier when there are multiple slashes after container without next identifier.
304 * Test is expected to fail with <code>RestconfDocumentedException</code>.
307 public void validArgIdentifierContainerEndsWithMultipleSlashesNegativeTest() {
308 final var ex = assertThrows(RestconfDocumentedException.class,
309 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA///"));
310 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
311 + "error-message: Invalid path 'deserializer-test:contA///' at offset 24, "
312 + "error-info: Identifier may not be empty]]", ex.getMessage());
313 final var errors = ex.getErrors();
314 assertEquals(1, errors.size());
315 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
316 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
317 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
321 * Negative test of validating identifier when there is a slash after list key values without next identifier. Test
322 * is expected to fail with <code>RestconfDocumentedException</code>.
325 public void validArgIdentifierListEndsWithSlashLNegativeTest() {
326 final var ex = assertThrows(RestconfDocumentedException.class,
327 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key=value/"));
328 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
329 + "error-message: Invalid path 'deserializer-test:list-one-key=value/' at offset 37, "
330 + "error-info: Identifier may not be empty]]", ex.getMessage());
331 final var errors = ex.getErrors();
332 assertEquals(1, errors.size());
333 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
334 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
335 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
339 * Negative test of validating identifier when there are multiple slashes after list key values without next
340 * identifier. Test is expected to fail with <code>RestconfDocumentedException</code>.
343 public void validArgIdentifierListEndsWithSlashesNegativeTest() {
344 final var ex = assertThrows(RestconfDocumentedException.class,
345 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key=value//"));
346 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
347 + "error-message: Invalid path 'deserializer-test:list-one-key=value//' at offset 37, "
348 + "error-info: Identifier may not be empty]]", ex.getMessage());
349 final var errors = ex.getErrors();
350 assertEquals(1, errors.size());
351 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
352 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
353 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
357 * Negative test of creating <code>QName</code> when identifier is empty (example: '/'). Test is expected to fail
358 * with <code>RestconfDocumentedException</code>.
361 public void prepareQnameEmptyIdentifierNegativeTest() {
362 final var ex = assertThrows(RestconfDocumentedException.class,
363 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "/"));
364 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
365 + "error-message: Invalid path '/' at offset 0, "
366 + "error-info: Identifier may not be empty]]", ex.getMessage());
367 final var errors = ex.getErrors();
368 assertEquals(1, errors.size());
369 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
370 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
371 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
375 * Negative test of creating <code>QName</code> when in identifier there is another sign than colon or equals.
376 * Test is expected to fail with <code>RestconfDocumentedException</code>.
379 public void prepareQnameBuildPathNegativeTest() {
380 final var ex = assertThrows(RestconfDocumentedException.class,
381 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test*contA"));
382 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
383 + "error-message: Invalid path 'deserializer-test*contA' at offset 17, "
384 + "error-info: Expecting [a-zA-Z_.-], not '*']]", ex.getMessage());
385 final var errors = ex.getErrors();
386 assertEquals(1, errors.size());
387 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
388 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
389 assertEquals("Expecting [a-zA-Z_.-], not '*'", errors.get(0).getErrorInfo());
393 * Negative test of creating <code>QName</code> when it is not possible to find module for specified prefix. Test is
394 * expected to fail with <code>RestconfDocumentedException</code>.
397 public void prepareQnameNotExistingPrefixNegativeTest() {
398 final var ex = assertThrows(RestconfDocumentedException.class,
399 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "not-existing:contA"));
400 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: unknown-element, "
401 + "error-message: Failed to lookup for module with name 'not-existing'.]]", ex.getMessage());
402 final var errors = ex.getErrors();
403 assertEquals(1, errors.size());
404 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
405 assertEquals(ErrorTag.UNKNOWN_ELEMENT, errors.get(0).getErrorTag());
409 * Negative test of creating <code>QName</code> when after prefix and colon there is not parsable identifier as
410 * local name. Test is expected to fail with <code>RestconfDocumentedException</code>.
413 public void prepareQnameNotValidPrefixAndLocalNameNegativeTest() {
414 final var ex = assertThrows(RestconfDocumentedException.class, () ->
415 YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:*not-parsable-identifier"));
416 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
417 + "error-message: Invalid path 'deserializer-test:*not-parsable-identifier' at offset 18, "
418 + "error-info: Expecting [a-zA-Z_], not '*']]", ex.getMessage());
419 final var errors = ex.getErrors();
420 assertEquals(1, errors.size());
421 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
422 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
423 assertEquals("Expecting [a-zA-Z_], not '*'", errors.get(0).getErrorInfo());
427 * Negative test of creating <code>QName</code> when data ends after prefix and colon. Test is expected to fail
428 * with <code>StringIndexOutOfBoundsException</code>.
431 public void prepareQnameErrorParsingNegativeTest() {
432 final var ex = assertThrows(RestconfDocumentedException.class,
433 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:"));
434 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
435 + "error-message: Invalid path 'deserializer-test:' at offset 18, "
436 + "error-info: Identifier may not be empty]]", ex.getMessage());
437 final var errors = ex.getErrors();
438 assertEquals(1, errors.size());
439 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
440 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
441 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
445 * Negative test of creating <code>QName</code> when after identifier and colon there is node name of unknown
446 * node in current container. Test is expected to fail with <code>RestconfDocumentedException</code> and error
447 * type, error tag and error status code are compared to expected values.
450 public void prepareQnameNotValidContainerNameNegativeTest() {
451 final var ex = assertThrows(RestconfDocumentedException.class,
452 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leafB"));
453 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: data-missing, "
454 + "error-message: Schema for '(deserializer:test?revision=2016-06-06)leafB' not found]]", ex.getMessage());
455 final var errors = ex.getErrors();
456 assertEquals(1, errors.size());
457 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
458 assertEquals(ErrorTag.DATA_MISSING, errors.get(0).getErrorTag());
462 * Negative test of creating <code>QName</code> when after identifier and equals there is node name of unknown
463 * node in current list. Test is expected to fail with <code>RestconfDocumentedException</code> and error
464 * type, error tag and error status code are compared to expected values.
467 public void prepareQnameNotValidListNameNegativeTest() {
468 final var ex = assertThrows(RestconfDocumentedException.class,
469 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
470 "deserializer-test:list-no-key/disabled=false"));
471 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
472 assertEquals(ErrorTag.DATA_MISSING, ex.getErrors().get(0).getErrorTag());
476 * Negative test of getting next identifier when current node is keyed entry. Test is expected to
477 * fail with <code>RestconfDocumentedException</code>.
480 public void prepareIdentifierNotKeyedEntryNegativeTest() {
481 final var ex = assertThrows(RestconfDocumentedException.class,
482 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key"));
483 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: missing-attribute, "
484 + "error-message: Entry '(deserializer:test?revision=2016-06-06)list-one-key' requires key or value "
485 + "predicate to be present.]]", ex.getMessage());
486 final var errors = ex.getErrors();
487 assertEquals(1, errors.size());
488 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
489 assertEquals(ErrorTag.MISSING_ATTRIBUTE, errors.get(0).getErrorTag());
493 * Negative test when there is a comma also after the last key. Test is expected to fail with
494 * <code>RestconfDocumentedException</code>. Last comma indicates a fourth key, which is a mismatch with schema.
497 public void deserializeKeysEndsWithCommaTooManyNegativeTest() {
498 final var ex = assertThrows(RestconfDocumentedException.class,
499 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
500 "deserializer-test:list-multiple-keys=value,100,false,"));
501 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: unknown-attribute, "
502 + "error-message: Schema for (deserializer:test?revision=2016-06-06)list-multiple-keys "
503 + "requires 3 key values, 4 supplied]]", ex.getMessage());
504 final var errors = ex.getErrors();
505 assertEquals(1, errors.size());
506 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
507 assertEquals(ErrorTag.UNKNOWN_ATTRIBUTE, errors.get(0).getErrorTag());
511 * Negative test when there is a comma also after the last key. Test is expected to fail with
512 * <code>RestconfDocumentedException</code>. Last comma indicates a third key, whose is a mismatch with schema.
515 public void deserializeKeysEndsWithCommaIllegalNegativeTest() {
516 final var ex = assertThrows(RestconfDocumentedException.class,
517 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
518 "deserializer-test:list-multiple-keys=value,100,"));
519 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: invalid-value, "
520 + "error-message: Invalid value '' for (deserializer:test?revision=2016-06-06)enabled, "
521 + "error-info: Invalid value '' for boolean type. Allowed values are 'true' and 'false']]",
523 final var errors = ex.getErrors();
524 assertEquals(1, errors.size());
525 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
526 assertEquals(ErrorTag.INVALID_VALUE, errors.get(0).getErrorTag());
527 assertEquals("Invalid value '' for boolean type. Allowed values are 'true' and 'false'",
528 errors.get(0).getErrorInfo());
532 * Positive when not all keys of list are encoded. The missing keys should be considered to has empty
533 * <code>String</code> values. Also value of next leaf must not be considered to be missing key value.
536 public void notAllListKeysEncodedPositiveTest() {
537 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
538 final Map<QName, Object> values = ImmutableMap.of(
539 QName.create(list, "name"), ":foo",
540 QName.create(list, "number"), Uint8.ONE,
541 QName.create(list, "enabled"), false);
543 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
544 "deserializer-test:list-multiple-keys=%3Afoo,1,false/string-value").path.getPathArguments();
545 assertEquals(3, result.size());
547 assertEquals(NodeIdentifier.create(list), result.get(0));
548 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
550 assertEquals(new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "string-value")),
555 * Negative test when not all keys of list are encoded and it is not possible to consider missing keys to be empty.
556 * Test is expected to fail with <code>RestconfDocumentedException</code> and error type, error tag and error
557 * status code are compared to expected values.
560 public void notAllListKeysEncodedNegativeTest() {
561 final var ex = assertThrows(RestconfDocumentedException.class,
562 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
563 "deserializer-test:list-multiple-keys=%3Afoo/string-value"));
564 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
565 assertEquals(ErrorTag.MISSING_ATTRIBUTE, ex.getErrors().get(0).getErrorTag());
569 * Test URI with list where key value starts with, ends with or contains percent encoded characters.The encoded
570 * value should be complete also with not percent-encoded parts.
573 public void percentEncodedKeyEndsWithNoPercentEncodedChars() {
574 final String URI = "deserializer-test:list-multiple-keys=%3Afoo,1,true";
575 final YangInstanceIdentifier result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, URI).path;
577 final Iterator<Entry<QName, Object>> resultListKeys =
578 ((NodeIdentifierWithPredicates)result.getLastPathArgument()).entrySet().iterator();
580 assertEquals(":foo", resultListKeys.next().getValue());
581 assertEquals(Uint8.ONE, resultListKeys.next().getValue());
582 assertEquals(true, resultListKeys.next().getValue());
586 * Positive test when all keys of list can be considered to be empty <code>String</code>.
589 public void deserializeAllKeysEmptyTest() {
590 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
591 final Map<QName, Object> values = ImmutableMap.of(
592 QName.create(list, "name"), "",
593 QName.create(list, "number"), Uint8.ZERO,
594 QName.create(list, "enabled"), true);
596 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
597 "deserializer-test:list-multiple-keys=,0,true").path.getPathArguments();
598 assertEquals(2, result.size());
599 assertEquals(NodeIdentifier.create(list), result.get(0));
600 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
604 * Negative test of deserialization when for leaf list there is no specified instance value.
605 * <code>RestconfDocumentedException</code> is expected and error type, error tag and error status code are
606 * compared to expected values.
609 public void leafListMissingKeyNegativeTest() {
610 final var ex = assertThrows(RestconfDocumentedException.class,
611 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:leaf-list-0="));
612 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
613 assertEquals(ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag());
617 * Positive test of deserialization when parts of input URI <code>String</code> are defined in another module.
620 public void deserializePartInOtherModuleTest() {
621 final List<PathArgument> result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
622 "deserializer-test-included:augmented-list=100/deserializer-test:augmented-leaf")
623 .path.getPathArguments();
624 assertEquals(4, result.size());
627 final QName list = QName.create("deserializer:test:included", "2016-06-06", "augmented-list");
628 assertEquals(NodeIdentifier.create(list), result.get(0));
629 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint16.valueOf(100)),
633 final QName augLeaf = QName.create("deserializer:test", "2016-06-06", "augmented-leaf");
634 final QName augList = QName.create("deserializer:test", "2016-06-06", "augmenting-list");
635 assertEquals(new AugmentationIdentifier(Set.of(augLeaf, augList)), result.get(2));
636 assertEquals(NodeIdentifier.create(augLeaf), result.get(3));
640 public void deserializeListInOtherModuleTest() {
641 final List<PathArgument> result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
642 "deserializer-test-included:augmented-list=100/deserializer-test:augmenting-list=0")
643 .path.getPathArguments();
644 assertEquals(5, result.size());
647 final QName list = QName.create("deserializer:test:included", "2016-06-06", "augmented-list");
648 assertEquals(NodeIdentifier.create(list), result.get(0));
649 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint16.valueOf(100)),
653 final QName augLeaf = QName.create("deserializer:test", "2016-06-06", "augmented-leaf");
654 final QName augList = QName.create("deserializer:test", "2016-06-06", "augmenting-list");
655 assertEquals(new AugmentationIdentifier(Set.of(augLeaf, augList)), result.get(2));
656 assertEquals(NodeIdentifier.create(augList), result.get(3));
657 assertEquals(NodeIdentifierWithPredicates.of(augList, QName.create(augList, "id"), 0), result.get(4));
661 * Deserialization of path that contains list entry with key which value is described by leaflef to identityref.
664 public void deserializePathWithIdentityrefKeyValueTest() {
665 assertIdentityrefKeyValue(
666 "deserializer-test-included:refs/list-with-identityref=deserializer-test%3Aderived-identity/foo");
670 * Identityref key value is not encoded correctly - ':' character must be encoded as '%3A'.
673 public void deserializePathWithInvalidIdentityrefKeyValueTest() {
674 assertIdentityrefKeyValue(
675 "deserializer-test-included:refs/list-with-identityref=deserializer-test:derived-identity/foo");
678 private static void assertIdentityrefKeyValue(final String path) {
679 final var pathArgs = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, path).path.getPathArguments();
680 assertEquals(4, pathArgs.size());
682 assertEquals("refs", pathArgs.get(0).getNodeType().getLocalName());
683 assertEquals("list-with-identityref", pathArgs.get(1).getNodeType().getLocalName());
685 final PathArgument listEntryArg = pathArgs.get(2);
686 assertThat(listEntryArg, instanceOf(NodeIdentifierWithPredicates.class));
687 assertEquals("list-with-identityref", listEntryArg.getNodeType().getLocalName());
688 final Set<QName> keys = ((NodeIdentifierWithPredicates) listEntryArg).keySet();
689 assertEquals(1, keys.size());
690 assertEquals("id", keys.iterator().next().getLocalName());
691 final Object keyValue = ((NodeIdentifierWithPredicates) listEntryArg).values().iterator().next();
692 assertEquals(QName.create("deserializer:test", "derived-identity", Revision.of("2016-06-06")), keyValue);
694 assertEquals("foo", pathArgs.get(3).getNodeType().getLocalName());