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.text.ParseException;
19 import java.util.Iterator;
20 import java.util.List;
22 import java.util.Map.Entry;
24 import org.junit.AfterClass;
25 import org.junit.BeforeClass;
26 import org.junit.Test;
27 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
28 import org.opendaylight.restconf.nb.rfc8040.ApiPath;
29 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
30 import org.opendaylight.yangtools.yang.common.ErrorTag;
31 import org.opendaylight.yangtools.yang.common.ErrorType;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.common.Revision;
34 import org.opendaylight.yangtools.yang.common.Uint16;
35 import org.opendaylight.yangtools.yang.common.Uint8;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
38 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
42 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
43 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
46 * Unit tests for {@link YangInstanceIdentifierDeserializer}.
48 public class YangInstanceIdentifierDeserializerTest {
50 private static EffectiveModelContext SCHEMA_CONTEXT;
53 public static void beforeClass() throws FileNotFoundException {
55 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/restconf/parser/deserializer"));
59 public static void afterClass() {
60 SCHEMA_CONTEXT = null;
64 * Test of deserialization <code>String</code> URI with container to
65 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
68 public void deserializeContainerTest() {
69 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA");
70 assertEquals(1, result.size());
71 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
75 * Test of deserialization <code>String</code> URI with container containing leaf to
76 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
79 public void deserializeContainerWithLeafTest() {
80 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leaf-A");
81 assertEquals(2, result.size());
82 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
83 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "leaf-A")), result.get(1));
87 * Test of deserialization <code>String</code> URI with container containing list with leaf list to
88 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
91 public void deserializeContainerWithListWithLeafListTest() {
92 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
93 "deserializer-test:contA/list-A=100/leaf-list-AA=instance");
94 assertEquals(5, result.size());
97 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
99 final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
100 assertEquals(NodeIdentifier.create(list), result.get(1));
101 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint8.valueOf(100)),
104 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-AA");
105 assertEquals(NodeIdentifier.create(leafList), result.get(3));
106 assertEquals(new NodeWithValue<>(leafList, "instance"), result.get(4));
110 * Test of deserialization <code>String</code> URI with container containing list with Action to
111 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
114 public void deserializeContainerWithListWithActionTest() {
115 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
116 "example-actions:interfaces/interface=eth0/reset");
117 assertEquals(4, result.size());
119 assertEquals(NodeIdentifier.create(
120 QName.create("https://example.com/ns/example-actions", "2016-07-07", "interfaces")), result.get(0));
122 final QName list = QName.create("https://example.com/ns/example-actions", "2016-07-07", "interface");
123 assertEquals(NodeIdentifier.create(list), result.get(1));
124 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "eth0"), result.get(2));
126 assertEquals(NodeIdentifier.create(
127 QName.create("https://example.com/ns/example-actions", "2016-07-07", "reset")), result.get(3));
131 * Test of deserialization <code>String</code> URI containing list with no keys to
132 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
135 public void deserializeListWithNoKeysTest() {
136 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-no-key");
137 assertEquals(2, result.size());
138 final QName list = QName.create("deserializer:test", "2016-06-06", "list-no-key");
139 assertEquals(NodeIdentifier.create(list), result.get(0));
140 assertEquals(NodeIdentifier.create(list), result.get(1));
144 * Test of deserialization <code>String</code> URI containing list with one key to
145 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
148 public void deserializeListWithOneKeyTest() {
149 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
150 "deserializer-test:list-one-key=value");
151 assertEquals(2, result.size());
152 final QName list = QName.create("deserializer:test", "2016-06-06", "list-one-key");
153 assertEquals(NodeIdentifier.create(list), result.get(0));
154 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "value"), result.get(1));
158 * Test of deserialization <code>String</code> URI containing list with multiple keys to
159 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
162 public void deserializeListWithMultipleKeysTest() {
163 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
164 final Map<QName, Object> values = ImmutableMap.of(
165 QName.create(list, "name"), "value",
166 QName.create(list, "number"), Uint8.valueOf(100),
167 QName.create(list, "enabled"), false);
169 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
170 "deserializer-test:list-multiple-keys=value,100,false");
171 assertEquals(2, result.size());
172 assertEquals(NodeIdentifier.create(list), result.get(0));
173 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
177 * Test of deserialization <code>String</code> URI containing leaf list to
178 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
181 public void deserializeLeafListTest() {
182 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
183 "deserializer-test:leaf-list-0=true");
184 assertEquals(2, result.size());
186 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-0");
187 assertEquals(new NodeIdentifier(leafList), result.get(0));
188 assertEquals(new NodeWithValue<>(leafList, true), result.get(1));
192 * Test when empty <code>String</code> is supplied as an input. Test is expected to return empty result.
195 public void deserializeEmptyDataTest() {
196 assertEquals(List.of(), YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, ""));
200 * Test of deserialization <code>String</code> URI with identifiers separated by multiple slashes to
201 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
204 public void deserializeMultipleSlashesTest() throws ParseException {
205 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
206 ApiPath.parseUrl("deserializer-test:contA////list-A=40//list-key"));
207 assertEquals(4, result.size());
210 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
212 final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
213 assertEquals(NodeIdentifier.create(list), result.get(1));
214 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint8.valueOf(40)),
217 assertEquals(new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "list-key")), result.get(3));
221 * Negative test when supplied <code>SchemaContext</code> is null. Test is expected to fail with
222 * <code>NullPointerException</code>.
225 public void deserializeNullSchemaContextNegativeTest() {
226 assertThrows(NullPointerException.class,
227 () -> YangInstanceIdentifierDeserializer.create(null, "deserializer-test:contA"));
231 * Negative test when supplied <code>String</code> data to deserialize is null. Test is expected to fail with
232 * <code>NullPointerException</code>.
235 public void nullDataNegativeNegativeTest() {
236 assertThrows(NullPointerException.class,
237 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, (String) null));
241 * Negative test when identifier is not followed by slash or equals. Test is expected to fail with
242 * <code>RestconfDocumentedException</code>.
245 public void deserializeBadCharMissingSlashOrEqualNegativeTest() {
246 final var ex = assertThrows(RestconfDocumentedException.class,
247 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:cont*leaf-A"));
248 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
249 + "error-message: Invalid path 'deserializer-test:cont*leaf-A' at offset 22, "
250 + "error-info: Expecting [a-zA-Z_.-], not '*']]", ex.getMessage());
251 final var errors = ex.getErrors();
252 assertEquals(1, errors.size());
253 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
254 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
255 assertEquals("Expecting [a-zA-Z_.-], not '*'", errors.get(0).getErrorInfo());
259 * Negative test of validating identifier when there is a slash after container without next identifier. Test
260 * is expected to fail with <code>RestconfDocumentedException</code>.
263 public void validArgIdentifierContainerEndsWithSlashNegativeTest() {
264 final var ex = assertThrows(RestconfDocumentedException.class,
265 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/"));
266 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
267 + "error-message: Invalid path 'deserializer-test:contA/' at offset 24, "
268 + "error-info: Identifier may not be empty]]", ex.getMessage());
269 final var errors = ex.getErrors();
270 assertEquals(1, errors.size());
271 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
272 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
273 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
277 * Negative test of validating identifier when there are multiple slashes after container without next identifier.
278 * Test is expected to fail with <code>RestconfDocumentedException</code>.
281 public void validArgIdentifierContainerEndsWithMultipleSlashesNegativeTest() {
282 final var ex = assertThrows(RestconfDocumentedException.class,
283 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA///"));
284 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
285 + "error-message: Invalid path 'deserializer-test:contA///' at offset 24, "
286 + "error-info: Identifier may not be empty]]", ex.getMessage());
287 final var errors = ex.getErrors();
288 assertEquals(1, errors.size());
289 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
290 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
291 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
295 * Negative test of validating identifier when there is a slash after list key values without next identifier. Test
296 * is expected to fail with <code>RestconfDocumentedException</code>.
299 public void validArgIdentifierListEndsWithSlashLNegativeTest() {
300 final var ex = assertThrows(RestconfDocumentedException.class,
301 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key=value/"));
302 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
303 + "error-message: Invalid path 'deserializer-test:list-one-key=value/' at offset 37, "
304 + "error-info: Identifier may not be empty]]", ex.getMessage());
305 final var errors = ex.getErrors();
306 assertEquals(1, errors.size());
307 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
308 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
309 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
313 * Negative test of validating identifier when there are multiple slashes after list key values without next
314 * identifier. Test is expected to fail with <code>RestconfDocumentedException</code>.
317 public void validArgIdentifierListEndsWithSlashesNegativeTest() {
318 final var ex = assertThrows(RestconfDocumentedException.class,
319 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key=value//"));
320 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
321 + "error-message: Invalid path 'deserializer-test:list-one-key=value//' at offset 37, "
322 + "error-info: Identifier may not be empty]]", ex.getMessage());
323 final var errors = ex.getErrors();
324 assertEquals(1, errors.size());
325 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
326 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
327 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
331 * Negative test of creating <code>QName</code> when identifier is empty (example: '/'). Test is expected to fail
332 * with <code>RestconfDocumentedException</code>.
335 public void prepareQnameEmptyIdentifierNegativeTest() {
336 final var ex = assertThrows(RestconfDocumentedException.class,
337 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "/"));
338 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
339 + "error-message: Invalid path '/' at offset 0, "
340 + "error-info: Identifier may not be empty]]", ex.getMessage());
341 final var errors = ex.getErrors();
342 assertEquals(1, errors.size());
343 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
344 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
345 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
349 * Negative test of creating <code>QName</code> when in identifier there is another sign than colon or equals.
350 * Test is expected to fail with <code>RestconfDocumentedException</code>.
353 public void prepareQnameBuildPathNegativeTest() {
354 final var ex = assertThrows(RestconfDocumentedException.class,
355 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test*contA"));
356 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
357 + "error-message: Invalid path 'deserializer-test*contA' at offset 17, "
358 + "error-info: Expecting [a-zA-Z_.-], not '*']]", ex.getMessage());
359 final var errors = ex.getErrors();
360 assertEquals(1, errors.size());
361 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
362 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
363 assertEquals("Expecting [a-zA-Z_.-], not '*'", errors.get(0).getErrorInfo());
367 * Negative test of creating <code>QName</code> when it is not possible to find module for specified prefix. Test is
368 * expected to fail with <code>RestconfDocumentedException</code>.
371 public void prepareQnameNotExistingPrefixNegativeTest() {
372 final var ex = assertThrows(RestconfDocumentedException.class,
373 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "not-existing:contA"));
374 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: unknown-element, "
375 + "error-message: Failed to lookup for module with name 'not-existing'.]]", ex.getMessage());
376 final var errors = ex.getErrors();
377 assertEquals(1, errors.size());
378 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
379 assertEquals(ErrorTag.UNKNOWN_ELEMENT, errors.get(0).getErrorTag());
383 * Negative test of creating <code>QName</code> when after prefix and colon there is not parsable identifier as
384 * local name. Test is expected to fail with <code>RestconfDocumentedException</code>.
387 public void prepareQnameNotValidPrefixAndLocalNameNegativeTest() {
388 final var ex = assertThrows(RestconfDocumentedException.class, () ->
389 YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:*not-parsable-identifier"));
390 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
391 + "error-message: Invalid path 'deserializer-test:*not-parsable-identifier' at offset 18, "
392 + "error-info: Expecting [a-zA-Z_], not '*']]", ex.getMessage());
393 final var errors = ex.getErrors();
394 assertEquals(1, errors.size());
395 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
396 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
397 assertEquals("Expecting [a-zA-Z_], not '*'", errors.get(0).getErrorInfo());
401 * Negative test of creating <code>QName</code> when data ends after prefix and colon. Test is expected to fail
402 * with <code>StringIndexOutOfBoundsException</code>.
405 public void prepareQnameErrorParsingNegativeTest() {
406 final var ex = assertThrows(RestconfDocumentedException.class,
407 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:"));
408 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
409 + "error-message: Invalid path 'deserializer-test:' at offset 18, "
410 + "error-info: Identifier may not be empty]]", ex.getMessage());
411 final var errors = ex.getErrors();
412 assertEquals(1, errors.size());
413 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
414 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
415 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
419 * Negative test of creating <code>QName</code> when after identifier and colon there is node name of unknown
420 * node in current container. 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 prepareQnameNotValidContainerNameNegativeTest() {
425 final var ex = assertThrows(RestconfDocumentedException.class,
426 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leafB"));
427 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: data-missing, "
428 + "error-message: Schema for '(deserializer:test?revision=2016-06-06)leafB' not found]]", ex.getMessage());
429 final var errors = ex.getErrors();
430 assertEquals(1, errors.size());
431 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
432 assertEquals(ErrorTag.DATA_MISSING, errors.get(0).getErrorTag());
436 * Negative test of creating <code>QName</code> when after identifier and equals there is node name of unknown
437 * node in current list. Test is expected to fail with <code>RestconfDocumentedException</code> and error
438 * type, error tag and error status code are compared to expected values.
441 public void prepareQnameNotValidListNameNegativeTest() {
442 final var ex = assertThrows(RestconfDocumentedException.class,
443 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
444 "deserializer-test:list-no-key/disabled=false"));
445 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
446 assertEquals(ErrorTag.DATA_MISSING, ex.getErrors().get(0).getErrorTag());
450 * Negative test of getting next identifier when current node is keyed entry. Test is expected to
451 * fail with <code>RestconfDocumentedException</code>.
454 public void prepareIdentifierNotKeyedEntryNegativeTest() {
455 final var ex = assertThrows(RestconfDocumentedException.class,
456 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key"));
457 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: missing-attribute, "
458 + "error-message: Entry '(deserializer:test?revision=2016-06-06)list-one-key' requires key or value "
459 + "predicate to be present.]]", ex.getMessage());
460 final var errors = ex.getErrors();
461 assertEquals(1, errors.size());
462 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
463 assertEquals(ErrorTag.MISSING_ATTRIBUTE, errors.get(0).getErrorTag());
467 * Negative test when there is a comma also after the last key. Test is expected to fail with
468 * <code>RestconfDocumentedException</code>. Last comma indicates a fourth key, which is a mismatch with schema.
471 public void deserializeKeysEndsWithCommaTooManyNegativeTest() {
472 final var ex = assertThrows(RestconfDocumentedException.class,
473 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
474 "deserializer-test:list-multiple-keys=value,100,false,"));
475 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: unknown-attribute, "
476 + "error-message: Schema for (deserializer:test?revision=2016-06-06)list-multiple-keys "
477 + "requires 3 key values, 4 supplied]]", ex.getMessage());
478 final var errors = ex.getErrors();
479 assertEquals(1, errors.size());
480 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
481 assertEquals(ErrorTag.UNKNOWN_ATTRIBUTE, errors.get(0).getErrorTag());
485 * Negative test when there is a comma also after the last key. Test is expected to fail with
486 * <code>RestconfDocumentedException</code>. Last comma indicates a third key, whose is a mismatch with schema.
489 public void deserializeKeysEndsWithCommaIllegalNegativeTest() {
490 final var ex = assertThrows(RestconfDocumentedException.class,
491 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
492 "deserializer-test:list-multiple-keys=value,100,"));
493 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: invalid-value, "
494 + "error-message: Invalid value '' for (deserializer:test?revision=2016-06-06)enabled, "
495 + "error-info: Invalid value '' for boolean type. Allowed values are 'true' and 'false']]",
497 final var errors = ex.getErrors();
498 assertEquals(1, errors.size());
499 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
500 assertEquals(ErrorTag.INVALID_VALUE, errors.get(0).getErrorTag());
501 assertEquals("Invalid value '' for boolean type. Allowed values are 'true' and 'false'",
502 errors.get(0).getErrorInfo());
506 * Positive when not all keys of list are encoded. The missing keys should be considered to has empty
507 * <code>String</code> values. Also value of next leaf must not be considered to be missing key value.
510 public void notAllListKeysEncodedPositiveTest() {
511 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
512 final Map<QName, Object> values = ImmutableMap.of(
513 QName.create(list, "name"), ":foo",
514 QName.create(list, "number"), Uint8.ONE,
515 QName.create(list, "enabled"), false);
517 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
518 "deserializer-test:list-multiple-keys=%3Afoo,1,false/string-value");
519 assertEquals(3, result.size());
521 assertEquals(NodeIdentifier.create(list), result.get(0));
522 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
524 assertEquals(new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "string-value")),
529 * Negative test when not all keys of list are encoded and it is not possible to consider missing keys to be empty.
530 * Test is expected to fail with <code>RestconfDocumentedException</code> and error type, error tag and error
531 * status code are compared to expected values.
534 public void notAllListKeysEncodedNegativeTest() {
535 final var ex = assertThrows(RestconfDocumentedException.class,
536 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
537 "deserializer-test:list-multiple-keys=%3Afoo/string-value"));
538 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
539 assertEquals(ErrorTag.MISSING_ATTRIBUTE, ex.getErrors().get(0).getErrorTag());
543 * Test URI with list where key value starts with, ends with or contains percent encoded characters.The encoded
544 * value should be complete also with not percent-encoded parts.
547 public void percentEncodedKeyEndsWithNoPercentEncodedChars() {
548 final String URI = "deserializer-test:list-multiple-keys=%3Afoo,1,true";
549 final YangInstanceIdentifier result = YangInstanceIdentifier.create(
550 YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, URI));
552 final Iterator<Entry<QName, Object>> resultListKeys =
553 ((NodeIdentifierWithPredicates)result.getLastPathArgument()).entrySet().iterator();
555 assertEquals(":foo", resultListKeys.next().getValue());
556 assertEquals(Uint8.ONE, resultListKeys.next().getValue());
557 assertEquals(true, resultListKeys.next().getValue());
561 * Positive test when all keys of list can be considered to be empty <code>String</code>.
564 public void deserializeAllKeysEmptyTest() {
565 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
566 final Map<QName, Object> values = ImmutableMap.of(
567 QName.create(list, "name"), "",
568 QName.create(list, "number"), Uint8.ZERO,
569 QName.create(list, "enabled"), true);
571 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
572 "deserializer-test:list-multiple-keys=,0,true");
573 assertEquals(2, result.size());
574 assertEquals(NodeIdentifier.create(list), result.get(0));
575 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
579 * Negative test of deserialization when for leaf list there is no specified instance value.
580 * <code>RestconfDocumentedException</code> is expected and error type, error tag and error status code are
581 * compared to expected values.
584 public void leafListMissingKeyNegativeTest() {
585 final var ex = assertThrows(RestconfDocumentedException.class,
586 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:leaf-list-0="));
587 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
588 assertEquals(ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag());
592 * Positive test of deserialization when parts of input URI <code>String</code> are defined in another module.
595 public void deserializePartInOtherModuleTest() {
596 final List<PathArgument> result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
597 "deserializer-test-included:augmented-list=100/deserializer-test:augmented-leaf");
599 assertEquals(4, result.size());
601 final QName list = QName.create("deserializer:test:included", "2016-06-06", "augmented-list");
602 final QName child = QName.create("deserializer:test", "2016-06-06", "augmented-leaf");
605 assertEquals(NodeIdentifier.create(list), result.get(0));
606 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint16.valueOf(100)),
610 assertEquals(new AugmentationIdentifier(Set.of(child)), result.get(2));
611 assertEquals(NodeIdentifier.create(child), result.get(3));
615 * Deserialization of path that contains list entry with key which value is described by leaflef to identityref.
618 public void deserializePathWithIdentityrefKeyValueTest() {
619 assertIdentityrefKeyValue(
620 "deserializer-test-included:refs/list-with-identityref=deserializer-test%3Aderived-identity/foo");
624 * Identityref key value is not encoded correctly - ':' character must be encoded as '%3A'.
627 public void deserializePathWithInvalidIdentityrefKeyValueTest() {
628 assertIdentityrefKeyValue(
629 "deserializer-test-included:refs/list-with-identityref=deserializer-test:derived-identity/foo");
632 private static void assertIdentityrefKeyValue(final String path) {
633 final var pathArgs = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, path);
634 assertEquals(4, pathArgs.size());
636 assertEquals("refs", pathArgs.get(0).getNodeType().getLocalName());
637 assertEquals("list-with-identityref", pathArgs.get(1).getNodeType().getLocalName());
639 final PathArgument listEntryArg = pathArgs.get(2);
640 assertThat(listEntryArg, instanceOf(NodeIdentifierWithPredicates.class));
641 assertEquals("list-with-identityref", listEntryArg.getNodeType().getLocalName());
642 final Set<QName> keys = ((NodeIdentifierWithPredicates) listEntryArg).keySet();
643 assertEquals(1, keys.size());
644 assertEquals("id", keys.iterator().next().getLocalName());
645 final Object keyValue = ((NodeIdentifierWithPredicates) listEntryArg).values().iterator().next();
646 assertEquals(QName.create("deserializer:test", "derived-identity", Revision.of("2016-06-06")), keyValue);
648 assertEquals("foo", pathArgs.get(3).getNodeType().getLocalName());