Provide better error messages when parsing JSON.
[yangtools.git] / yang / yang-data-codec-gson / src / test / java / org / opendaylight / yangtools / yang / data / codec / gson / JsonStreamToNormalizedNodeTest.java
1 /*
2  * Copyright (c) 2014 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.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertTrue;
13 import static org.junit.Assert.fail;
14 import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadModules;
15 import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
16 import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.augmentationBuilder;
17 import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.choiceBuilder;
18 import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.containerBuilder;
19 import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
20
21 import com.google.common.collect.Sets;
22 import com.google.gson.stream.JsonReader;
23 import java.io.IOException;
24 import java.io.StringReader;
25 import java.net.URISyntaxException;
26 import org.junit.BeforeClass;
27 import org.junit.Ignore;
28 import org.junit.Test;
29 import org.opendaylight.yangtools.yang.common.QName;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
34 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
35 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
36 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
37 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
39 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
40
41 /**
42  *
43  * Each test tests whether json input is correctly transformed to normalized node structure
44  */
45 public class JsonStreamToNormalizedNodeTest {
46
47     private static final QName CONT_1 = QName.create("ns:complex:json", "2014-08-11", "cont1");
48     private static final QName EMPTY_LEAF = QName.create(CONT_1,"empty");
49     private static SchemaContext schemaContext;
50
51     @BeforeClass
52     public static void initialization() throws IOException, URISyntaxException {
53         schemaContext = loadModules("/complexjson/yang");
54     }
55
56     /**
57      * case when anyxml contains simple value will be implemented when anyxml normalized node reprezentation will be
58      * specified
59      */
60     @Ignore
61     @Test
62     public void anyXmlNodeWithSimpleValueInContainer() throws IOException, URISyntaxException {
63
64     }
65
66     /**
67      * case when anyxml contains complex xml will be implemented when anyxml normalized node reprezentation will be
68      * specified
69      */
70     @Ignore
71     @Test
72     public void anyXmlNodeWithCompositeValueInContainer() throws IOException, URISyntaxException {
73
74     }
75
76     @Test
77     public void leafNodeInContainer() throws IOException, URISyntaxException {
78         final String inputJson = loadTextFile("/complexjson/leaf-node-in-container.json");
79         verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.leafNodeInContainer());
80     }
81
82     @Test
83     public void leafNodeViaAugmentationInContainer() throws IOException, URISyntaxException {
84         final String inputJson = loadTextFile("/complexjson/leaf-node-via-augmentation-in-container.json");
85         verifyTransformationToNormalizedNode(inputJson,
86                 TestingNormalizedNodeStructuresCreator.leafNodeViaAugmentationInContainer());
87     }
88
89     @Test
90     public void leafListNodeInContainer() throws IOException, URISyntaxException {
91         final String inputJson = loadTextFile("/complexjson/leaflist-node-in-container.json");
92         verifyTransformationToNormalizedNode(inputJson,
93                 TestingNormalizedNodeStructuresCreator.leafListNodeInContainer());
94     }
95
96     @Test
97     public void keyedListNodeInContainer() throws IOException, URISyntaxException {
98         final String inputJson = loadTextFile("/complexjson/keyed-list-node-in-container.json");
99         verifyTransformationToNormalizedNode(inputJson,
100                 TestingNormalizedNodeStructuresCreator.keyedListNodeInContainer());
101     }
102
103     @Test
104     public void choiceNodeInContainer() throws IOException, URISyntaxException {
105         final String inputJson = loadTextFile("/complexjson/choice-node-in-container.json");
106         verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.choiceNodeInContainer());
107     }
108
109     /**
110      * Test of translating internal augmentations to normalized nodes structure
111      *
112      * 2 nodes are added via internal augmentation A, 1 node via internal augmentation B and one node is originally
113      * member of case.
114      *
115      */
116     @Test
117     public void caseNodeAugmentationInChoiceInContainer() throws IOException, URISyntaxException {
118         final String inputJson = loadTextFile("/complexjson/case-node-augmentation-in-choice-in-container.json");
119         verifyTransformationToNormalizedNode(inputJson,
120                 TestingNormalizedNodeStructuresCreator.caseNodeAugmentationInChoiceInContainer());
121     }
122
123     /**
124      * also test using of namesakes (equal local names with different
125      *
126      * @throws IOException
127      * @throws URISyntaxException
128      */
129     @Test
130     public void caseNodeExternalAugmentationInChoiceInContainer() throws IOException, URISyntaxException {
131         final String inputJson = loadTextFile("/complexjson/case-node-external-augmentation-in-choice-in-container.json");
132         verifyTransformationToNormalizedNode(inputJson,
133                 TestingNormalizedNodeStructuresCreator.caseNodeExternalAugmentationInChoiceInContainer());
134     }
135
136     /**
137      * augmentation of choice - adding new case
138      */
139     @Test
140     public void choiceNodeAugmentationInContainer() throws IOException, URISyntaxException {
141         final String inputJson = loadTextFile("/complexjson/choice-node-augmentation-in-container.json");
142         verifyTransformationToNormalizedNode(inputJson,
143                 TestingNormalizedNodeStructuresCreator.choiceNodeAugmentationInContainer());
144     }
145
146     @Test
147     public void unkeyedNodeInContainer() throws IOException, URISyntaxException {
148         final String inputJson = loadTextFile("/complexjson/unkeyed-node-in-container.json");
149         verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.unkeyedNodeInContainer());
150     }
151
152     /**
153      * Top level JSON element contains no information about module name.
154      *
155      * It should be possible to find out potential module name from available schema context.
156      *
157      */
158     @Test
159     public void missingModuleInfoInTopLevelElement() throws IOException, URISyntaxException {
160         final String inputJson = loadTextFile("/complexjson/missing-module-in-top-level.json");
161         verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.topLevelContainer());
162     }
163
164     /**
165      *
166      * Exception expected.
167      *
168      * It tests case when several elements with the same name and various namespaces exists and are in JSON specified
169      * without module name prefix.
170      */
171     @Test
172     public void leafNamesakes() throws IOException, URISyntaxException {
173         final String inputJson = loadTextFile("/complexjson/namesakes.json");
174         try {
175             //second parameter isn't necessary because error will be raised before it is used.
176             verifyTransformationToNormalizedNode(inputJson, null);
177                         fail("Expected exception not raised");
178         } catch (final IllegalStateException e) {
179             final String errorMessage = e.getMessage();
180             assertTrue(errorMessage.contains("Choose suitable module name for element lf11-namesake:"));
181             assertTrue(errorMessage.contains("complexjson-augmentation"));
182             assertTrue(errorMessage.contains("complexjson-augmentation-namesake"));
183         }
184     }
185
186     @Test
187     public void emptyTypeTest() throws IOException, URISyntaxException {
188         final String inputJson = loadTextFile("/complexjson/type-empty.json");
189         final ContainerNode awaitedStructure = containerBuilder()
190                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CONT_1))
191                 .addChild(leafNode(EMPTY_LEAF, null))
192                 .build();
193
194         verifyTransformationToNormalizedNode(inputJson, awaitedStructure);
195     }
196
197     /**
198      *
199      * Exception expected.
200      *
201      * Json input contains element which doesn't exist in YANG schema
202      */
203     @Test
204     public void parsingNotExistingElement() throws IOException, URISyntaxException {
205         final String inputJson = loadTextFile("/complexjson/not-existing-element.json");
206         try {
207             //second parameter isn't necessary because error will be raised before it is used.
208             verifyTransformationToNormalizedNode(inputJson, null);
209         } catch (final IllegalStateException e) {
210             assertTrue(e.getMessage().contains("Schema node with name dummy-element wasn't found"));
211         }
212     }
213
214
215     @Test
216     public void listItemWithoutArray() throws IOException, URISyntaxException {
217         final String inputJson = loadTextFile("/complexjson/keyed-list-restconf-behaviour.json");
218
219         final NormalizedNodeResult result = new NormalizedNodeResult();
220         final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
221         final SchemaNode parentNode = schemaContext.getDataChildByName("cont1");
222         final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, schemaContext, parentNode);
223         jsonParser.parse(new JsonReader(new StringReader(inputJson)));
224         final NormalizedNode<?, ?> transformedInput = result.getResult();
225         assertNotNull(transformedInput);
226     }
227
228     @Test
229     public void listItemWithArray() throws IOException, URISyntaxException {
230         final String inputJson = loadTextFile("/complexjson/keyed-list-yang-json-behaviour.json");
231
232         final NormalizedNodeResult result = new NormalizedNodeResult();
233         final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
234         final SchemaNode parentNode = schemaContext.getDataChildByName("cont1");
235         final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, schemaContext, parentNode);
236         jsonParser.parse(new JsonReader(new StringReader(inputJson)));
237         final NormalizedNode<?, ?> transformedInput = result.getResult();
238         assertNotNull(transformedInput);
239     }
240
241    @Test
242     public void multipleChoiceAugmentation() throws IOException, URISyntaxException {
243         final String inputJson = loadTextFile("/complexjson/multiple-choice-augmentation-in-container.json");
244
245         final NormalizedNodeResult result = new NormalizedNodeResult();
246         final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
247         final SchemaNode parentNode = schemaContext.getDataChildByName("cont1");
248
249         QName augmentChoice1QName = QName.create(parentNode.getQName(), "augment-choice1");
250         QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2");
251         final QName containerQName = QName.create(augmentChoice1QName, "case11-choice-case-container");
252         final QName leafQName = QName.create(augmentChoice1QName, "case11-choice-case-leaf");
253
254         final YangInstanceIdentifier.AugmentationIdentifier aug1Id =
255                 new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(augmentChoice1QName));
256         final YangInstanceIdentifier.AugmentationIdentifier aug2Id =
257                 new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(augmentChoice2QName));
258         final YangInstanceIdentifier.NodeIdentifier augmentChoice1Id =
259                 new YangInstanceIdentifier.NodeIdentifier(augmentChoice1QName);
260         final YangInstanceIdentifier.NodeIdentifier augmentChoice2Id =
261                 new YangInstanceIdentifier.NodeIdentifier(augmentChoice2QName);
262         final YangInstanceIdentifier.NodeIdentifier containerId =
263                 new YangInstanceIdentifier.NodeIdentifier(containerQName);
264
265         final NormalizedNode<?, ?> cont1Normalized =
266                 containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(parentNode.getQName()))
267                         .withChild(augmentationBuilder().withNodeIdentifier(aug1Id)
268                                 .withChild(choiceBuilder().withNodeIdentifier(augmentChoice1Id)
269                                         .withChild(augmentationBuilder().withNodeIdentifier(aug2Id)
270                                                 .withChild(choiceBuilder().withNodeIdentifier(augmentChoice2Id)
271                                                         .withChild(containerBuilder().withNodeIdentifier(containerId)
272                                                                 .withChild(leafNode(leafQName, "leaf-value"))
273                                                                 .build())
274                                                         .build())
275                                                 .build())
276                                         .build())
277                                 .build()).build();
278
279         final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, schemaContext);
280         jsonParser.parse(new JsonReader(new StringReader(inputJson)));
281         final NormalizedNode<?, ?> transformedInput = result.getResult();
282         assertNotNull(transformedInput);
283         assertEquals(cont1Normalized, transformedInput);
284     }
285
286     private void verifyTransformationToNormalizedNode(final String inputJson,
287             final NormalizedNode<?, ?> awaitedStructure) {
288         final NormalizedNodeResult result = new NormalizedNodeResult();
289         final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
290         final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, schemaContext);
291         jsonParser.parse(new JsonReader(new StringReader(inputJson)));
292         final NormalizedNode<?, ?> transformedInput = result.getResult();
293         assertEquals("Transformation of json input to normalized node wasn't successful.", awaitedStructure,
294                 transformedInput);
295     }
296
297 }