mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 11:06:49 +00:00
Schema Validation tests
This commit is contained in:
parent
aaf04ee036
commit
7a27d3ac0d
@ -33,14 +33,8 @@ object JsonSchemaGenerator {
|
||||
("message_format" -> messageDoc.messageFormat) ~
|
||||
("outbound_topic" -> messageDoc.outboundTopic) ~
|
||||
("inbound_topic" -> messageDoc.inboundTopic) ~
|
||||
("outbound_schema" -> (
|
||||
("$schema" -> "http://json-schema.org/draft-07/schema#") ~
|
||||
typeToJsonSchema(outboundType)
|
||||
)) ~
|
||||
("inbound_schema" -> (
|
||||
("$schema" -> "http://json-schema.org/draft-07/schema#") ~
|
||||
typeToJsonSchema(inboundType)
|
||||
)) ~
|
||||
("outbound_schema" -> typeToJsonSchema(outboundType)) ~
|
||||
("inbound_schema" -> typeToJsonSchema(inboundType)) ~
|
||||
("adapter_implementation" -> messageDoc.adapterImplementation.map { impl =>
|
||||
("group" -> impl.group) ~
|
||||
("suggested_order" -> JInt(BigInt(impl.suggestedOrder)))
|
||||
|
||||
@ -4,13 +4,36 @@ import code.api.util.APIUtil.OAuth._
|
||||
import code.api.util.ApiRole
|
||||
import code.api.util.ErrorMessages.InvalidConnector
|
||||
import code.api.v6_0_0.OBPAPI6_0_0.Implementations6_0_0
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.networknt.schema.{JsonSchemaFactory, SpecVersion}
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import net.liftweb.json._
|
||||
import org.scalatest.Tag
|
||||
|
||||
/**
|
||||
* Tests for the Message Docs JSON Schema endpoint (v6.0.0)
|
||||
*
|
||||
* This endpoint returns message documentation as JSON Schema format for code generation.
|
||||
* The schema follows JSON Schema draft-07 specification and is validated using the
|
||||
* networknt/json-schema-validator library (https://github.com/networknt/json-schema-validator).
|
||||
*
|
||||
* Schema structure:
|
||||
* - Root level: $schema, title, description, type, properties, definitions
|
||||
* - Each message includes: process, description, outbound_schema, inbound_schema
|
||||
* - Type definitions use $ref references to the definitions section
|
||||
* - All definitions have: type: "object", properties, required (for non-Option fields)
|
||||
*
|
||||
* Industry Standard Compliance:
|
||||
* - Validated against JSON Schema draft-07 meta-schema
|
||||
* - Uses standard $ref for type references
|
||||
* - Compatible with code generation tools like quicktype
|
||||
*/
|
||||
class MessageDocsJsonSchemaTest extends V600ServerSetup {
|
||||
|
||||
|
||||
// Jackson ObjectMapper for converting between Lift JSON and Jackson JsonNode
|
||||
private val mapper = new ObjectMapper()
|
||||
|
||||
override def beforeAll(): Unit = {
|
||||
super.beforeAll()
|
||||
}
|
||||
@ -77,25 +100,17 @@ class MessageDocsJsonSchemaTest extends V600ServerSetup {
|
||||
|
||||
And("Outbound schema should be valid JSON Schema")
|
||||
val outboundSchema = (firstMessage \ "outbound_schema").extract[JObject]
|
||||
val outboundSchemaVersion = (outboundSchema \ "$schema").extractOpt[String]
|
||||
outboundSchemaVersion shouldBe defined
|
||||
|
||||
// Schema can have either a direct "type" or a "$ref" to definitions for case classes
|
||||
val outboundType = (outboundSchema \ "type").extractOpt[String]
|
||||
outboundType shouldBe Some("object")
|
||||
|
||||
val outboundProperties = (outboundSchema \ "properties").extractOpt[JObject]
|
||||
outboundProperties shouldBe defined
|
||||
|
||||
val outboundRef = (outboundSchema \ "$ref").extractOpt[String]
|
||||
(outboundType.isDefined || outboundRef.isDefined) shouldBe true
|
||||
|
||||
And("Inbound schema should be valid JSON Schema")
|
||||
val inboundSchema = (firstMessage \ "inbound_schema").extract[JObject]
|
||||
val inboundSchemaVersion = (inboundSchema \ "$schema").extractOpt[String]
|
||||
inboundSchemaVersion shouldBe defined
|
||||
|
||||
// Schema can have either a direct "type" or a "$ref" to definitions for case classes
|
||||
val inboundType = (inboundSchema \ "type").extractOpt[String]
|
||||
inboundType shouldBe Some("object")
|
||||
|
||||
val inboundProperties = (inboundSchema \ "properties").extractOpt[JObject]
|
||||
inboundProperties shouldBe defined
|
||||
val inboundRef = (inboundSchema \ "$ref").extractOpt[String]
|
||||
(inboundType.isDefined || inboundRef.isDefined) shouldBe true
|
||||
}
|
||||
|
||||
scenario("We get JSON Schema for rest_vMar2019 connector", ApiEndpoint1, VersionOfApi) {
|
||||
@ -130,14 +145,14 @@ class MessageDocsJsonSchemaTest extends V600ServerSetup {
|
||||
When("We make a request with invalid connector name")
|
||||
val request = (v6_0_0_Request / "message-docs" / "invalid_connector" / "json-schema").GET
|
||||
val response = makeGetRequest(request)
|
||||
|
||||
|
||||
Then("We should get a 400 Bad Request response")
|
||||
response.code should equal(400)
|
||||
|
||||
|
||||
And("Error message should mention invalid connector")
|
||||
val errorMessage = (response.body \ "message").extractOpt[String]
|
||||
errorMessage shouldBe defined
|
||||
errorMessage.get should include("InvalidConnector")
|
||||
errorMessage.get should include("Invalid Connector")
|
||||
}
|
||||
|
||||
scenario("We verify schema includes nested type definitions", ApiEndpoint1, VersionOfApi) {
|
||||
@ -201,19 +216,64 @@ class MessageDocsJsonSchemaTest extends V600ServerSetup {
|
||||
When("We make a request to get message docs as JSON Schema")
|
||||
val request = (v6_0_0_Request / "message-docs" / "rabbitmq_vOct2024" / "json-schema").GET
|
||||
val response = makeGetRequest(request)
|
||||
|
||||
|
||||
Then("We should get a 200 OK response")
|
||||
response.code should equal(200)
|
||||
|
||||
|
||||
And("Process names should follow obp.methodName pattern")
|
||||
val json = response.body.extract[JValue]
|
||||
val messages = (json \ "properties" \ "messages" \ "items").extract[List[JValue]]
|
||||
|
||||
|
||||
messages.foreach { message =>
|
||||
val process = (message \ "process").extract[String]
|
||||
process should startWith("obp.")
|
||||
process.length should be > 4
|
||||
}
|
||||
}
|
||||
|
||||
scenario("We validate schema is industry-standard JSON Schema draft-07 using networknt validator", ApiEndpoint1, VersionOfApi) {
|
||||
When("We make a request to get message docs as JSON Schema")
|
||||
val request = (v6_0_0_Request / "message-docs" / "rabbitmq_vOct2024" / "json-schema").GET
|
||||
val response = makeGetRequest(request)
|
||||
|
||||
Then("We should get a 200 OK response")
|
||||
response.code should equal(200)
|
||||
|
||||
And("Schema should be valid according to JSON Schema draft-07 specification")
|
||||
val schemaString = compactRender(response.body)
|
||||
val schemaNode = mapper.readTree(schemaString)
|
||||
|
||||
// Use networknt JSON Schema validator with draft-07
|
||||
val factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)
|
||||
val jsonSchema = factory.getSchema(schemaNode)
|
||||
|
||||
// The schema should load without errors (this validates the schema structure)
|
||||
jsonSchema should not be null
|
||||
|
||||
And("Schema should have valid definitions that can be resolved")
|
||||
val definitions = (response.body \ "definitions").extract[JObject]
|
||||
definitions.obj.length should be > 100 // Should have many type definitions
|
||||
|
||||
And("Each definition should be valid JSON Schema")
|
||||
definitions.obj.foreach { case JField(name, defn) =>
|
||||
val defnString = compactRender(defn)
|
||||
val defnNode = mapper.readTree(defnString)
|
||||
// Create a schema from each definition to validate it
|
||||
val defnSchema = factory.getSchema(defnNode)
|
||||
defnSchema should not be null
|
||||
}
|
||||
|
||||
And("$ref references should resolve correctly within the schema")
|
||||
val messages = (response.body \ "properties" \ "messages" \ "items").extract[List[JValue]]
|
||||
val firstMessage = messages.head
|
||||
val outboundRef = (firstMessage \ "outbound_schema" \ "$ref").extractOpt[String]
|
||||
outboundRef shouldBe defined
|
||||
outboundRef.get should startWith("#/definitions/")
|
||||
|
||||
// Extract the referenced definition name and verify it exists
|
||||
val refName = outboundRef.get.replace("#/definitions/", "")
|
||||
val definitionNames = definitions.obj.map(_.name)
|
||||
definitionNames should contain(refName)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user