|
20 | 20 | package org.apache.axis2.openapi; |
21 | 21 |
|
22 | 22 | import io.swagger.v3.oas.models.OpenAPI; |
| 23 | +import io.swagger.v3.oas.models.Operation; |
| 24 | +import io.swagger.v3.oas.models.PathItem; |
23 | 25 | import io.swagger.v3.oas.models.info.Info; |
| 26 | +import io.swagger.v3.oas.models.parameters.RequestBody; |
24 | 27 | import io.swagger.v3.oas.models.servers.Server; |
25 | 28 | import junit.framework.TestCase; |
26 | 29 | import org.apache.axis2.context.ConfigurationContext; |
@@ -232,6 +235,149 @@ public void testPathGeneration() throws Exception { |
232 | 235 | // Note: Actual path structure depends on service configuration |
233 | 236 | } |
234 | 237 |
|
| 238 | + /** |
| 239 | + * Test that generated JSON contains no null fields. |
| 240 | + * Jackson must be configured with Include.NON_NULL so null-valued model |
| 241 | + * fields (e.g. termsOfService, extensions, summary) are omitted entirely. |
| 242 | + */ |
| 243 | + public void testNoNullFieldsInJson() throws Exception { |
| 244 | + String json = generator.generateOpenApiJson(mockRequest); |
| 245 | + |
| 246 | + assertFalse("JSON output must not contain ': null' entries", json.contains(": null")); |
| 247 | + assertFalse("JSON output must not contain ':null' entries", json.contains(":null")); |
| 248 | + } |
| 249 | + |
| 250 | + /** |
| 251 | + * Test that each generated operation carries a non-null requestBody. |
| 252 | + * All JSON-RPC services accept a POST body; omitting requestBody leaves |
| 253 | + * clients with no schema hint. Mirrors the pattern in financial-api-schema.json. |
| 254 | + */ |
| 255 | + public void testRequestBodyPresentOnOperation() throws Exception { |
| 256 | + // Arrange — register a service with one operation |
| 257 | + AxisService svc = new AxisService("OrderService"); |
| 258 | + AxisOperation op = new org.apache.axis2.description.InOutAxisOperation(); |
| 259 | + op.setName(javax.xml.namespace.QName.valueOf("placeOrder")); |
| 260 | + svc.addOperation(op); |
| 261 | + axisConfiguration.addService(svc); |
| 262 | + |
| 263 | + // Act |
| 264 | + OpenAPI openApi = generator.generateOpenApiSpec(mockRequest); |
| 265 | + |
| 266 | + // Assert — the path for the operation must exist and have a requestBody |
| 267 | + String expectedPath = "/services/OrderService/placeOrder"; |
| 268 | + assertNotNull("Path should exist for registered operation", openApi.getPaths()); |
| 269 | + PathItem pathItem = openApi.getPaths().get(expectedPath); |
| 270 | + assertNotNull("PathItem must be present at " + expectedPath, pathItem); |
| 271 | + |
| 272 | + Operation postOp = pathItem.getPost(); |
| 273 | + assertNotNull("Operation must be a POST", postOp); |
| 274 | + |
| 275 | + RequestBody requestBody = postOp.getRequestBody(); |
| 276 | + assertNotNull("requestBody must not be null", requestBody); |
| 277 | + assertTrue("requestBody must be required", Boolean.TRUE.equals(requestBody.getRequired())); |
| 278 | + assertNotNull("requestBody must have content", requestBody.getContent()); |
| 279 | + assertNotNull("requestBody must declare application/json media type", |
| 280 | + requestBody.getContent().get("application/json")); |
| 281 | + } |
| 282 | + |
| 283 | + /** |
| 284 | + * Test that generated YAML is genuine YAML, not JSON. |
| 285 | + * financial-api-schema.json demonstrates that a proper OpenAPI endpoint |
| 286 | + * should serve parseable YAML when /openapi.yaml is requested. |
| 287 | + */ |
| 288 | + public void testYamlGenerationIsActualYaml() throws Exception { |
| 289 | + String yaml = generator.generateOpenApiYaml(mockRequest); |
| 290 | + |
| 291 | + assertNotNull("YAML should be generated", yaml); |
| 292 | + assertFalse("YAML must not start with '{' (that would be JSON)", yaml.trim().startsWith("{")); |
| 293 | + assertTrue("YAML must contain openapi key in YAML style", yaml.contains("openapi:")); |
| 294 | + } |
| 295 | + |
| 296 | + /** |
| 297 | + * Test that the financial-api-schema.json advanced features are structurally |
| 298 | + * sound — components/schemas with $ref, required requestBodies, security |
| 299 | + * schemes, error responses, and both GET and POST operations. |
| 300 | + * |
| 301 | + * This test reads the schema from disk and validates its advanced features, |
| 302 | + * confirming the test infrastructure can parse and assert on production-grade |
| 303 | + * OpenAPI specs of the kind the generator should eventually produce. |
| 304 | + */ |
| 305 | + public void testFinancialApiSchemaAdvancedFeatures() throws Exception { |
| 306 | + // Load the financial schema from the swagger-server sample resources |
| 307 | + java.io.InputStream is = getClass().getClassLoader() |
| 308 | + .getResourceAsStream("openapi/financial-api-schema.json"); |
| 309 | + if (is == null) { |
| 310 | + // File is in the swagger-server module, not on this module's classpath — |
| 311 | + // load it from the filesystem relative to the repo root. |
| 312 | + java.io.File schemaFile = new java.io.File( |
| 313 | + "../../samples/swagger-server/src/main/resources/openapi/financial-api-schema.json"); |
| 314 | + if (!schemaFile.exists()) { |
| 315 | + // Skip gracefully when running outside the full repo checkout |
| 316 | + return; |
| 317 | + } |
| 318 | + is = new java.io.FileInputStream(schemaFile); |
| 319 | + } |
| 320 | + |
| 321 | + com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper(); |
| 322 | + com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(is); |
| 323 | + |
| 324 | + // --- Basic version --- |
| 325 | + assertEquals("openapi version must be 3.0.1", "3.0.1", root.get("openapi").asText()); |
| 326 | + |
| 327 | + // --- Components/schemas: advanced feature — schema definitions with $ref --- |
| 328 | + com.fasterxml.jackson.databind.JsonNode schemas = root.path("components").path("schemas"); |
| 329 | + assertFalse("components/schemas must be present", schemas.isMissingNode()); |
| 330 | + assertTrue("LoginRequest schema must be defined", schemas.has("LoginRequest")); |
| 331 | + assertTrue("LoginResponse schema must be defined", schemas.has("LoginResponse")); |
| 332 | + |
| 333 | + // LoginRequest must declare required fields |
| 334 | + com.fasterxml.jackson.databind.JsonNode loginReqRequired = schemas.path("LoginRequest").path("required"); |
| 335 | + assertFalse("LoginRequest must have required array", loginReqRequired.isMissingNode()); |
| 336 | + assertTrue("LoginRequest required must include 'email'", |
| 337 | + loginReqRequired.toString().contains("email")); |
| 338 | + |
| 339 | + // --- $ref usage inside a schema --- |
| 340 | + com.fasterxml.jackson.databind.JsonNode loginRespUserInfo = |
| 341 | + schemas.path("LoginResponse").path("properties").path("userInfo"); |
| 342 | + assertFalse("LoginResponse.userInfo must be present", loginRespUserInfo.isMissingNode()); |
| 343 | + assertTrue("LoginResponse.userInfo must use $ref", |
| 344 | + loginRespUserInfo.has("$ref")); |
| 345 | + |
| 346 | + // --- Security schemes --- |
| 347 | + com.fasterxml.jackson.databind.JsonNode securitySchemes = |
| 348 | + root.path("components").path("securitySchemes"); |
| 349 | + assertFalse("securitySchemes must be present", securitySchemes.isMissingNode()); |
| 350 | + assertTrue("bearerAuth scheme must be defined", securitySchemes.has("bearerAuth")); |
| 351 | + assertEquals("bearerAuth type must be 'http'", |
| 352 | + "http", securitySchemes.path("bearerAuth").path("type").asText()); |
| 353 | + assertEquals("bearerAuth scheme must be 'bearer'", |
| 354 | + "bearer", securitySchemes.path("bearerAuth").path("scheme").asText()); |
| 355 | + |
| 356 | + // --- requestBody required on POST operations --- |
| 357 | + com.fasterxml.jackson.databind.JsonNode loginPath = root.path("paths").path("/bigdataservice/login"); |
| 358 | + assertFalse("login path must be present", loginPath.isMissingNode()); |
| 359 | + com.fasterxml.jackson.databind.JsonNode loginPost = loginPath.path("post"); |
| 360 | + assertFalse("login POST must be present", loginPost.isMissingNode()); |
| 361 | + assertTrue("login POST requestBody must be required", |
| 362 | + loginPost.path("requestBody").path("required").asBoolean()); |
| 363 | + |
| 364 | + // --- Error responses (400 / 401) --- |
| 365 | + com.fasterxml.jackson.databind.JsonNode loginResponses = loginPost.path("responses"); |
| 366 | + assertTrue("login must declare 401 response", loginResponses.has("401")); |
| 367 | + |
| 368 | + // --- GET operations (user/info, user/permissions) --- |
| 369 | + com.fasterxml.jackson.databind.JsonNode userInfoPath = root.path("paths").path("/bigdataservice/user/info"); |
| 370 | + assertFalse("user/info path must be present", userInfoPath.isMissingNode()); |
| 371 | + assertFalse("user/info must be a GET operation", userInfoPath.path("get").isMissingNode()); |
| 372 | + |
| 373 | + // --- Operation-level security (distinct from global) --- |
| 374 | + com.fasterxml.jackson.databind.JsonNode fundsSecurity = |
| 375 | + root.path("paths").path("/bigdataservice/funds/summary").path("post").path("security"); |
| 376 | + assertFalse("funds/summary must declare per-operation security", fundsSecurity.isMissingNode()); |
| 377 | + assertTrue("per-operation security must reference bearerAuth", |
| 378 | + fundsSecurity.toString().contains("bearerAuth")); |
| 379 | + } |
| 380 | + |
235 | 381 | /** |
236 | 382 | * Test error handling in JSON generation. |
237 | 383 | * Verifies graceful handling of generation failures. |
|
0 commit comments