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 {
49 private static final QName ACTIONS_INTERFACES =
50 QName.create("https://example.com/ns/example-actions", "2016-07-07", "interfaces");
53 private static EffectiveModelContext SCHEMA_CONTEXT;
56 public static void beforeClass() throws FileNotFoundException {
58 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/restconf/parser/deserializer"));
62 public static void afterClass() {
63 SCHEMA_CONTEXT = null;
67 * Test of deserialization <code>String</code> URI with container to
68 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
71 public void deserializeContainerTest() {
72 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA");
73 assertEquals(1, result.size());
74 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
78 * Test of deserialization <code>String</code> URI with container containing leaf to
79 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
82 public void deserializeContainerWithLeafTest() {
83 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leaf-A");
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");
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");
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");
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");
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 assertEquals(2, result.size());
184 final QName list = QName.create("deserializer:test", "2016-06-06", "list-no-key");
185 assertEquals(NodeIdentifier.create(list), result.get(0));
186 assertEquals(NodeIdentifier.create(list), result.get(1));
190 * Test of deserialization <code>String</code> URI containing list with one key to
191 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
194 public void deserializeListWithOneKeyTest() {
195 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
196 "deserializer-test:list-one-key=value");
197 assertEquals(2, result.size());
198 final QName list = QName.create("deserializer:test", "2016-06-06", "list-one-key");
199 assertEquals(NodeIdentifier.create(list), result.get(0));
200 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "value"), result.get(1));
204 * Test of deserialization <code>String</code> URI containing list with multiple keys to
205 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
208 public void deserializeListWithMultipleKeysTest() {
209 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
210 final Map<QName, Object> values = ImmutableMap.of(
211 QName.create(list, "name"), "value",
212 QName.create(list, "number"), Uint8.valueOf(100),
213 QName.create(list, "enabled"), false);
215 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
216 "deserializer-test:list-multiple-keys=value,100,false");
217 assertEquals(2, result.size());
218 assertEquals(NodeIdentifier.create(list), result.get(0));
219 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
223 * Test of deserialization <code>String</code> URI containing leaf list to
224 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
227 public void deserializeLeafListTest() {
228 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
229 "deserializer-test:leaf-list-0=true");
230 assertEquals(2, result.size());
232 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-0");
233 assertEquals(new NodeIdentifier(leafList), result.get(0));
234 assertEquals(new NodeWithValue<>(leafList, true), result.get(1));
238 * Test when empty <code>String</code> is supplied as an input. Test is expected to return empty result.
241 public void deserializeEmptyDataTest() {
242 assertEquals(List.of(), YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, ""));
246 * Test of deserialization <code>String</code> URI with identifiers separated by multiple slashes to
247 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
250 public void deserializeMultipleSlashesTest() throws ParseException {
251 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
252 ApiPath.parseUrl("deserializer-test:contA////list-A=40//list-key"));
253 assertEquals(4, result.size());
256 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
258 final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
259 assertEquals(NodeIdentifier.create(list), result.get(1));
260 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint8.valueOf(40)),
263 assertEquals(new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "list-key")), result.get(3));
267 * Negative test when supplied <code>SchemaContext</code> is null. Test is expected to fail with
268 * <code>NullPointerException</code>.
271 public void deserializeNullSchemaContextNegativeTest() {
272 assertThrows(NullPointerException.class,
273 () -> YangInstanceIdentifierDeserializer.create(null, "deserializer-test:contA"));
277 * Negative test when supplied <code>String</code> data to deserialize is null. Test is expected to fail with
278 * <code>NullPointerException</code>.
281 public void nullDataNegativeNegativeTest() {
282 assertThrows(NullPointerException.class,
283 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, (String) null));
287 * Negative test when identifier is not followed by slash or equals. Test is expected to fail with
288 * <code>RestconfDocumentedException</code>.
291 public void deserializeBadCharMissingSlashOrEqualNegativeTest() {
292 final var ex = assertThrows(RestconfDocumentedException.class,
293 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:cont*leaf-A"));
294 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
295 + "error-message: Invalid path 'deserializer-test:cont*leaf-A' at offset 22, "
296 + "error-info: Expecting [a-zA-Z_.-], not '*']]", ex.getMessage());
297 final var errors = ex.getErrors();
298 assertEquals(1, errors.size());
299 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
300 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
301 assertEquals("Expecting [a-zA-Z_.-], not '*'", errors.get(0).getErrorInfo());
305 * Negative test of validating identifier when there is a slash after container without next identifier. Test
306 * is expected to fail with <code>RestconfDocumentedException</code>.
309 public void validArgIdentifierContainerEndsWithSlashNegativeTest() {
310 final var ex = assertThrows(RestconfDocumentedException.class,
311 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/"));
312 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
313 + "error-message: Invalid path 'deserializer-test:contA/' at offset 24, "
314 + "error-info: Identifier may not be empty]]", ex.getMessage());
315 final var errors = ex.getErrors();
316 assertEquals(1, errors.size());
317 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
318 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
319 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
323 * Negative test of validating identifier when there are multiple slashes after container without next identifier.
324 * Test is expected to fail with <code>RestconfDocumentedException</code>.
327 public void validArgIdentifierContainerEndsWithMultipleSlashesNegativeTest() {
328 final var ex = assertThrows(RestconfDocumentedException.class,
329 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA///"));
330 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
331 + "error-message: Invalid path 'deserializer-test:contA///' at offset 24, "
332 + "error-info: Identifier may not be empty]]", ex.getMessage());
333 final var errors = ex.getErrors();
334 assertEquals(1, errors.size());
335 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
336 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
337 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
341 * Negative test of validating identifier when there is a slash after list key values without next identifier. Test
342 * is expected to fail with <code>RestconfDocumentedException</code>.
345 public void validArgIdentifierListEndsWithSlashLNegativeTest() {
346 final var ex = assertThrows(RestconfDocumentedException.class,
347 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key=value/"));
348 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
349 + "error-message: Invalid path 'deserializer-test:list-one-key=value/' at offset 37, "
350 + "error-info: Identifier may not be empty]]", 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());
355 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
359 * Negative test of validating identifier when there are multiple slashes after list key values without next
360 * identifier. Test is expected to fail with <code>RestconfDocumentedException</code>.
363 public void validArgIdentifierListEndsWithSlashesNegativeTest() {
364 final var ex = assertThrows(RestconfDocumentedException.class,
365 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key=value//"));
366 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
367 + "error-message: Invalid path 'deserializer-test:list-one-key=value//' at offset 37, "
368 + "error-info: Identifier may not be empty]]", ex.getMessage());
369 final var errors = ex.getErrors();
370 assertEquals(1, errors.size());
371 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
372 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
373 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
377 * Negative test of creating <code>QName</code> when identifier is empty (example: '/'). Test is expected to fail
378 * with <code>RestconfDocumentedException</code>.
381 public void prepareQnameEmptyIdentifierNegativeTest() {
382 final var ex = assertThrows(RestconfDocumentedException.class,
383 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "/"));
384 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
385 + "error-message: Invalid path '/' at offset 0, "
386 + "error-info: Identifier may not be empty]]", ex.getMessage());
387 final var errors = ex.getErrors();
388 assertEquals(1, errors.size());
389 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
390 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
391 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
395 * Negative test of creating <code>QName</code> when in identifier there is another sign than colon or equals.
396 * Test is expected to fail with <code>RestconfDocumentedException</code>.
399 public void prepareQnameBuildPathNegativeTest() {
400 final var ex = assertThrows(RestconfDocumentedException.class,
401 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test*contA"));
402 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
403 + "error-message: Invalid path 'deserializer-test*contA' at offset 17, "
404 + "error-info: Expecting [a-zA-Z_.-], not '*']]", ex.getMessage());
405 final var errors = ex.getErrors();
406 assertEquals(1, errors.size());
407 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
408 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
409 assertEquals("Expecting [a-zA-Z_.-], not '*'", errors.get(0).getErrorInfo());
413 * Negative test of creating <code>QName</code> when it is not possible to find module for specified prefix. Test is
414 * expected to fail with <code>RestconfDocumentedException</code>.
417 public void prepareQnameNotExistingPrefixNegativeTest() {
418 final var ex = assertThrows(RestconfDocumentedException.class,
419 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "not-existing:contA"));
420 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: unknown-element, "
421 + "error-message: Failed to lookup for module with name 'not-existing'.]]", ex.getMessage());
422 final var errors = ex.getErrors();
423 assertEquals(1, errors.size());
424 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
425 assertEquals(ErrorTag.UNKNOWN_ELEMENT, errors.get(0).getErrorTag());
429 * Negative test of creating <code>QName</code> when after prefix and colon there is not parsable identifier as
430 * local name. Test is expected to fail with <code>RestconfDocumentedException</code>.
433 public void prepareQnameNotValidPrefixAndLocalNameNegativeTest() {
434 final var ex = assertThrows(RestconfDocumentedException.class, () ->
435 YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:*not-parsable-identifier"));
436 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
437 + "error-message: Invalid path 'deserializer-test:*not-parsable-identifier' at offset 18, "
438 + "error-info: Expecting [a-zA-Z_], not '*']]", ex.getMessage());
439 final var errors = ex.getErrors();
440 assertEquals(1, errors.size());
441 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
442 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
443 assertEquals("Expecting [a-zA-Z_], not '*'", errors.get(0).getErrorInfo());
447 * Negative test of creating <code>QName</code> when data ends after prefix and colon. Test is expected to fail
448 * with <code>StringIndexOutOfBoundsException</code>.
451 public void prepareQnameErrorParsingNegativeTest() {
452 final var ex = assertThrows(RestconfDocumentedException.class,
453 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:"));
454 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
455 + "error-message: Invalid path 'deserializer-test:' at offset 18, "
456 + "error-info: Identifier may not be empty]]", ex.getMessage());
457 final var errors = ex.getErrors();
458 assertEquals(1, errors.size());
459 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
460 assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
461 assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
465 * Negative test of creating <code>QName</code> when after identifier and colon there is node name of unknown
466 * node in current container. Test is expected to fail with <code>RestconfDocumentedException</code> and error
467 * type, error tag and error status code are compared to expected values.
470 public void prepareQnameNotValidContainerNameNegativeTest() {
471 final var ex = assertThrows(RestconfDocumentedException.class,
472 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leafB"));
473 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: data-missing, "
474 + "error-message: Schema for '(deserializer:test?revision=2016-06-06)leafB' not found]]", ex.getMessage());
475 final var errors = ex.getErrors();
476 assertEquals(1, errors.size());
477 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
478 assertEquals(ErrorTag.DATA_MISSING, errors.get(0).getErrorTag());
482 * Negative test of creating <code>QName</code> when after identifier and equals there is node name of unknown
483 * node in current list. Test is expected to fail with <code>RestconfDocumentedException</code> and error
484 * type, error tag and error status code are compared to expected values.
487 public void prepareQnameNotValidListNameNegativeTest() {
488 final var ex = assertThrows(RestconfDocumentedException.class,
489 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
490 "deserializer-test:list-no-key/disabled=false"));
491 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
492 assertEquals(ErrorTag.DATA_MISSING, ex.getErrors().get(0).getErrorTag());
496 * Negative test of getting next identifier when current node is keyed entry. Test is expected to
497 * fail with <code>RestconfDocumentedException</code>.
500 public void prepareIdentifierNotKeyedEntryNegativeTest() {
501 final var ex = assertThrows(RestconfDocumentedException.class,
502 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key"));
503 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: missing-attribute, "
504 + "error-message: Entry '(deserializer:test?revision=2016-06-06)list-one-key' requires key or value "
505 + "predicate to be present.]]", ex.getMessage());
506 final var errors = ex.getErrors();
507 assertEquals(1, errors.size());
508 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
509 assertEquals(ErrorTag.MISSING_ATTRIBUTE, errors.get(0).getErrorTag());
513 * Negative test when there is a comma also after the last key. Test is expected to fail with
514 * <code>RestconfDocumentedException</code>. Last comma indicates a fourth key, which is a mismatch with schema.
517 public void deserializeKeysEndsWithCommaTooManyNegativeTest() {
518 final var ex = assertThrows(RestconfDocumentedException.class,
519 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
520 "deserializer-test:list-multiple-keys=value,100,false,"));
521 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: unknown-attribute, "
522 + "error-message: Schema for (deserializer:test?revision=2016-06-06)list-multiple-keys "
523 + "requires 3 key values, 4 supplied]]", ex.getMessage());
524 final var errors = ex.getErrors();
525 assertEquals(1, errors.size());
526 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
527 assertEquals(ErrorTag.UNKNOWN_ATTRIBUTE, errors.get(0).getErrorTag());
531 * Negative test when there is a comma also after the last key. Test is expected to fail with
532 * <code>RestconfDocumentedException</code>. Last comma indicates a third key, whose is a mismatch with schema.
535 public void deserializeKeysEndsWithCommaIllegalNegativeTest() {
536 final var ex = assertThrows(RestconfDocumentedException.class,
537 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
538 "deserializer-test:list-multiple-keys=value,100,"));
539 assertEquals("errors: [RestconfError [error-type: protocol, error-tag: invalid-value, "
540 + "error-message: Invalid value '' for (deserializer:test?revision=2016-06-06)enabled, "
541 + "error-info: Invalid value '' for boolean type. Allowed values are 'true' and 'false']]",
543 final var errors = ex.getErrors();
544 assertEquals(1, errors.size());
545 assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
546 assertEquals(ErrorTag.INVALID_VALUE, errors.get(0).getErrorTag());
547 assertEquals("Invalid value '' for boolean type. Allowed values are 'true' and 'false'",
548 errors.get(0).getErrorInfo());
552 * Positive when not all keys of list are encoded. The missing keys should be considered to has empty
553 * <code>String</code> values. Also value of next leaf must not be considered to be missing key value.
556 public void notAllListKeysEncodedPositiveTest() {
557 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
558 final Map<QName, Object> values = ImmutableMap.of(
559 QName.create(list, "name"), ":foo",
560 QName.create(list, "number"), Uint8.ONE,
561 QName.create(list, "enabled"), false);
563 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
564 "deserializer-test:list-multiple-keys=%3Afoo,1,false/string-value");
565 assertEquals(3, result.size());
567 assertEquals(NodeIdentifier.create(list), result.get(0));
568 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
570 assertEquals(new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "string-value")),
575 * Negative test when not all keys of list are encoded and it is not possible to consider missing keys to be empty.
576 * Test is expected to fail with <code>RestconfDocumentedException</code> and error type, error tag and error
577 * status code are compared to expected values.
580 public void notAllListKeysEncodedNegativeTest() {
581 final var ex = assertThrows(RestconfDocumentedException.class,
582 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
583 "deserializer-test:list-multiple-keys=%3Afoo/string-value"));
584 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
585 assertEquals(ErrorTag.MISSING_ATTRIBUTE, ex.getErrors().get(0).getErrorTag());
589 * Test URI with list where key value starts with, ends with or contains percent encoded characters.The encoded
590 * value should be complete also with not percent-encoded parts.
593 public void percentEncodedKeyEndsWithNoPercentEncodedChars() {
594 final String URI = "deserializer-test:list-multiple-keys=%3Afoo,1,true";
595 final YangInstanceIdentifier result = YangInstanceIdentifier.create(
596 YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, URI));
598 final Iterator<Entry<QName, Object>> resultListKeys =
599 ((NodeIdentifierWithPredicates)result.getLastPathArgument()).entrySet().iterator();
601 assertEquals(":foo", resultListKeys.next().getValue());
602 assertEquals(Uint8.ONE, resultListKeys.next().getValue());
603 assertEquals(true, resultListKeys.next().getValue());
607 * Positive test when all keys of list can be considered to be empty <code>String</code>.
610 public void deserializeAllKeysEmptyTest() {
611 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
612 final Map<QName, Object> values = ImmutableMap.of(
613 QName.create(list, "name"), "",
614 QName.create(list, "number"), Uint8.ZERO,
615 QName.create(list, "enabled"), true);
617 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
618 "deserializer-test:list-multiple-keys=,0,true");
619 assertEquals(2, result.size());
620 assertEquals(NodeIdentifier.create(list), result.get(0));
621 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
625 * Negative test of deserialization when for leaf list there is no specified instance value.
626 * <code>RestconfDocumentedException</code> is expected and error type, error tag and error status code are
627 * compared to expected values.
630 public void leafListMissingKeyNegativeTest() {
631 final var ex = assertThrows(RestconfDocumentedException.class,
632 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:leaf-list-0="));
633 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
634 assertEquals(ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag());
638 * Positive test of deserialization when parts of input URI <code>String</code> are defined in another module.
641 public void deserializePartInOtherModuleTest() {
642 final List<PathArgument> result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
643 "deserializer-test-included:augmented-list=100/deserializer-test:augmented-leaf");
644 assertEquals(4, 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(augLeaf), result.get(3));
660 public void deserializeListInOtherModuleTest() {
661 final List<PathArgument> result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
662 "deserializer-test-included:augmented-list=100/deserializer-test:augmenting-list=0");
663 assertEquals(5, result.size());
666 final QName list = QName.create("deserializer:test:included", "2016-06-06", "augmented-list");
667 assertEquals(NodeIdentifier.create(list), result.get(0));
668 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint16.valueOf(100)),
672 final QName augLeaf = QName.create("deserializer:test", "2016-06-06", "augmented-leaf");
673 final QName augList = QName.create("deserializer:test", "2016-06-06", "augmenting-list");
674 assertEquals(new AugmentationIdentifier(Set.of(augLeaf, augList)), result.get(2));
675 assertEquals(NodeIdentifier.create(augList), result.get(3));
676 assertEquals(NodeIdentifierWithPredicates.of(augList, QName.create(augList, "id"), 0), result.get(4));
680 * Deserialization of path that contains list entry with key which value is described by leaflef to identityref.
683 public void deserializePathWithIdentityrefKeyValueTest() {
684 assertIdentityrefKeyValue(
685 "deserializer-test-included:refs/list-with-identityref=deserializer-test%3Aderived-identity/foo");
689 * Identityref key value is not encoded correctly - ':' character must be encoded as '%3A'.
692 public void deserializePathWithInvalidIdentityrefKeyValueTest() {
693 assertIdentityrefKeyValue(
694 "deserializer-test-included:refs/list-with-identityref=deserializer-test:derived-identity/foo");
697 private static void assertIdentityrefKeyValue(final String path) {
698 final var pathArgs = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, path);
699 assertEquals(4, pathArgs.size());
701 assertEquals("refs", pathArgs.get(0).getNodeType().getLocalName());
702 assertEquals("list-with-identityref", pathArgs.get(1).getNodeType().getLocalName());
704 final PathArgument listEntryArg = pathArgs.get(2);
705 assertThat(listEntryArg, instanceOf(NodeIdentifierWithPredicates.class));
706 assertEquals("list-with-identityref", listEntryArg.getNodeType().getLocalName());
707 final Set<QName> keys = ((NodeIdentifierWithPredicates) listEntryArg).keySet();
708 assertEquals(1, keys.size());
709 assertEquals("id", keys.iterator().next().getLocalName());
710 final Object keyValue = ((NodeIdentifierWithPredicates) listEntryArg).values().iterator().next();
711 assertEquals(QName.create("deserializer:test", "derived-identity", Revision.of("2016-06-06")), keyValue);
713 assertEquals("foo", pathArgs.get(3).getNodeType().getLocalName());