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