+ assertEquals("now", containerNode.body().iterator().next().body());
+ }
+
+ @Test
+ public void toActionEmptyBodyWithOutputDefinedResultTest() throws Exception {
+ NetconfMessage message = new NetconfMessage(XmlUtil.readXmlToDocument(
+ "<rpc-reply message-id=\"101\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<ok/>"
+ + "</rpc-reply>"));
+ DOMActionResult actionResult =
+ actionNetconfMessageTransformer.toActionResult(CHECK_WITH_OUTPUT_INTERFACE_PATH, message);
+ assertNotNull(actionResult);
+ assertTrue(actionResult.getOutput().isEmpty());
+ }
+
+ @Test
+ public void toActionEmptyBodyWithoutOutputDefinedResultTest() throws Exception {
+ NetconfMessage message = new NetconfMessage(XmlUtil.readXmlToDocument(
+ "<rpc-reply message-id=\"101\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<ok/>"
+ + "</rpc-reply>"));
+ DOMActionResult actionResult =
+ actionNetconfMessageTransformer.toActionResult(CHECK_WITHOUT_OUTPUT_INTERFACE_PATH, message);
+ assertNotNull(actionResult);
+ assertTrue(actionResult.getOutput().isEmpty());
+ }
+
+ @Test
+ public void getTwoNonOverlappingFieldsTest() throws IOException, SAXException {
+ // preparation of the fields
+ final YangInstanceIdentifier parentYiid = YangInstanceIdentifier.create(toId(NetconfState.QNAME));
+ final YangInstanceIdentifier netconfStartTimeField = YangInstanceIdentifier.create(toId(Statistics.QNAME),
+ toId(QName.create(Statistics.QNAME, "netconf-start-time")));
+ final YangInstanceIdentifier datastoresField = YangInstanceIdentifier.create(toId(Datastores.QNAME));
+
+ // building filter structure and NETCONF message
+ final AnyxmlNode<?> filterStructure = toFilterStructure(
+ List.of(FieldsFilter.of(parentYiid, List.of(netconfStartTimeField, datastoresField))), SCHEMA);
+ final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(NETCONF_GET_QNAME,
+ NetconfMessageTransformUtil.wrap(toId(NETCONF_GET_QNAME), filterStructure));
+
+ // testing
+ assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ + "<get>\n"
+ + "<filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n"
+ + "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n"
+ + "<statistics>\n"
+ + "<netconf-start-time/>\n"
+ + "</statistics>\n"
+ + "<datastores/>\n"
+ + "</netconf-state>\n"
+ + "</filter>\n"
+ + "</get>\n"
+ + "</rpc>");
+ }
+
+ @Test
+ public void getOverlappingFieldsTest() throws IOException, SAXException {
+ // preparation of the fields
+ final YangInstanceIdentifier parentYiid = YangInstanceIdentifier.create(toId(NetconfState.QNAME));
+ final YangInstanceIdentifier capabilitiesField = YangInstanceIdentifier.create(toId(Capabilities.QNAME));
+ final YangInstanceIdentifier capabilityField = YangInstanceIdentifier.create(toId(Capabilities.QNAME),
+ toId(QName.create(Capabilities.QNAME, "capability").intern()));
+ final YangInstanceIdentifier datastoreField = YangInstanceIdentifier.create(toId(Datastores.QNAME));
+ final YangInstanceIdentifier locksFields = YangInstanceIdentifier.create(toId(Datastores.QNAME),
+ toId(Datastore.QNAME), NodeIdentifierWithPredicates.of(Datastore.QNAME), toId(Locks.QNAME));
+
+ // building filter structure and NETCONF message
+ final AnyxmlNode<?> filterStructure = toFilterStructure(
+ List.of(FieldsFilter.of(parentYiid,
+ List.of(capabilitiesField, capabilityField, datastoreField, locksFields))),
+ SCHEMA);
+ final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(NETCONF_GET_QNAME,
+ NetconfMessageTransformUtil.wrap(toId(NETCONF_GET_QNAME), filterStructure));
+
+ // testing
+ assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ + "<get>\n"
+ + "<filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n"
+ + "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n"
+ + "<capabilities/>\n"
+ + "<datastores/>\n"
+ + "</netconf-state>\n"
+ + "</filter>\n"
+ + "</get>\n"
+ + "</rpc>");
+ }
+
+ @Test
+ public void getOverlappingFieldsWithEmptyFieldTest() throws IOException, SAXException {
+ // preparation of the fields
+ final YangInstanceIdentifier parentYiid = YangInstanceIdentifier.create(toId(NetconfState.QNAME));
+ final YangInstanceIdentifier capabilitiesField = YangInstanceIdentifier.create(toId(Capabilities.QNAME));
+
+ // building filter structure and NETCONF message
+ final AnyxmlNode<?> filterStructure = toFilterStructure(
+ List.of(FieldsFilter.of(parentYiid, List.of(capabilitiesField, YangInstanceIdentifier.empty()))),
+ SCHEMA);
+ final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(NETCONF_GET_QNAME,
+ NetconfMessageTransformUtil.wrap(toId(NETCONF_GET_QNAME), filterStructure));
+
+ // testing
+ assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ + "<get>\n"
+ + "<filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n"
+ + "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"/>\n"
+ + "</filter>\n"
+ + "</get>\n"
+ + "</rpc>");
+ }
+
+ @Test
+ public void getSpecificFieldsUnderListTest() throws IOException, SAXException {
+ // preparation of the fields
+ final YangInstanceIdentifier parentYiid = YangInstanceIdentifier.create(toId(NetconfState.QNAME),
+ toId(Schemas.QNAME), toId(Schema.QNAME), NodeIdentifierWithPredicates.of(Schema.QNAME));
+ final YangInstanceIdentifier versionField = YangInstanceIdentifier.create(
+ toId(QName.create(Schema.QNAME, "version").intern()));
+ final YangInstanceIdentifier identifierField = YangInstanceIdentifier.create(
+ toId(QName.create(Schema.QNAME, "namespace").intern()));
+
+ // building filter structure and NETCONF message
+ final AnyxmlNode<?> filterStructure = toFilterStructure(
+ List.of(FieldsFilter.of(parentYiid, List.of(versionField, identifierField))), SCHEMA);
+ final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(NETCONF_GET_QNAME,
+ NetconfMessageTransformUtil.wrap(toId(NETCONF_GET_QNAME), filterStructure));
+
+ // testing
+ assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ + "<get>\n"
+ + "<filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n"
+ + "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n"
+ + "<schemas>\n"
+ + "<schema>\n"
+ + "<version/>\n"
+ + "<namespace/>\n"
+ // explicitly fetched list keys - identifier and format
+ + "<identifier/>\n"
+ + "<format/>\n"
+ + "</schema>\n"
+ + "</schemas>\n"
+ + "</netconf-state>\n"
+ + "</filter>\n"
+ + "</get>\n"
+ + "</rpc>");
+ }
+
+ @Test
+ public void getSpecificFieldsUnderMultipleLists() throws IOException, SAXException {
+ // preparation of the fields
+ final YangInstanceIdentifier parentYiid = YangInstanceIdentifier.create(
+ toId(NetconfState.QNAME), toId(Datastores.QNAME));
+ final YangInstanceIdentifier partialLockYiid = YangInstanceIdentifier.create(toId(Datastore.QNAME),
+ NodeIdentifierWithPredicates.of(Datastore.QNAME), toId(Locks.QNAME),
+ toId(QName.create(Locks.QNAME, "lock-type").intern()), toId(PartialLock.QNAME),
+ NodeIdentifierWithPredicates.of(PartialLock.QNAME));
+ final YangInstanceIdentifier lockedTimeField = partialLockYiid.node(
+ QName.create(Locks.QNAME, "locked-time").intern());
+ final YangInstanceIdentifier lockedBySessionField = partialLockYiid.node(
+ QName.create(Locks.QNAME, "locked-by-session").intern());
+
+ // building filter structure and NETCONF message
+ final AnyxmlNode<?> filterStructure = toFilterStructure(
+ List.of(FieldsFilter.of(parentYiid, List.of(lockedTimeField, lockedBySessionField))),
+ SCHEMA);
+ final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(NETCONF_GET_QNAME,
+ NetconfMessageTransformUtil.wrap(toId(NETCONF_GET_QNAME), filterStructure));
+
+ // testing
+ assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ + "<get>\n"
+ + "<filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n"
+ + "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n"
+ + "<datastores>\n"
+ + "<datastore>\n"
+ + "<locks>\n"
+ + "<partial-lock>\n"
+ + "<locked-time/>\n"
+ + "<locked-by-session/>\n"
+ + "<lock-id/>\n"
+ + "</partial-lock>\n"
+ + "</locks>\n"
+ + "<name/>\n"
+ + "</datastore>\n"
+ + "</datastores>\n"
+ + "</netconf-state>\n"
+ + "</filter>\n"
+ + "</get>\n"
+ + "</rpc>");
+ }
+
+ @Test
+ public void getWholeListsUsingFieldsTest() throws IOException, SAXException {
+ // preparation of the fields
+ final YangInstanceIdentifier parentYiid = YangInstanceIdentifier.create(toId(NetconfState.QNAME));
+ final YangInstanceIdentifier datastoreListField = YangInstanceIdentifier.create(toId(Datastores.QNAME),
+ toId(Datastore.QNAME), NodeIdentifierWithPredicates.of(Datastore.QNAME));
+ final YangInstanceIdentifier sessionListField = YangInstanceIdentifier.create(toId(Sessions.QNAME),
+ toId(Session.QNAME), NodeIdentifierWithPredicates.of(Session.QNAME));
+
+ // building filter structure and NETCONF message
+ final AnyxmlNode<?> filterStructure = toFilterStructure(
+ List.of(FieldsFilter.of(parentYiid, List.of(datastoreListField, sessionListField))), SCHEMA);
+ final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(NETCONF_GET_QNAME,
+ NetconfMessageTransformUtil.wrap(toId(NETCONF_GET_QNAME), filterStructure));
+
+ // testing
+ assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ + "<get>\n"
+ + "<filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n"
+ + "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n"
+ + "<datastores>\n"
+ + "<datastore/>\n"
+ + "</datastores>\n"
+ + "<sessions>\n"
+ + "<session/>\n"
+ + "</sessions>\n"
+ + "</netconf-state>\n"
+ + "</filter>\n"
+ + "</get>\n"
+ + "</rpc>");
+ }
+
+ @Test
+ public void getSpecificListEntriesWithSpecificFieldsTest() throws IOException, SAXException {
+ // preparation of the fields
+ final YangInstanceIdentifier parentYiid = YangInstanceIdentifier.create(toId(NetconfState.QNAME),
+ toId(Sessions.QNAME));
+ final QName sessionId = QName.create(Session.QNAME, "session-id").intern();
+ final YangInstanceIdentifier session1Field = YangInstanceIdentifier.create(toId(Session.QNAME),
+ NodeIdentifierWithPredicates.of(Session.QNAME, sessionId, 1));
+ final YangInstanceIdentifier session2TransportField = YangInstanceIdentifier.create(toId(Session.QNAME),
+ NodeIdentifierWithPredicates.of(Session.QNAME, sessionId, 2),
+ toId(QName.create(Session.QNAME, "transport").intern()));
+
+ // building filter structure and NETCONF message
+ final AnyxmlNode<?> filterStructure = toFilterStructure(
+ List.of(FieldsFilter.of(parentYiid, List.of(session1Field, session2TransportField))), SCHEMA);
+ final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(NETCONF_GET_QNAME,
+ NetconfMessageTransformUtil.wrap(toId(NETCONF_GET_QNAME), filterStructure));
+
+ // testing
+ assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ + "<get>\n"
+ + "<filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n"
+ + "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n"
+ + "<sessions>\n"
+ + "<session>\n"
+ + "<session-id>1</session-id>\n"
+ + "</session>\n"
+ + "<session>\n"
+ + "<session-id>2</session-id>\n"
+ + "<transport/>\n"
+ + "</session>\n"
+ + "</sessions>\n"
+ + "</netconf-state>\n"
+ + "</filter>\n"
+ + "</get>\n"
+ + "</rpc>");
+ }
+
+ @Test
+ // Proof that YANGTOOLS-1362 works on DOM level
+ public void testConfigChangeToNotification() throws SAXException, IOException {
+ final var message = new NetconfMessage(XmlUtil.readXmlToDocument(
+ "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n"
+ + " <eventTime>2021-11-11T11:26:16Z</eventTime> \n"
+ + " <netconf-config-change xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-notifications\">\n"
+ + " <changed-by> \n"
+ + " <username>root</username> \n"
+ + " <session-id>3</session-id> \n"
+ + " </changed-by> \n"
+ + " <datastore>running</datastore> \n"
+ + " <edit> \n"
+ + " <target xmlns:ncm=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">/ncm:netconf-state"
+ + "/ncm:datastores/ncm:datastore[ncm:name='running']</target>\n"
+ + " <operation>replace</operation> \n"
+ + " </edit> \n"
+ + " </netconf-config-change> \n"
+ + "</notification>"));
+
+ final var change = netconfMessageTransformer.toNotification(message).getBody();
+ final var editList = change.getChildByArg(new NodeIdentifier(Edit.QNAME));
+ assertThat(editList, instanceOf(UnkeyedListNode.class));
+ final var edits = ((UnkeyedListNode) editList).body();
+ assertEquals(1, edits.size());
+ final var edit = edits.iterator().next();
+ final var target = edit.getChildByArg(new NodeIdentifier(QName.create(Edit.QNAME, "target"))).body();
+ assertThat(target, instanceOf(YangInstanceIdentifier.class));
+
+ final var args = ((YangInstanceIdentifier) target).getPathArguments();
+ assertEquals(4, args.size());