/*
* Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
* Copyright (c) 2021 PANTHEON.tech, s.r.o.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.restconf.nb.rfc8040.utils.parser;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableMap;
import java.io.FileNotFoundException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.common.Uint16;
import org.opendaylight.yangtools.yang.common.Uint8;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
/**
* Unit tests for {@link YangInstanceIdentifierDeserializer}.
*/
public class YangInstanceIdentifierDeserializerTest {
private static final QName ACTIONS_INTERFACES =
QName.create("https://example.com/ns/example-actions", "2016-07-07", "interfaces");
// schema context
private static EffectiveModelContext SCHEMA_CONTEXT;
@BeforeClass
public static void beforeClass() throws FileNotFoundException {
SCHEMA_CONTEXT =
YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/restconf/parser/deserializer"));
}
@AfterClass
public static void afterClass() {
SCHEMA_CONTEXT = null;
}
/**
* Test of deserialization String
URI with container to
* {@code Iterable}.
*/
@Test
public void deserializeContainerTest() {
final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA")
.path.getPathArguments();
assertEquals(1, result.size());
assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
}
/**
* Test of deserialization String
URI with container containing leaf to
* {@code Iterable}.
*/
@Test
public void deserializeContainerWithLeafTest() {
final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leaf-A")
.path.getPathArguments();
assertEquals(2, result.size());
assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "leaf-A")), result.get(1));
}
/**
* Test of deserialization String
URI with container containing list with leaf list to
* {@code Iterable}.
*/
@Test
public void deserializeContainerWithListWithLeafListTest() {
final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"deserializer-test:contA/list-A=100/leaf-list-AA=instance").path.getPathArguments();
assertEquals(5, result.size());
// container
assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
// list
final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
assertEquals(NodeIdentifier.create(list), result.get(1));
assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint8.valueOf(100)),
result.get(2));
// leaf list
final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-AA");
assertEquals(NodeIdentifier.create(leafList), result.get(3));
assertEquals(new NodeWithValue<>(leafList, "instance"), result.get(4));
}
/**
* Test of deserialization String
URI with container containing list with Action to
* {@code Iterable}.
*/
@Test
public void deserializeContainerWithListWithActionTest() {
final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"example-actions:interfaces/interface=eth0/reset").path.getPathArguments();
assertEquals(4, result.size());
// container
assertEquals(NodeIdentifier.create(ACTIONS_INTERFACES), result.get(0));
// list
final QName list = QName.create(ACTIONS_INTERFACES, "interface");
assertEquals(NodeIdentifier.create(list), result.get(1));
assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "eth0"), result.get(2));
// action
assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "reset")), result.get(3));
}
/**
* Test of deserialization String
URI with container containing choice node with Action to
* {@code Iterable}.
*/
@Test
public void deserializeContainerWithChoiceSchemaNodeWithActionTest() {
final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"example-actions:interfaces/typeA-gigabyte/interface=eth0/reboot").path.getPathArguments();
assertEquals(6, result.size());
// container
assertEquals(NodeIdentifier.create(ACTIONS_INTERFACES), result.get(0));
// choice
assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "interface-type")), result.get(1));
// container
assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "typeA-gigabyte")), result.get(2));
// list
final QName list = QName.create(ACTIONS_INTERFACES, "interface");
assertEquals(NodeIdentifier.create(list), result.get(3));
assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "eth0"), result.get(4));
// action QName
assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "reboot")), result.get(5));
}
/**
* Test of deserialization String
URI with container containing choice node with Action to
* {@code Iterable}.
*/
@Test
public void deserializeContainerWithChoiceCaseSchemaNodeWithActionTest() {
final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"example-actions:interfaces/udp/reboot").path.getPathArguments();
assertEquals(4, result.size());
// container
assertEquals(NodeIdentifier.create(ACTIONS_INTERFACES), result.get(0));
// choice
assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "protocol")), result.get(1));
// choice container
assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "udp")), result.get(2));
// action QName
assertEquals(NodeIdentifier.create(QName.create(ACTIONS_INTERFACES, "reboot")), result.get(3));
}
/**
* Test of deserialization String
URI containing list with no keys to
* {@code Iterable}.
*/
@Test
public void deserializeListWithNoKeysTest() {
final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-no-key")
.path.getPathArguments();
assertEquals(2, result.size());
final QName list = QName.create("deserializer:test", "2016-06-06", "list-no-key");
assertEquals(NodeIdentifier.create(list), result.get(0));
assertEquals(NodeIdentifier.create(list), result.get(1));
}
/**
* Test of deserialization String
URI containing list with one key to
* {@code Iterable}.
*/
@Test
public void deserializeListWithOneKeyTest() {
final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"deserializer-test:list-one-key=value").path.getPathArguments();
assertEquals(2, result.size());
final QName list = QName.create("deserializer:test", "2016-06-06", "list-one-key");
assertEquals(NodeIdentifier.create(list), result.get(0));
assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "value"), result.get(1));
}
/**
* Test of deserialization String
URI containing list with multiple keys to
* {@code Iterable}.
*/
@Test
public void deserializeListWithMultipleKeysTest() {
final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
final Map values = ImmutableMap.of(
QName.create(list, "name"), "value",
QName.create(list, "number"), Uint8.valueOf(100),
QName.create(list, "enabled"), false);
final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"deserializer-test:list-multiple-keys=value,100,false").path.getPathArguments();
assertEquals(2, result.size());
assertEquals(NodeIdentifier.create(list), result.get(0));
assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
}
/**
* Test of deserialization String
URI containing leaf list to
* {@code Iterable}.
*/
@Test
public void deserializeLeafListTest() {
final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"deserializer-test:leaf-list-0=true").path.getPathArguments();
assertEquals(2, result.size());
final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-0");
assertEquals(new NodeIdentifier(leafList), result.get(0));
assertEquals(new NodeWithValue<>(leafList, true), result.get(1));
}
/**
* Test when empty String
is supplied as an input. Test is expected to return empty result.
*/
@Test
public void deserializeEmptyDataTest() {
assertEquals(List.of(), YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "").path.getPathArguments());
}
/**
* Negative test when supplied SchemaContext
is null. Test is expected to fail with
* NullPointerException
.
*/
@Test
public void deserializeNullSchemaContextNegativeTest() {
assertThrows(NullPointerException.class,
() -> YangInstanceIdentifierDeserializer.create(null, "deserializer-test:contA"));
}
/**
* Negative test when supplied String
data to deserialize is null. Test is expected to fail with
* NullPointerException
.
*/
@Test
public void nullDataNegativeNegativeTest() {
assertThrows(NullPointerException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, (String) null));
}
/**
* Negative test when identifier is not followed by slash or equals. Test is expected to fail with
* RestconfDocumentedException
.
*/
@Test
public void deserializeBadCharMissingSlashOrEqualNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:cont*leaf-A"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
+ "error-message: Invalid path 'deserializer-test:cont*leaf-A' at offset 22, "
+ "error-info: Expecting [a-zA-Z_.-], not '*']]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
assertEquals("Expecting [a-zA-Z_.-], not '*'", errors.get(0).getErrorInfo());
}
/**
* Negative test of validating identifier when there is a slash after container without next identifier. Test
* is expected to fail with RestconfDocumentedException
.
*/
@Test
public void validArgIdentifierContainerEndsWithSlashNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
+ "error-message: Invalid path 'deserializer-test:contA/' at offset 24, "
+ "error-info: Identifier may not be empty]]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
}
/**
* Negative test of validating identifier when there are multiple slashes after container without next identifier.
* Test is expected to fail with RestconfDocumentedException
.
*/
@Test
public void validArgIdentifierContainerEndsWithMultipleSlashesNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA///"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
+ "error-message: Invalid path 'deserializer-test:contA///' at offset 24, "
+ "error-info: Identifier may not be empty]]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
}
/**
* Negative test of validating identifier when there is a slash after list key values without next identifier. Test
* is expected to fail with RestconfDocumentedException
.
*/
@Test
public void validArgIdentifierListEndsWithSlashLNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key=value/"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
+ "error-message: Invalid path 'deserializer-test:list-one-key=value/' at offset 37, "
+ "error-info: Identifier may not be empty]]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
}
/**
* Negative test of validating identifier when there are multiple slashes after list key values without next
* identifier. Test is expected to fail with RestconfDocumentedException
.
*/
@Test
public void validArgIdentifierListEndsWithSlashesNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key=value//"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
+ "error-message: Invalid path 'deserializer-test:list-one-key=value//' at offset 37, "
+ "error-info: Identifier may not be empty]]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
}
/**
* Negative test of creating QName
when identifier is empty (example: '/'). Test is expected to fail
* with RestconfDocumentedException
.
*/
@Test
public void prepareQnameEmptyIdentifierNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "/"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
+ "error-message: Invalid path '/' at offset 0, "
+ "error-info: Identifier may not be empty]]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
}
/**
* Negative test of creating QName
when in identifier there is another sign than colon or equals.
* Test is expected to fail with RestconfDocumentedException
.
*/
@Test
public void prepareQnameBuildPathNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test*contA"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
+ "error-message: Invalid path 'deserializer-test*contA' at offset 17, "
+ "error-info: Expecting [a-zA-Z_.-], not '*']]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
assertEquals("Expecting [a-zA-Z_.-], not '*'", errors.get(0).getErrorInfo());
}
/**
* Negative test of creating QName
when it is not possible to find module for specified prefix. Test is
* expected to fail with RestconfDocumentedException
.
*/
@Test
public void prepareQnameNotExistingPrefixNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "not-existing:contA"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: unknown-element, "
+ "error-message: Failed to lookup for module with name 'not-existing'.]]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.UNKNOWN_ELEMENT, errors.get(0).getErrorTag());
}
/**
* Negative test of creating QName
when after prefix and colon there is not parsable identifier as
* local name. Test is expected to fail with RestconfDocumentedException
.
*/
@Test
public void prepareQnameNotValidPrefixAndLocalNameNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class, () ->
YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:*not-parsable-identifier"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
+ "error-message: Invalid path 'deserializer-test:*not-parsable-identifier' at offset 18, "
+ "error-info: Expecting [a-zA-Z_], not '*']]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
assertEquals("Expecting [a-zA-Z_], not '*'", errors.get(0).getErrorInfo());
}
/**
* Negative test of creating QName
when data ends after prefix and colon. Test is expected to fail
* with StringIndexOutOfBoundsException
.
*/
@Test
public void prepareQnameErrorParsingNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: malformed-message, "
+ "error-message: Invalid path 'deserializer-test:' at offset 18, "
+ "error-info: Identifier may not be empty]]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.MALFORMED_MESSAGE, errors.get(0).getErrorTag());
assertEquals("Identifier may not be empty", errors.get(0).getErrorInfo());
}
/**
* Negative test of creating QName
when after identifier and colon there is node name of unknown
* node in current container. Test is expected to fail with RestconfDocumentedException
and error
* type, error tag and error status code are compared to expected values.
*/
@Test
public void prepareQnameNotValidContainerNameNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leafB"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: data-missing, "
+ "error-message: Schema for '(deserializer:test?revision=2016-06-06)leafB' not found]]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.DATA_MISSING, errors.get(0).getErrorTag());
}
/**
* Negative test of creating QName
when after identifier and equals there is node name of unknown
* node in current list. Test is expected to fail with RestconfDocumentedException
and error
* type, error tag and error status code are compared to expected values.
*/
@Test
public void prepareQnameNotValidListNameNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"deserializer-test:list-no-key/disabled=false"));
assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
assertEquals(ErrorTag.DATA_MISSING, ex.getErrors().get(0).getErrorTag());
}
/**
* Negative test of getting next identifier when current node is keyed entry. Test is expected to
* fail with RestconfDocumentedException
.
*/
@Test
public void prepareIdentifierNotKeyedEntryNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: missing-attribute, "
+ "error-message: Entry '(deserializer:test?revision=2016-06-06)list-one-key' requires key or value "
+ "predicate to be present.]]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.MISSING_ATTRIBUTE, errors.get(0).getErrorTag());
}
/**
* Negative test when there is a comma also after the last key. Test is expected to fail with
* RestconfDocumentedException
. Last comma indicates a fourth key, which is a mismatch with schema.
*/
@Test
public void deserializeKeysEndsWithCommaTooManyNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"deserializer-test:list-multiple-keys=value,100,false,"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: unknown-attribute, "
+ "error-message: Schema for (deserializer:test?revision=2016-06-06)list-multiple-keys "
+ "requires 3 key values, 4 supplied]]", ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.UNKNOWN_ATTRIBUTE, errors.get(0).getErrorTag());
}
/**
* Negative test when there is a comma also after the last key. Test is expected to fail with
* RestconfDocumentedException
. Last comma indicates a third key, whose is a mismatch with schema.
*/
@Test
public void deserializeKeysEndsWithCommaIllegalNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"deserializer-test:list-multiple-keys=value,100,"));
assertEquals("errors: [RestconfError [error-type: protocol, error-tag: invalid-value, "
+ "error-message: Invalid value '' for (deserializer:test?revision=2016-06-06)enabled, "
+ "error-info: Invalid value '' for boolean type. Allowed values are 'true' and 'false']]",
ex.getMessage());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, errors.get(0).getErrorTag());
assertEquals("Invalid value '' for boolean type. Allowed values are 'true' and 'false'",
errors.get(0).getErrorInfo());
}
/**
* Positive when not all keys of list are encoded. The missing keys should be considered to has empty
* String
values. Also value of next leaf must not be considered to be missing key value.
*/
@Test
public void notAllListKeysEncodedPositiveTest() {
final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
final Map values = ImmutableMap.of(
QName.create(list, "name"), ":foo",
QName.create(list, "number"), Uint8.ONE,
QName.create(list, "enabled"), false);
final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"deserializer-test:list-multiple-keys=%3Afoo,1,false/string-value").path.getPathArguments();
assertEquals(3, result.size());
// list
assertEquals(NodeIdentifier.create(list), result.get(0));
assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
// leaf
assertEquals(new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "string-value")),
result.get(2));
}
/**
* Negative test when not all keys of list are encoded and it is not possible to consider missing keys to be empty.
* Test is expected to fail with RestconfDocumentedException
and error type, error tag and error
* status code are compared to expected values.
*/
@Test
public void notAllListKeysEncodedNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"deserializer-test:list-multiple-keys=%3Afoo/string-value"));
assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
assertEquals(ErrorTag.MISSING_ATTRIBUTE, ex.getErrors().get(0).getErrorTag());
}
/**
* Test URI with list where key value starts with, ends with or contains percent encoded characters.The encoded
* value should be complete also with not percent-encoded parts.
*/
@Test
public void percentEncodedKeyEndsWithNoPercentEncodedChars() {
final String URI = "deserializer-test:list-multiple-keys=%3Afoo,1,true";
final YangInstanceIdentifier result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, URI).path;
final Iterator> resultListKeys =
((NodeIdentifierWithPredicates)result.getLastPathArgument()).entrySet().iterator();
assertEquals(":foo", resultListKeys.next().getValue());
assertEquals(Uint8.ONE, resultListKeys.next().getValue());
assertEquals(true, resultListKeys.next().getValue());
}
/**
* Positive test when all keys of list can be considered to be empty String
.
*/
@Test
public void deserializeAllKeysEmptyTest() {
final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
final Map values = ImmutableMap.of(
QName.create(list, "name"), "",
QName.create(list, "number"), Uint8.ZERO,
QName.create(list, "enabled"), true);
final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"deserializer-test:list-multiple-keys=,0,true").path.getPathArguments();
assertEquals(2, result.size());
assertEquals(NodeIdentifier.create(list), result.get(0));
assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
}
/**
* Negative test of deserialization when for leaf list there is no specified instance value.
* RestconfDocumentedException
is expected and error type, error tag and error status code are
* compared to expected values.
*/
@Test
public void leafListMissingKeyNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:leaf-list-0="));
assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag());
}
/**
* Positive test of deserialization when parts of input URI String
are defined in another module.
*/
@Test
public void deserializePartInOtherModuleTest() {
final List result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"deserializer-test-included:augmented-list=100/deserializer-test:augmented-leaf")
.path.getPathArguments();
assertEquals(4, result.size());
// list
final QName list = QName.create("deserializer:test:included", "2016-06-06", "augmented-list");
assertEquals(NodeIdentifier.create(list), result.get(0));
assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint16.valueOf(100)),
result.get(1));
// augmented leaf
final QName augLeaf = QName.create("deserializer:test", "2016-06-06", "augmented-leaf");
final QName augList = QName.create("deserializer:test", "2016-06-06", "augmenting-list");
assertEquals(new AugmentationIdentifier(Set.of(augLeaf, augList)), result.get(2));
assertEquals(NodeIdentifier.create(augLeaf), result.get(3));
}
@Test
public void deserializeListInOtherModuleTest() {
final List result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
"deserializer-test-included:augmented-list=100/deserializer-test:augmenting-list=0")
.path.getPathArguments();
assertEquals(5, result.size());
// list
final QName list = QName.create("deserializer:test:included", "2016-06-06", "augmented-list");
assertEquals(NodeIdentifier.create(list), result.get(0));
assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint16.valueOf(100)),
result.get(1));
// augmented list
final QName augLeaf = QName.create("deserializer:test", "2016-06-06", "augmented-leaf");
final QName augList = QName.create("deserializer:test", "2016-06-06", "augmenting-list");
assertEquals(new AugmentationIdentifier(Set.of(augLeaf, augList)), result.get(2));
assertEquals(NodeIdentifier.create(augList), result.get(3));
assertEquals(NodeIdentifierWithPredicates.of(augList, QName.create(augList, "id"), 0), result.get(4));
}
/**
* Deserialization of path that contains list entry with key which value is described by leaflef to identityref.
*/
@Test
public void deserializePathWithIdentityrefKeyValueTest() {
assertIdentityrefKeyValue(
"deserializer-test-included:refs/list-with-identityref=deserializer-test%3Aderived-identity/foo");
}
/**
* Identityref key value is not encoded correctly - ':' character must be encoded as '%3A'.
*/
@Test
public void deserializePathWithInvalidIdentityrefKeyValueTest() {
assertIdentityrefKeyValue(
"deserializer-test-included:refs/list-with-identityref=deserializer-test:derived-identity/foo");
}
private static void assertIdentityrefKeyValue(final String path) {
final var pathArgs = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, path).path.getPathArguments();
assertEquals(4, pathArgs.size());
assertEquals("refs", pathArgs.get(0).getNodeType().getLocalName());
assertEquals("list-with-identityref", pathArgs.get(1).getNodeType().getLocalName());
final PathArgument listEntryArg = pathArgs.get(2);
assertThat(listEntryArg, instanceOf(NodeIdentifierWithPredicates.class));
assertEquals("list-with-identityref", listEntryArg.getNodeType().getLocalName());
final Set keys = ((NodeIdentifierWithPredicates) listEntryArg).keySet();
assertEquals(1, keys.size());
assertEquals("id", keys.iterator().next().getLocalName());
final Object keyValue = ((NodeIdentifierWithPredicates) listEntryArg).values().iterator().next();
assertEquals(QName.create("deserializer:test", "derived-identity", Revision.of("2016-06-06")), keyValue);
assertEquals("foo", pathArgs.get(3).getNodeType().getLocalName());
}
}