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.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;
34 public class ApiDocGeneratorTest {
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;
46 public void setUp() throws Exception {
47 this.helper = new DocGenTestHelper();
50 this.schemaContext = this.helper.getSchemaContext();
52 this.generator = new ApiDocGeneratorDraftO2(this.helper.createMockSchemaService(this.schemaContext));
56 * Method: getApiDeclaration(String module, String revision, UriInfo uriInfo).
59 public void testGetModuleDoc() throws Exception {
60 Preconditions.checkArgument(this.helper.getModules() != null, "No modules found");
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);
68 validateSwaggerModules(doc);
74 * Validates whether doc {@code doc} contains concrete specified models.
76 private static void validateSwaggerModules(final SwaggerObject doc) {
77 final ObjectNode definitions = doc.getDefinitions();
78 assertNotNull(definitions);
80 final JsonNode configLstTop = definitions.get("toaster2_config_lst_TOP");
81 assertNotNull(configLstTop);
83 containsReferences(configLstTop, "lst", "toaster2_config_lst");
85 final JsonNode configLst = definitions.get("toaster2_config_lst");
86 assertNotNull(configLst);
88 containsReferences(configLst, "lst1", "toaster2_lst_config_lst1");
89 containsReferences(configLst, "cont1", "toaster2_lst_config_cont1");
91 final JsonNode configLst1Top = definitions.get("toaster2_lst_config_lst1_TOP");
92 assertNotNull(configLst1Top);
94 containsReferences(configLst1Top, "lst1", "toaster2_config_lst");
96 final JsonNode configLst1 = definitions.get("toaster2_lst_config_lst1");
97 assertNotNull(configLst1);
99 final JsonNode configCont1Top = definitions.get("toaster2_lst_config_cont1_TOP");
100 assertNotNull(configCont1Top);
102 containsReferences(configCont1Top, "cont1", "toaster2_config_lst");
103 final JsonNode configCont1 = definitions.get("toaster2_lst_config_cont1");
104 assertNotNull(configCont1);
106 containsReferences(configCont1, "cont11", "toaster2_lst_config_cont1");
107 containsReferences(configCont1, "lst11", "toaster2_lst_config_lst1");
109 final JsonNode configCont11Top = definitions.get("toaster2_lst_cont1_config_cont11_TOP");
110 assertNotNull(configCont11Top);
112 containsReferences(configCont11Top, "cont11", "toaster2_lst_config_cont1");
113 final JsonNode configCont11 = definitions.get("toaster2_lst_cont1_config_cont11");
114 assertNotNull(configCont11);
116 final JsonNode configLst11Top = definitions.get("toaster2_lst_cont1_config_lst11_TOP");
117 assertNotNull(configLst11Top);
119 containsReferences(configLst11Top, "lst11", "toaster2_lst_config_cont1");
120 final JsonNode configLst11 = definitions.get("toaster2_lst_cont1_config_lst11");
121 assertNotNull(configLst11);
125 * Checks whether object {@code mainObject} contains in properties/items key $ref with concrete value.
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);
132 final JsonNode childNode = properties.get(childObject);
133 assertNotNull(childNode);
136 JsonNode refWrapper = childNode.get("items");
137 if (refWrapper == null) {
139 refWrapper = childNode;
141 assertEquals(expectedRef, refWrapper.get("$ref").asText());
145 public void testEdgeCases() throws Exception {
146 Preconditions.checkArgument(this.helper.getModules() != null, "No modules found");
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);
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\"]}"));
164 public void testRPCsModel() throws Exception {
165 Preconditions.checkArgument(this.helper.getModules() != null, "No modules found");
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);
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"));
188 * Tests whether from yang files are generated all required paths for HTTP operations (GET, DELETE, PUT, POST)
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")
194 * @param doc Api declaration
195 * @throws Exception if operation fails
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"));
219 final Set<String> actualUrls = new TreeSet<>();
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);
230 final boolean isAllDocumented = actualUrls.containsAll(expectedUrls);
232 if (!isAllDocumented) {
233 expectedUrls.removeAll(actualUrls);
234 fail("Missing expected urls: " + expectedUrls);
237 final Set<String> expectedConfigMethods = new TreeSet<>(Arrays.asList("get", "put", "delete", "post"));
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());
246 final boolean isAllMethods = actualConfigMethods.containsAll(expectedConfigMethods);
248 expectedConfigMethods.removeAll(actualConfigMethods);
249 fail("Missing expected method on config API: " + expectedConfigMethods);
253 // TODO: we should really do some more validation of the
256 * Missing validation: Explicit validation of URLs, and their methods Input / output models.