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 {
48 private static EffectiveModelContext SCHEMA_CONTEXT;
51 public static void beforeClass() throws FileNotFoundException {
53 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/restconf/parser/deserializer"));
57 public static void afterClass() {
58 SCHEMA_CONTEXT = null;
62 * Test of deserialization <code>String</code> URI with container to
63 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
66 public void deserializeContainerTest() {
67 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA");
68 assertEquals(1, result.size());
69 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
73 * Test of deserialization <code>String</code> URI with container containing leaf to
74 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
77 public void deserializeContainerWithLeafTest() {
78 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leaf-A");
79 assertEquals(2, result.size());
80 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
81 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "leaf-A")), result.get(1));
85 * Test of deserialization <code>String</code> URI with container containing list with leaf list to
86 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
89 public void deserializeContainerWithListWithLeafListTest() {
90 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
91 "deserializer-test:contA/list-A=100/leaf-list-AA=instance");
92 assertEquals(5, result.size());
95 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
97 final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
98 assertEquals(NodeIdentifier.create(list), result.get(1));
99 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint8.valueOf(100)),
102 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-AA");
103 assertEquals(NodeIdentifier.create(leafList), result.get(3));
104 assertEquals(new NodeWithValue<>(leafList, "instance"), result.get(4));
108 * Test of deserialization <code>String</code> URI with container containing list with Action to
109 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
112 public void deserializeContainerWithListWithActionTest() {
113 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
114 "example-actions:interfaces/interface=eth0/reset");
115 assertEquals(4, result.size());
117 assertEquals(NodeIdentifier.create(
118 QName.create("https://example.com/ns/example-actions", "2016-07-07", "interfaces")), result.get(0));
120 final QName list = QName.create("https://example.com/ns/example-actions", "2016-07-07", "interface");
121 assertEquals(NodeIdentifier.create(list), result.get(1));
122 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "eth0"), result.get(2));
124 assertEquals(NodeIdentifier.create(
125 QName.create("https://example.com/ns/example-actions", "2016-07-07", "reset")), result.get(3));
129 * Test of deserialization <code>String</code> URI containing list with no keys to
130 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
133 public void deserializeListWithNoKeysTest() {
134 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-no-key");
135 assertEquals(2, result.size());
136 final QName list = QName.create("deserializer:test", "2016-06-06", "list-no-key");
137 assertEquals(NodeIdentifier.create(list), result.get(0));
138 assertEquals(NodeIdentifier.create(list), result.get(1));
142 * Test of deserialization <code>String</code> URI containing list with one key to
143 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
146 public void deserializeListWithOneKeyTest() {
147 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
148 "deserializer-test:list-one-key=value");
149 assertEquals(2, result.size());
150 final QName list = QName.create("deserializer:test", "2016-06-06", "list-one-key");
151 assertEquals(NodeIdentifier.create(list), result.get(0));
152 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "value"), result.get(1));
156 * Test of deserialization <code>String</code> URI containing list with multiple keys to
157 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
160 public void deserializeListWithMultipleKeysTest() {
161 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
162 final Map<QName, Object> values = ImmutableMap.of(
163 QName.create(list, "name"), "value",
164 QName.create(list, "number"), Uint8.valueOf(100),
165 QName.create(list, "enabled"), false);
167 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
168 "deserializer-test:list-multiple-keys=value,100,false");
169 assertEquals(2, result.size());
170 assertEquals(NodeIdentifier.create(list), result.get(0));
171 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
175 * Test of deserialization <code>String</code> URI containing leaf list to
176 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
179 public void deserializeLeafListTest() {
180 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
181 "deserializer-test:leaf-list-0=true");
182 assertEquals(2, result.size());
184 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-0");
185 assertEquals(new NodeIdentifier(leafList), result.get(0));
186 assertEquals(new NodeWithValue<>(leafList, true), result.get(1));
190 * Test when empty <code>String</code> is supplied as an input. Test is expected to return empty result.
193 public void deserializeEmptyDataTest() {
194 assertEquals(List.of(), YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, ""));
198 * Test of deserialization <code>String</code> URI with identifiers separated by multiple slashes to
199 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
202 public void deserializeMultipleSlashesTest() {
203 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
204 "deserializer-test:contA////list-A=40//list-key");
205 assertEquals(4, result.size());
208 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
210 final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
211 assertEquals(NodeIdentifier.create(list), result.get(1));
212 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint8.valueOf(40)),
215 assertEquals(new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "list-key")), result.get(3));
219 * Negative test when supplied <code>SchemaContext</code> is null. Test is expected to fail with
220 * <code>NullPointerException</code>.
223 public void deserializeNullSchemaContextNegativeTest() {
224 assertThrows(NullPointerException.class,
225 () -> YangInstanceIdentifierDeserializer.create(null, "deserializer-test:contA"));
229 * Negative test when supplied <code>String</code> data to deserialize is null. Test is expected to fail with
230 * <code>NullPointerException</code>.
233 public void nullDataNegativeNegativeTest() {
234 assertThrows(NullPointerException.class,
235 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, null));
239 * Negative test when identifier is not followed by slash or equals. Test is expected to fail with
240 * <code>RestconfDocumentedException</code>.
243 public void deserializeBadCharMissingSlashOrEqualNegativeTest() {
244 final var ex = assertThrows(RestconfDocumentedException.class,
245 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:cont*leaf-A"));
246 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
247 + "error-message: Could not parse Instance Identifier 'deserializer-test:cont*leaf-A'. Offset: '22' : "
248 + "Reason: Bad char '*' on the current position.]]", ex.getMessage());
249 final var errors = ex.getErrors();
250 assertEquals(1, errors.size());
251 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
252 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
256 * Negative test of validating identifier when there is a slash after container without next identifier. Test
257 * is expected to fail with <code>RestconfDocumentedException</code>.
260 public void validArgIdentifierContainerEndsWithSlashNegativeTest() {
261 final var ex = assertThrows(RestconfDocumentedException.class,
262 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/"));
263 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
264 + "error-message: Could not parse Instance Identifier 'deserializer-test:contA/'. Offset: '24' : "
265 + "Reason: Identifier cannot end with '/'.]]", ex.getMessage());
266 final var errors = ex.getErrors();
267 assertEquals(1, errors.size());
268 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
269 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
273 * Negative test of validating identifier when there are multiple slashes after container without next identifier.
274 * Test is expected to fail with <code>RestconfDocumentedException</code>.
277 public void validArgIdentifierContainerEndsWithMultipleSlashesNegativeTest() {
278 final var ex = assertThrows(RestconfDocumentedException.class,
279 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA///"));
280 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
281 + "error-message: Could not parse Instance Identifier 'deserializer-test:contA///'. Offset: '26' : "
282 + "Reason: Identifier cannot end with '/'.]]", ex.getMessage());
283 final var errors = ex.getErrors();
284 assertEquals(1, errors.size());
285 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
286 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
290 * Negative test of validating identifier when there is a slash after list key values without next identifier. Test
291 * is expected to fail with <code>RestconfDocumentedException</code>.
294 public void validArgIdentifierListEndsWithSlashLNegativeTest() {
295 final var ex = assertThrows(RestconfDocumentedException.class,
296 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key=value/"));
297 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
298 + "error-message: Could not parse Instance Identifier 'deserializer-test:list-one-key=value/'. "
299 + "Offset: '37' : Reason: Identifier cannot end with '/'.]]", ex.getMessage());
300 final var errors = ex.getErrors();
301 assertEquals(1, errors.size());
302 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
303 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
307 * Negative test of validating identifier when there are multiple slashes after list key values without next
308 * identifier. Test is expected to fail with <code>RestconfDocumentedException</code>.
311 public void validArgIdentifierListEndsWithSlashesNegativeTest() {
312 final var ex = assertThrows(RestconfDocumentedException.class,
313 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key=value//"));
314 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
315 + "error-message: Could not parse Instance Identifier 'deserializer-test:list-one-key=value//'. "
316 + "Offset: '38' : Reason: Identifier cannot end with '/'.]]", ex.getMessage());
317 final var errors = ex.getErrors();
318 assertEquals(1, errors.size());
319 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
320 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
324 * Negative test of creating <code>QName</code> when identifier is empty (example: '/'). Test is expected to fail
325 * with <code>RestconfDocumentedException</code>.
328 public void prepareQnameEmptyIdentifierNegativeTest() {
329 final var ex = assertThrows(RestconfDocumentedException.class,
330 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "/"));
331 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
332 + "error-message: Could not parse Instance Identifier '/'. Offset: '0' : "
333 + "Reason: Identifier must start with character from set 'a-zA-Z_']]", ex.getMessage());
334 final var errors = ex.getErrors();
335 assertEquals(1, errors.size());
336 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
337 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
341 * Negative test of creating <code>QName</code> when in identifier there is another sign than colon or equals.
342 * Test is expected to fail with <code>RestconfDocumentedException</code>.
345 public void prepareQnameBuildPathNegativeTest() {
346 final var ex = assertThrows(RestconfDocumentedException.class,
347 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test*contA"));
348 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
349 + "error-message: Could not parse Instance Identifier 'deserializer-test*contA'. Offset: '17' : "
350 + "Reason: Bad char '*' on the current position.]]", ex.getMessage());
351 final var errors = ex.getErrors();
352 assertEquals(1, errors.size());
353 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
354 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
358 * Negative test of creating <code>QName</code> when it is not possible to find module for specified prefix. Test is
359 * expected to fail with <code>RestconfDocumentedException</code>.
362 public void prepareQnameNotExistingPrefixNegativeTest() {
363 final var ex = assertThrows(RestconfDocumentedException.class,
364 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "not-existing:contA"));
365 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: unknown-element, "
366 + "error-message: Failed to lookup for module with name 'not-existing'.]]", ex.getMessage());
367 final var errors = ex.getErrors();
368 assertEquals(1, errors.size());
369 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
370 assertEquals(ErrorTag.UNKNOWN_ELEMENT, errors.get(0).getErrorTag());
374 * Negative test of creating <code>QName</code> when after prefix and colon there is not parsable identifier as
375 * local name. Test is expected to fail with <code>RestconfDocumentedException</code>.
378 public void prepareQnameNotValidPrefixAndLocalNameNegativeTest() {
379 final var ex = assertThrows(RestconfDocumentedException.class, () ->
380 YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:*not-parsable-identifier"));
381 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
382 + "error-message: Could not parse Instance Identifier 'deserializer-test:*not-parsable-identifier'. "
383 + "Offset: '18' : Reason: Identifier must start with character from set 'a-zA-Z_']]", ex.getMessage());
384 final var errors = ex.getErrors();
385 assertEquals(1, errors.size());
386 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
387 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
391 * Negative test of creating <code>QName</code> when data ends after prefix and colon. Test is expected to fail
392 * with <code>StringIndexOutOfBoundsException</code>.
395 public void prepareQnameErrorParsingNegativeTest() {
396 // FIXME: this is just wrong
397 assertThrows(StringIndexOutOfBoundsException.class,
398 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:"));
402 * Negative test of creating <code>QName</code> when after identifier and colon there is node name of unknown
403 * node in current container. Test is expected to fail with <code>RestconfDocumentedException</code> and error
404 * type, error tag and error status code are compared to expected values.
407 public void prepareQnameNotValidContainerNameNegativeTest() {
408 final var ex = assertThrows(RestconfDocumentedException.class,
409 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leafB"));
410 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: data-missing, "
411 + "error-message: Schema node leafB does not exist in module.]]", ex.getMessage());
412 final var errors = ex.getErrors();
413 assertEquals(1, errors.size());
414 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
415 assertEquals(ErrorTag.DATA_MISSING, errors.get(0).getErrorTag());
419 * Negative test of creating <code>QName</code> when after identifier and equals there is node name of unknown
420 * node in current list. Test is expected to fail with <code>RestconfDocumentedException</code> and error
421 * type, error tag and error status code are compared to expected values.
424 public void prepareQnameNotValidListNameNegativeTest() {
425 final var ex = assertThrows(RestconfDocumentedException.class,
426 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
427 "deserializer-test:list-no-key/disabled=false"));
428 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
429 assertEquals(ErrorTag.DATA_MISSING, ex.getErrors().get(0).getErrorTag());
433 * Negative test of getting next identifier when current node is keyed entry. Test is expected to
434 * fail with <code>RestconfDocumentedException</code>.
437 public void prepareIdentifierNotKeyedEntryNegativeTest() {
438 final var ex = assertThrows(RestconfDocumentedException.class,
439 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key"));
440 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: missing-attribute, "
441 + "error-message: Could not parse Instance Identifier 'deserializer-test:list-one-key'. Offset: '30' : "
442 + "Reason: Entry '(deserializer:test?revision=2016-06-06)list-one-key' requires key or value predicate "
443 + "to be present.]]", ex.getMessage());
444 final var errors = ex.getErrors();
445 assertEquals(1, errors.size());
446 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
447 assertEquals(ErrorTag.MISSING_ATTRIBUTE, errors.get(0).getErrorTag());
451 * Negative test when there is a comma also after the last key. Test is expected to fail with
452 * <code>RestconfDocumentedException</code>.
455 public void deserializeKeysEndsWithComaNegativeTest() {
456 final var ex = assertThrows(RestconfDocumentedException.class,
457 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
458 "deserializer-test:list-multiple-keys=value,100,false,"));
459 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, error-message: "
460 + "Could not parse Instance Identifier 'deserializer-test:list-multiple-keys=value,100,false,'. "
461 + "Offset: '52' : Reason: Identifier must start with '/'.]]", ex.getMessage());
462 final var errors = ex.getErrors();
463 assertEquals(1, errors.size());
464 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
465 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
469 * Positive when not all keys of list are encoded. The missing keys should be considered to has empty
470 * <code>String</code> values. Also value of next leaf must not be considered to be missing key value.
473 public void notAllListKeysEncodedPositiveTest() {
474 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
475 final Map<QName, Object> values = ImmutableMap.of(
476 QName.create(list, "name"), ":foo",
477 QName.create(list, "number"), "",
478 QName.create(list, "enabled"), "");
480 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
481 "deserializer-test:list-multiple-keys=%3Afoo,,/string-value");
482 assertEquals(3, result.size());
484 assertEquals(NodeIdentifier.create(list), result.get(0));
485 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
487 assertEquals(new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "string-value")),
492 * Negative test when not all keys of list are encoded and it is not possible to consider missing keys to be empty.
493 * Test is expected to fail with <code>RestconfDocumentedException</code> and error type, error tag and error
494 * status code are compared to expected values.
497 public void notAllListKeysEncodedNegativeTest() {
498 final var ex = assertThrows(RestconfDocumentedException.class,
499 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
500 "deserializer-test:list-multiple-keys=%3Afoo/string-value"));
501 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
502 assertEquals(ErrorTag.MISSING_ATTRIBUTE, ex.getErrors().get(0).getErrorTag());
506 * Test URI with list where key value starts with, ends with or contains percent encoded characters.The encoded
507 * value should be complete also with not percent-encoded parts.
510 public void percentEncodedKeyEndsWithNoPercentEncodedChars() {
511 final String URI = "deserializer-test:list-multiple-keys=%3Afoo,1,true";
512 final YangInstanceIdentifier result = YangInstanceIdentifier.create(
513 YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, URI));
515 final Iterator<Entry<QName, Object>> resultListKeys =
516 ((NodeIdentifierWithPredicates)result.getLastPathArgument()).entrySet().iterator();
518 assertEquals(":foo", resultListKeys.next().getValue());
519 assertEquals(Uint8.ONE, resultListKeys.next().getValue());
520 assertEquals(true, resultListKeys.next().getValue());
524 * Positive test when all keys of list can be considered to be empty <code>String</code>.
527 public void deserializeAllKeysEmptyTest() {
528 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
529 final Map<QName, Object> values = ImmutableMap.of(
530 QName.create(list, "name"), "",
531 QName.create(list, "number"), "",
532 QName.create(list, "enabled"), "");
534 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
535 "deserializer-test:list-multiple-keys=,,");
536 assertEquals(2, result.size());
537 assertEquals(NodeIdentifier.create(list), result.get(0));
538 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
542 * Negative test of deserialization when for leaf list there is no specified instance value.
543 * <code>RestconfDocumentedException</code> is expected and error type, error tag and error status code are
544 * compared to expected values.
547 public void leafListMissingKeyNegativeTest() {
548 final var ex = assertThrows(RestconfDocumentedException.class,
549 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:leaf-list-0="));
550 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
551 assertEquals(ErrorTag.MISSING_ATTRIBUTE, ex.getErrors().get(0).getErrorTag());
555 * Positive test of deserialization when parts of input URI <code>String</code> are defined in another module.
558 public void deserializePartInOtherModuleTest() {
559 final List<PathArgument> result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
560 "deserializer-test-included:augmented-list=100/augmented-leaf");
562 assertEquals(4, result.size());
564 final QName list = QName.create("deserializer:test:included", "2016-06-06", "augmented-list");
565 final QName child = QName.create("deserializer:test", "2016-06-06", "augmented-leaf");
568 assertEquals(NodeIdentifier.create(list), result.get(0));
569 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint16.valueOf(100)),
573 assertEquals(new AugmentationIdentifier(Set.of(child)), result.get(2));
574 assertEquals(NodeIdentifier.create(child), result.get(3));
578 * Deserialization of path that contains list entry with key which value is described by leaflef to identityref.
581 public void deserializePathWithIdentityrefKeyValueTest() {
582 final var pathArgs = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
583 "deserializer-test-included:refs/list-with-identityref=deserializer-test%3Aderived-identity/foo");
584 assertEquals(4, pathArgs.size());
586 assertEquals("refs", pathArgs.get(0).getNodeType().getLocalName());
587 assertEquals("list-with-identityref", pathArgs.get(1).getNodeType().getLocalName());
589 final PathArgument listEntryArg = pathArgs.get(2);
590 assertThat(listEntryArg, instanceOf(NodeIdentifierWithPredicates.class));
591 assertEquals("list-with-identityref", listEntryArg.getNodeType().getLocalName());
592 final Set<QName> keys = ((NodeIdentifierWithPredicates) listEntryArg).keySet();
593 assertEquals(1, keys.size());
594 assertEquals("id", keys.iterator().next().getLocalName());
595 final Object keyValue = ((NodeIdentifierWithPredicates) listEntryArg).values().iterator().next();
596 assertEquals(QName.create("deserializer:test", "derived-identity", Revision.of("2016-06-06")), keyValue);
598 assertEquals("foo", pathArgs.get(3).getNodeType().getLocalName());
602 * Identityref key value is not encoded correctly - ':' character must be encoded as '%3A'.
605 public void deserializePathWithInvalidIdentityrefKeyValueTest() {
606 final var ex = assertThrows(RestconfDocumentedException.class,
607 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
608 "deserializer-test-included:refs/list-with-identityref=deserializer-test:derived-identity/foo"));
609 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: bad-element, "
610 + "error-message: Cannot decode value 'deserializer-test' for identityref type in "
611 + "(deserializer:test:included?revision=2016-06-06)list-with-identityref. Make sure reserved characters "
612 + "such as comma, single-quote, double-quote, colon, double-quote, space, and forward slash (,'\":\" /) "
613 + "are percent-encoded, for example ':' is '%3A']]", ex.getMessage());
614 final var errors = ex.getErrors();
615 assertEquals(1, errors.size());
616 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
617 assertEquals(ErrorTag.BAD_ELEMENT, errors.get(0).getErrorTag());