Bump MRI upstreams
[netconf.git] / restconf / sal-rest-docgen / src / test / java / org / opendaylight / controller / sal / rest / doc / impl / ApiDocGeneratorTest.java
1 /*
2  * Copyright (c) 2014, 2015 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
9 package org.opendaylight.controller.sal.rest.doc.impl;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertTrue;
14 import static org.junit.Assert.fail;
15
16 import com.fasterxml.jackson.databind.JsonNode;
17 import com.fasterxml.jackson.databind.node.ObjectNode;
18 import com.google.common.base.Preconditions;
19 import java.sql.Date;
20 import java.util.Arrays;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.Set;
24 import java.util.TreeSet;
25 import org.junit.Before;
26 import org.junit.Test;
27 import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocGeneratorDraftO2;
28 import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl;
29 import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.URIType;
30 import org.opendaylight.netconf.sal.rest.doc.swagger.SwaggerObject;
31 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
32 import org.opendaylight.yangtools.yang.model.api.Module;
33
34 public class ApiDocGeneratorTest {
35
36     private static final String NAMESPACE = "http://netconfcentral.org/ns/toaster2";
37     private static final String STRING_DATE = "2009-11-20";
38     private static final Date DATE = Date.valueOf(STRING_DATE);
39     private static final String NAMESPACE_2 = "http://netconfcentral.org/ns/toaster";
40     private static final Date REVISION_2 = Date.valueOf(STRING_DATE);
41     private ApiDocGeneratorDraftO2 generator;
42     private DocGenTestHelper helper;
43     private EffectiveModelContext schemaContext;
44
45     @Before
46     public void setUp() throws Exception {
47         this.helper = new DocGenTestHelper();
48         this.helper.setUp();
49
50         this.schemaContext = this.helper.getSchemaContext();
51
52         this.generator = new ApiDocGeneratorDraftO2(this.helper.createMockSchemaService(this.schemaContext));
53     }
54
55     /**
56      * Method: getApiDeclaration(String module, String revision, UriInfo uriInfo).
57      */
58     @Test
59     public void testGetModuleDoc() throws Exception {
60         Preconditions.checkArgument(this.helper.getModules() != null, "No modules found");
61
62         for (final Module m : this.helper.getSchemaContext().getModules()) {
63             if (m.getQNameModule().getNamespace().toString().equals(NAMESPACE)
64                     && m.getQNameModule().getRevision().equals(DATE)) {
65                 final SwaggerObject doc = this.generator.getSwaggerDocSpec(m, "http","localhost:8181", "/", "",
66                         this.schemaContext, URIType.DRAFT02, ApiDocServiceImpl.OAversion.V2_0);
67                 validateToaster(doc);
68                 validateSwaggerModules(doc);
69             }
70         }
71     }
72
73     /**
74      * Validates whether doc {@code doc} contains concrete specified models.
75      */
76     private static void validateSwaggerModules(final SwaggerObject doc) {
77         final ObjectNode definitions = doc.getDefinitions();
78         assertNotNull(definitions);
79
80         final JsonNode configLstTop = definitions.get("toaster2_config_lst_TOP");
81         assertNotNull(configLstTop);
82
83         containsReferences(configLstTop, "lst", "toaster2_config_lst");
84
85         final JsonNode configLst = definitions.get("toaster2_config_lst");
86         assertNotNull(configLst);
87
88         containsReferences(configLst, "lst1", "toaster2_lst_config_lst1");
89         containsReferences(configLst, "cont1", "toaster2_lst_config_cont1");
90
91         final JsonNode configLst1Top = definitions.get("toaster2_lst_config_lst1_TOP");
92         assertNotNull(configLst1Top);
93
94         containsReferences(configLst1Top, "lst1", "toaster2_config_lst");
95
96         final JsonNode configLst1 = definitions.get("toaster2_lst_config_lst1");
97         assertNotNull(configLst1);
98
99         final JsonNode configCont1Top = definitions.get("toaster2_lst_config_cont1_TOP");
100         assertNotNull(configCont1Top);
101
102         containsReferences(configCont1Top, "cont1", "toaster2_config_lst");
103         final JsonNode configCont1 = definitions.get("toaster2_lst_config_cont1");
104         assertNotNull(configCont1);
105
106         containsReferences(configCont1, "cont11", "toaster2_lst_config_cont1");
107         containsReferences(configCont1, "lst11", "toaster2_lst_config_lst1");
108
109         final JsonNode configCont11Top = definitions.get("toaster2_lst_cont1_config_cont11_TOP");
110         assertNotNull(configCont11Top);
111
112         containsReferences(configCont11Top, "cont11", "toaster2_lst_config_cont1");
113         final JsonNode configCont11 = definitions.get("toaster2_lst_cont1_config_cont11");
114         assertNotNull(configCont11);
115
116         final JsonNode configLst11Top = definitions.get("toaster2_lst_cont1_config_lst11_TOP");
117         assertNotNull(configLst11Top);
118
119         containsReferences(configLst11Top, "lst11", "toaster2_lst_config_cont1");
120         final JsonNode configLst11 = definitions.get("toaster2_lst_cont1_config_lst11");
121         assertNotNull(configLst11);
122     }
123
124     /**
125      * Checks whether object {@code mainObject} contains in properties/items key $ref with concrete value.
126      */
127     private static void containsReferences(final JsonNode mainObject, final String childObject,
128                                     final String expectedRef) {
129         final JsonNode properties = mainObject.get("properties");
130         assertNotNull(properties);
131
132         final JsonNode childNode = properties.get(childObject);
133         assertNotNull(childNode);
134
135         //list case
136         JsonNode refWrapper = childNode.get("items");
137         if (refWrapper == null) {
138             //container case
139             refWrapper = childNode;
140         }
141         assertEquals(expectedRef, refWrapper.get("$ref").asText());
142     }
143
144     @Test
145     public void testEdgeCases() throws Exception {
146         Preconditions.checkArgument(this.helper.getModules() != null, "No modules found");
147
148         for (final Module m : this.helper.getModules()) {
149             if (m.getQNameModule().getNamespace().toString().equals(NAMESPACE_2)
150                     && m.getQNameModule().getRevision().equals(REVISION_2)) {
151                 final SwaggerObject doc = this.generator.getSwaggerDocSpec(m, "http", "localhost:8080", "/restconf", "",
152                         this.schemaContext, URIType.DRAFT02, ApiDocServiceImpl.OAversion.V2_0);
153                 assertNotNull(doc);
154
155                 // testing bugs.opendaylight.org bug 1290. UnionType model type.
156                 final String jsonString = doc.getDefinitions().toString();
157                 assertTrue(jsonString.contains("testUnion\":{\"required\":false,\"type\":\"-2147483648\","
158                         + "\"enum\":[\"-2147483648\",\"Some testUnion\"]}"));
159             }
160         }
161     }
162
163     @Test
164     public void testRPCsModel() throws Exception {
165         Preconditions.checkArgument(this.helper.getModules() != null, "No modules found");
166
167         for (final Module m : this.helper.getModules()) {
168             if (m.getQNameModule().getNamespace().toString().equals(NAMESPACE_2)
169                     && m.getQNameModule().getRevision().equals(REVISION_2)) {
170                 final SwaggerObject doc = this.generator.getSwaggerDocSpec(m, "http","localhost:8181", "/", "",
171                         this.schemaContext, URIType.DRAFT02, ApiDocServiceImpl.OAversion.V2_0);
172                 assertNotNull(doc);
173
174                 final ObjectNode definitions = doc.getDefinitions();
175                 final JsonNode inputTop = definitions.get("toaster_make-toast_input_TOP");
176                 assertNotNull(inputTop);
177                 final String testString = "{\"input\":{\"$ref\":\"toaster_make-toast_input\"}}";
178                 assertEquals(testString, inputTop.get("properties").toString());
179                 final JsonNode input = definitions.get("toaster_make-toast_input");
180                 final JsonNode properties = input.get("properties");
181                 assertTrue(properties.has("toasterDoneness"));
182                 assertTrue(properties.has("toasterToastType"));
183             }
184         }
185     }
186
187     /**
188      * Tests whether from yang files are generated all required paths for HTTP operations (GET, DELETE, PUT, POST)
189      *
190      * <p>
191      * If container | list is augmented then in path there should be specified module name followed with colon (e. g.
192      * "/config/module1:element1/element2/module2:element3")
193      *
194      * @param doc Api declaration
195      * @throws Exception if operation fails
196      */
197     private static void validateToaster(final SwaggerObject doc) throws Exception {
198         final Set<String> expectedUrls =
199                 new TreeSet<>(Arrays.asList("/restconf/config", "/restconf/config/toaster2:toaster",
200                         "/restconf/config/toaster2:toaster/toasterSlot/{slotId}",
201                         "/restconf/config/toaster2:toaster/toasterSlot/{slotId}/toaster-augmented:slotInfo",
202                         "/restconf/operational/toaster2:toaster/toasterSlot/{slotId}",
203                         "/restconf/operational/toaster2:toaster/toasterSlot/{slotId}/toaster-augmented:slotInfo",
204                         "/restconf/config/toaster2:lst",
205                         "/restconf/config/toaster2:lst/cont1",
206                         "/restconf/config/toaster2:lst/cont1/cont11",
207                         "/restconf/config/toaster2:lst/cont1/lst11",
208                         "/restconf/config/toaster2:lst/lst1/{key1}/{key2}",
209                         "/restconf/operational/toaster2:lst",
210                         "/restconf/operational/toaster2:lst/cont1",
211                         "/restconf/operational/toaster2:lst/cont1/cont11",
212                         "/restconf/operational/toaster2:lst/cont1/lst11",
213                         "/restconf/operational/toaster2:lst/lst1/{key1}/{key2}",
214                         "/restconf/operational/toaster2:lst/lst1/{key1}/{key2}",
215                         "/restconf/operations/toaster2:make-toast",
216                         "/restconf/operations/toaster2:cancel-toast"));
217
218
219         final Set<String> actualUrls = new TreeSet<>();
220
221         final Set<JsonNode> configActualPathItems = new HashSet<>();
222         for (final JsonNode pathItem : doc.getPaths()) {
223             final String actualUrl = pathItem.fieldNames().next();
224             actualUrls.add(actualUrl);
225             if (actualUrl.contains("/config/toaster2:toaster/")) {
226                 configActualPathItems.add(pathItem);
227             }
228         }
229
230         final boolean isAllDocumented = actualUrls.containsAll(expectedUrls);
231
232         if (!isAllDocumented) {
233             expectedUrls.removeAll(actualUrls);
234             fail("Missing expected urls: " + expectedUrls);
235         }
236
237         final Set<String> expectedConfigMethods = new TreeSet<>(Arrays.asList("get", "put", "delete", "post"));
238
239         for (final JsonNode configPathItem : configActualPathItems) {
240             final Set<String> actualConfigMethods = new TreeSet<>();
241             final JsonNode operations =  configPathItem.get(0);
242             final Iterator<String> it = operations.fieldNames();
243             while (it.hasNext()) {
244                 actualConfigMethods.add(it.next());
245             }
246             final boolean isAllMethods = actualConfigMethods.containsAll(expectedConfigMethods);
247             if (!isAllMethods) {
248                 expectedConfigMethods.removeAll(actualConfigMethods);
249                 fail("Missing expected method on config API: " + expectedConfigMethods);
250             }
251         }
252
253         // TODO: we should really do some more validation of the
254         // documentation...
255         /*
256          * Missing validation: Explicit validation of URLs, and their methods Input / output models.
257          */
258     }
259
260 }