Tests for YangInstanceIdentifier key value serialization
[yangtools.git] / codec / yang-data-codec-gson / src / test / java / org / opendaylight / yangtools / yang / data / codec / gson / YT1473Test.java
1 /*
2  * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.data.codec.gson;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertTrue;
12 import static org.mockito.ArgumentMatchers.anyString;
13 import static org.mockito.Mockito.doReturn;
14 import static org.mockito.Mockito.verify;
15
16 import com.google.gson.stream.JsonWriter;
17 import org.junit.jupiter.api.AfterAll;
18 import org.junit.jupiter.api.BeforeAll;
19 import org.junit.jupiter.api.Disabled;
20 import org.junit.jupiter.api.Test;
21 import org.junit.jupiter.api.extension.ExtendWith;
22 import org.mockito.ArgumentCaptor;
23 import org.mockito.Captor;
24 import org.mockito.Mock;
25 import org.mockito.junit.jupiter.MockitoExtension;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
31 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
34 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
35
36 @ExtendWith(MockitoExtension.class)
37 public class YT1473Test {
38
39     private static final String FOO_NS = "foons"; // namespace for prefix 'foo'
40     private static final QName FOO_FOO = QName.create(FOO_NS, "foo"); // list with key 'str'
41     private static final QName FOO_BAR = QName.create(FOO_NS, "bar"); // list with key 'qname'
42     private static final QName FOO_BAZ = QName.create(FOO_NS, "baz"); // list with key 'id'
43     private static final QName FOO_ONE = QName.create(FOO_NS, "one"); // identity
44     private static final QName FOO_STR = QName.create(FOO_NS, "str"); // key of type 'string'
45     private static final QName FOO_QNAME = QName.create(FOO_NS, "qname"); // key of type 'one' based
46     private static final QName FOO_ID = QName.create(FOO_NS, "id"); // key of type 'instance-identifier'
47
48     private static final String BAR_NS = "barns"; // namespace for prefix 'bar'
49     private static final QName BAR_FOO = QName.create(BAR_NS, "foo"); // leaf of type 'foo:one' based
50     private static final QName BAR_BAR = QName.create(BAR_NS, "bar"); // leaf of type 'instance-identifier'
51     private static final QName BAR_TWO = QName.create(BAR_NS, "two"); // identity inheriting 'foo:one'
52
53     private static JSONCodec<YangInstanceIdentifier> CODEC;
54
55     @Mock
56     private JsonWriter writer;
57     @Captor
58     private ArgumentCaptor<String> captor;
59
60     @BeforeAll
61     public static void beforeAll() {
62         final var modelContext = YangParserTestUtils.parseYangResourceDirectory("/yt1473");
63         final var baz = modelContext.getDataChildByName(FOO_BAZ);
64         assertTrue(baz instanceof ListSchemaNode);
65         final var id = ((ListSchemaNode) baz).getDataChildByName(FOO_ID);
66         assertTrue(id instanceof LeafSchemaNode);
67         final var type = ((LeafSchemaNode) id).getType();
68         assertTrue(type instanceof InstanceIdentifierTypeDefinition);
69         CODEC = JSONCodecFactorySupplier.RFC7951.getShared(modelContext)
70                 .instanceIdentifierCodec((InstanceIdentifierTypeDefinition) type);
71     }
72
73     @AfterAll
74     public static void afterAll() {
75         CODEC = null;
76     }
77
78     @Test
79     public void testSerializeSimple() throws Exception {
80         // No escaping needed, use single quotes
81         assertEquals("/foo:foo[str='str\"']", write(buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str\"")));
82     }
83
84     @Test
85     @Disabled("YT-1473: string escaping needs to work")
86     public void testSerializeEscaped() throws Exception {
87         // Escaping is needed, use double quotes and escape
88         assertEquals("/foo:foo[str=\"str'\\\"\"]", write(buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str'\"")));
89     }
90
91     @Test
92     @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
93     public void testSerializeIdentityRefSame() throws Exception {
94         // TODO: an improvement is to use just 'one' as the namespace is the same as the leaf (see RFC7951 section 6.8)
95         assertEquals("/foo:bar[foo:qname='foo:one']", write(buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, FOO_ONE)));
96     }
97
98     @Test
99     @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
100     public void testSerializeIdentityRefOther() throws Exception {
101         // No escaping is needed, use double quotes and escape
102         assertEquals("/foo:bar[qname='bar:two']", write(buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO)));
103     }
104
105     @Test
106     @Disabled("YT-1473: Instance-identifier values need to be recognized and properly encoded and escaped")
107     public void testSerializeInstanceIdentifierRef() throws Exception {
108         assertEquals("/foo:baz[id=\"/foo:bar[qname='bar:two']\"]", write(
109                 buildYangInstanceIdentifier(FOO_BAZ, FOO_ID, buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO)))
110         );
111     }
112
113     @Test
114     @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
115     public void testSerializeIdentityValue() throws Exception {
116         assertEquals("/bar:foo[.='foo:one']", write(buildYangInstanceIdentifier(BAR_FOO, FOO_ONE)));
117     }
118
119     @Test
120     @Disabled("YT-1473: Instance-identifier values need to be recognized and properly encoded and escaped")
121     public void testSerializeInstanceIdentifierValue() throws Exception {
122         assertEquals("/bar:bar[.=\"/foo:bar/bar[qname='bar:two'\"]']",
123                 write(buildYangInstanceIdentifier(BAR_BAR, buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO))));
124     }
125
126     private static YangInstanceIdentifier buildYangInstanceIdentifier(final QName node, final QName key,
127             final Object value) {
128         return YangInstanceIdentifier.create(
129                 new NodeIdentifier(node), NodeIdentifierWithPredicates.of(node, key, value));
130     }
131
132     private static YangInstanceIdentifier buildYangInstanceIdentifier(final QName nodeQName, final Object value) {
133         return YangInstanceIdentifier.create(new NodeWithValue<>(nodeQName, value));
134     }
135
136     private String write(final YangInstanceIdentifier yangInstanceIdentifier) throws Exception {
137         doReturn(writer).when(writer).value(anyString());
138         CODEC.writeValue(writer, yangInstanceIdentifier);
139         verify(writer).value(captor.capture());
140         return captor.getValue();
141     }
142 }