Release yangtools
[yangtools.git] / codec / yang-data-codec-gson / src / test / java / org / opendaylight / yangtools / yang / data / codec / gson / Bug8083Test.java
1 /*
2  * Copyright (c) 2017 Cisco Systems, Inc. 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.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
12 import static org.junit.jupiter.api.Assertions.assertNotNull;
13 import static org.mockito.ArgumentMatchers.any;
14 import static org.mockito.Mockito.doNothing;
15 import static org.mockito.Mockito.mock;
16 import static org.mockito.Mockito.verify;
17 import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
18
19 import com.google.common.collect.ImmutableMap;
20 import com.google.gson.stream.JsonReader;
21 import java.io.IOException;
22 import java.io.StringReader;
23 import java.net.URISyntaxException;
24 import org.junit.jupiter.api.AfterAll;
25 import org.junit.jupiter.api.BeforeAll;
26 import org.junit.jupiter.api.Test;
27 import org.mockito.ArgumentCaptor;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.common.QNameModule;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
33 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
35 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
36 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
37 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
39 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
41 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
42
43 class Bug8083Test {
44     private static final QNameModule FOOMOD = QNameModule.of("http://example.com/foomod");
45     private static final QNameModule BARMOD = QNameModule.of("http://example.com/barmod");
46
47     private static final QName FOO_QNAME = QName.create(FOOMOD, "foo");
48     private static final QName FOOLIST_QNAME = QName.create(FOOMOD, "foo-list");
49     private static final QName NAME_QNAME = QName.create(FOOMOD, "name");
50     private static final QName TOP_QNAME = QName.create(FOOMOD, "top");
51     private static final QName BARCONTAINER_QNAME = QName.create(BARMOD, "bar-container");
52
53     private static final YangInstanceIdentifier TEST_IID = YangInstanceIdentifier.builder()
54             .node(TOP_QNAME)
55             .node(FOOLIST_QNAME)
56             .node(NodeIdentifierWithPredicates.of(FOOLIST_QNAME, ImmutableMap.of(NAME_QNAME, "key-value")))
57             .node(BARCONTAINER_QNAME)
58             .node(QName.create(BARMOD, "bar-leaf"))
59             .build();
60     private static final String BAZ_YANG = """
61         module baz {
62           namespace baz-ns;
63           prefix baz-prefix;
64
65           container top-cont {
66             list keyed-list {
67               key empty-key-leaf;
68               leaf empty-key-leaf {
69                 type empty;
70               }
71               leaf regular-leaf {
72                 type int32;
73               }
74             }
75             leaf iid-leaf {
76               type instance-identifier;
77             }
78           }
79         }""";
80     private static final String FOOBAR_YANG = """
81         module foobar {
82           namespace foobar-ns;
83           prefix foobar-prefix;
84           container top-cont {
85             list keyed-list {
86               key iid-key-leaf;
87               leaf iid-key-leaf {
88                 type instance-identifier;
89               }
90               leaf regular-leaf {
91                 type int32;
92               }
93             }
94             leaf iid-leaf {
95               type instance-identifier;
96             }
97             leaf leaf-b {
98               type int32;
99             }
100           }
101         }""";
102     private static final String ZAB_YANG = """
103         module zab {
104           namespace zab-ns;
105           prefix zab-prefix;
106           identity base-id;
107           identity derived-id {
108             base base-id;
109           }
110           container top-cont {
111             list keyed-list {
112               key identityref-key-leaf;
113               leaf identityref-key-leaf {
114                 type identityref {
115                   base base-id;
116                 }
117               }
118               leaf regular-leaf {
119                 type int32;
120               }
121             }
122             leaf iid-leaf {
123               type instance-identifier;
124             }
125           }
126         }""";
127
128     private static EffectiveModelContext FULL_SCHEMA_CONTEXT;
129
130     @BeforeAll
131     static void init() {
132         FULL_SCHEMA_CONTEXT = YangParserTestUtils.parseYang("""
133             module example-barmod {
134               namespace "http://example.com/barmod";
135               prefix "barmod";
136               import example-foomod {
137                 prefix "foomod";
138               }
139               augment "/foomod:top/foomod:foo-list" {
140                 container bar-container {
141                   leaf bar-leaf {
142                     type string;
143                   }
144                 }
145               }
146             }""", BAZ_YANG, """
147             module example-foomod {
148               namespace "http://example.com/foomod";
149               prefix "foomod";
150               container top {
151                 leaf foo {
152                   type instance-identifier;
153                 }
154                 list foo-list {
155                   key name;
156                   leaf name {
157                     type string;
158                   }
159                 }
160               }
161             }""", FOOBAR_YANG, ZAB_YANG);
162     }
163
164     @AfterAll
165     static void cleanup() {
166         FULL_SCHEMA_CONTEXT = null;
167     }
168
169     @Test
170     void testInstanceIdentifierSerializeNew() throws IOException {
171         assertEquals("/example-foomod:top/foo-list[name='key-value']/example-barmod:bar-container/bar-leaf",
172             writeInstanceIdentifier(JSONCodecFactorySupplier.RFC7951));
173     }
174
175     @Test
176     void testInstanceIdentifierSerializeOld() throws IOException {
177         assertEquals("/example-foomod:top/example-foomod:foo-list[example-foomod:name='key-value']"
178                 + "/example-barmod:bar-container/example-barmod:bar-leaf",
179             writeInstanceIdentifier(JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02));
180     }
181
182     @Test
183     void testRFC7951InstanceIdentifierPath() throws IOException, URISyntaxException {
184         final var inputJson = loadTextFile("/bug8083/json/foo.json");
185
186         // deserialization
187         final var result = new NormalizationResultHolder();
188         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
189         final var jsonParser = JsonParserStream.create(streamWriter,
190             JSONCodecFactorySupplier.RFC7951.getShared(FULL_SCHEMA_CONTEXT));
191         jsonParser.parse(new JsonReader(new StringReader(inputJson)));
192         final var transformedInput = result.getResult().data();
193
194         final var container = assertInstanceOf(ContainerNode.class, transformedInput);
195         assertEquals(TEST_IID,
196             assertInstanceOf(LeafNode.class, container.childByArg(new NodeIdentifier(FOO_QNAME))).body());
197     }
198
199     @Test
200     void testInstanceIdentifierPathWithEmptyListKey() throws IOException, URISyntaxException {
201         final var schemaContext = YangParserTestUtils.parseYang(BAZ_YANG);
202         final var inputJson = loadTextFile("/bug8083/json/baz.json");
203
204         // deserialization
205         final var result = new NormalizationResultHolder();
206         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
207         final var jsonParser = JsonParserStream.create(streamWriter,
208             JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext));
209         jsonParser.parse(new JsonReader(new StringReader(inputJson)));
210         final var transformedInput = result.getResult().data();
211         assertNotNull(transformedInput);
212     }
213
214     @Test
215     void testInstanceIdentifierPathWithIdentityrefListKey() throws IOException, URISyntaxException {
216         final var schemaContext = YangParserTestUtils.parseYang(ZAB_YANG);
217         final var inputJson = loadTextFile("/bug8083/json/zab.json");
218
219         // deserialization
220         final var result = new NormalizationResultHolder();
221         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
222         final var jsonParser = JsonParserStream.create(streamWriter,
223             JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext));
224         jsonParser.parse(new JsonReader(new StringReader(inputJson)));
225         final var transformedInput = result.getResult().data();
226         assertNotNull(transformedInput);
227     }
228
229     @Test
230     void testInstanceIdentifierPathWithInstanceIdentifierListKey() throws IOException, URISyntaxException {
231         final var schemaContext = YangParserTestUtils.parseYang(FOOBAR_YANG);
232         final var inputJson = loadTextFile("/bug8083/json/foobar.json");
233
234         // deserialization
235         final var result = new NormalizationResultHolder();
236         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
237         final var jsonParser = JsonParserStream.create(streamWriter,
238             JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext));
239         jsonParser.parse(new JsonReader(new StringReader(inputJson)));
240         final var transformedInput = result.getResult().data();
241         assertNotNull(transformedInput);
242     }
243
244     private static JSONCodec<YangInstanceIdentifier> getCodec(final JSONCodecFactorySupplier supplier) {
245         final var top = assertInstanceOf(ContainerSchemaNode.class, FULL_SCHEMA_CONTEXT.dataChildByName(TOP_QNAME));
246         final var foo = assertInstanceOf(LeafSchemaNode.class, top.dataChildByName(FOO_QNAME));
247         final var type = assertInstanceOf(InstanceIdentifierTypeDefinition.class, foo.getType());
248         return supplier.createSimple(FULL_SCHEMA_CONTEXT).instanceIdentifierCodec(type);
249     }
250
251     private static String writeInstanceIdentifier(final JSONCodecFactorySupplier supplier) throws IOException {
252         final var writer = mock(JSONValueWriter.class);
253         doNothing().when(writer).writeString(any());
254
255         getCodec(supplier).writeValue(writer, TEST_IID);
256
257         final var captor = ArgumentCaptor.forClass(String.class);
258         verify(writer).writeString(captor.capture());
259         return captor.getValue();
260     }
261 }