[tests-only] check for invalid schema validators (#8533)
* test(api): check schema validators * test(api): check schema validators * test(api): check schema validators * test(api): fix schema * test(api): fix php code style * test(api): fix schema
This commit is contained in:
@@ -85,7 +85,7 @@ Feature: Notification
|
|||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enim": ["%user_id%"]
|
"pattern": "^%user_id_pattern%$"
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -97,9 +97,7 @@ Feature: Notification
|
|||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enim": [
|
"pattern": "^%user_id_pattern%$"
|
||||||
"%user_id%"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -232,9 +230,7 @@ Feature: Notification
|
|||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enim": [
|
"pattern": "^%user_id_pattern%$"
|
||||||
"%user_id%"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -368,9 +364,7 @@ Feature: Notification
|
|||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enim": [
|
"pattern": "^%user_id_pattern%$"
|
||||||
"%user_id%"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -553,8 +553,6 @@ Feature: Create a share link for a resource
|
|||||||
},
|
},
|
||||||
"link": {
|
"link": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"minItems": 5,
|
|
||||||
"maxItems": 5,
|
|
||||||
"required": [
|
"required": [
|
||||||
"@libre.graph.displayName",
|
"@libre.graph.displayName",
|
||||||
"@libre.graph.quickLink",
|
"@libre.graph.quickLink",
|
||||||
|
|||||||
@@ -102,8 +102,6 @@ Feature: Update permission of a share
|
|||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"minItems": 3,
|
|
||||||
"maxItems": 3,
|
|
||||||
"required": [
|
"required": [
|
||||||
"grantedToV2",
|
"grantedToV2",
|
||||||
"id",
|
"id",
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ class FeatureContext extends BehatVariablesContext {
|
|||||||
use Sharing;
|
use Sharing;
|
||||||
use WebDav;
|
use WebDav;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* json schema validator keywords
|
||||||
|
* See: https://json-schema.org/draft-06/draft-wright-json-schema-validation-01#rfc.section.6
|
||||||
|
*/
|
||||||
|
private array $jsonSchemaValidators = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unix timestamp seconds
|
* Unix timestamp seconds
|
||||||
*/
|
*/
|
||||||
@@ -572,6 +578,8 @@ class FeatureContext extends BehatVariablesContext {
|
|||||||
$this->publicLinkSharePassword = $publicLinkSharePasswordFromEnvironment;
|
$this->publicLinkSharePassword = $publicLinkSharePasswordFromEnvironment;
|
||||||
}
|
}
|
||||||
$this->originalAdminPassword = $this->adminPassword;
|
$this->originalAdminPassword = $this->adminPassword;
|
||||||
|
|
||||||
|
$this->jsonSchemaValidators = \array_keys(JsonSchema::properties()->getDataKeyMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1206,6 +1214,56 @@ class FeatureContext extends BehatVariablesContext {
|
|||||||
return $a;
|
return $a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param JsonSchema $schemaObj
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function checkInvalidValidator(JsonSchema $schemaObj): void {
|
||||||
|
$validators = \array_keys((array)$schemaObj->jsonSerialize());
|
||||||
|
foreach ($validators as $validator) {
|
||||||
|
Assert::assertContains(\ltrim($validator, "$"), $this->jsonSchemaValidators, "Invalid schema validator: '$validator'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates against the requirements that object schema should adhere to
|
||||||
|
*
|
||||||
|
* @param JsonSchema $schemaObj
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function validateSchemaObject(JsonSchema $schemaObj): void {
|
||||||
|
$this->checkInvalidValidator($schemaObj);
|
||||||
|
|
||||||
|
if ($schemaObj->type && $schemaObj->type !== "object") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$notAllowedValidators = ["items", "maxItems", "minItems", "uniqueItems"];
|
||||||
|
|
||||||
|
// check invalid validators
|
||||||
|
foreach ($notAllowedValidators as $validator) {
|
||||||
|
Assert::assertTrue(null === $schemaObj->$validator, "'$validator' should not be used with object type");
|
||||||
|
}
|
||||||
|
|
||||||
|
$propNames = $schemaObj->getPropertyNames();
|
||||||
|
$props = $schemaObj->getProperties();
|
||||||
|
foreach ($propNames as $propName) {
|
||||||
|
switch ($props->type) {
|
||||||
|
case "array":
|
||||||
|
$this->validateSchemaArray($props->$propName);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// traverse for nested properties
|
||||||
|
$this->validateSchemaObject($props->$propName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates against the requirements that array schema should adhere to
|
* Validates against the requirements that array schema should adhere to
|
||||||
*
|
*
|
||||||
@@ -1214,7 +1272,13 @@ class FeatureContext extends BehatVariablesContext {
|
|||||||
* @return void
|
* @return void
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private function validateSchemaArrayEntries(JsonSchema $schemaObj): void {
|
private function validateSchemaArray(JsonSchema $schemaObj): void {
|
||||||
|
$this->checkInvalidValidator($schemaObj);
|
||||||
|
|
||||||
|
if ($schemaObj->type && $schemaObj->type !== "array") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$hasTwoElementValidator = ($schemaObj->enum && $schemaObj->const) || ($schemaObj->enum && $schemaObj->items) || ($schemaObj->const && $schemaObj->items);
|
$hasTwoElementValidator = ($schemaObj->enum && $schemaObj->const) || ($schemaObj->enum && $schemaObj->items) || ($schemaObj->const && $schemaObj->items);
|
||||||
Assert::assertFalse($hasTwoElementValidator, "'items', 'enum' and 'const' should not be used together");
|
Assert::assertFalse($hasTwoElementValidator, "'items', 'enum' and 'const' should not be used together");
|
||||||
if ($schemaObj->enum || $schemaObj->const) {
|
if ($schemaObj->enum || $schemaObj->const) {
|
||||||
@@ -1224,20 +1288,26 @@ class FeatureContext extends BehatVariablesContext {
|
|||||||
|
|
||||||
$requiredValidators = ["maxItems", "minItems"];
|
$requiredValidators = ["maxItems", "minItems"];
|
||||||
$optionalValidators = ["items", "uniqueItems"];
|
$optionalValidators = ["items", "uniqueItems"];
|
||||||
|
$notAllowedValidators = ["properties", "minProperties", "maxProperties", "required"];
|
||||||
$errMsg = "'%s' is required for array assertion";
|
$errMsg = "'%s' is required for array assertion";
|
||||||
|
|
||||||
// validate required keywords
|
// check invalid validators
|
||||||
|
foreach ($notAllowedValidators as $validator) {
|
||||||
|
Assert::assertTrue($schemaObj->$validator === null, "'$validator' should not be used with array type");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check required validators
|
||||||
foreach ($requiredValidators as $validator) {
|
foreach ($requiredValidators as $validator) {
|
||||||
Assert::assertNotNull($schemaObj->$validator, \sprintf($errMsg, $validator));
|
Assert::assertNotNull($schemaObj->$validator, \sprintf($errMsg, $validator));
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert::assertEquals($schemaObj->minItems, $schemaObj->maxItems, "'minItems' and 'maxItems' should be equal for strict assertion");
|
Assert::assertEquals($schemaObj->minItems, $schemaObj->maxItems, "'minItems' and 'maxItems' should be equal for strict assertion");
|
||||||
|
|
||||||
// validate optional keywords
|
// check optional validators
|
||||||
foreach ($optionalValidators as $validator) {
|
foreach ($optionalValidators as $validator) {
|
||||||
$value = $schemaObj->$validator;
|
$value = $schemaObj->$validator;
|
||||||
switch ($validator) {
|
switch ($validator) {
|
||||||
case 'items':
|
case "items":
|
||||||
if ($schemaObj->maxItems === 0) {
|
if ($schemaObj->maxItems === 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1267,6 +1337,15 @@ class FeatureContext extends BehatVariablesContext {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$items = $schemaObj->items;
|
||||||
|
if ($items !== null && $items->oneOf !== null) {
|
||||||
|
foreach ($items->oneOf as $oneOfItem) {
|
||||||
|
$this->validateSchemaObject($oneOfItem);
|
||||||
|
}
|
||||||
|
} elseif ($items !== null) {
|
||||||
|
$this->validateSchemaObject($items);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1278,29 +1357,17 @@ class FeatureContext extends BehatVariablesContext {
|
|||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function validateSchemaRequirements(JsonSchema $schema): void {
|
public function validateSchemaRequirements(JsonSchema $schema): void {
|
||||||
$propNames = $schema->getPropertyNames();
|
Assert::assertNotNull($schema->type, "'type' is required for root level schema");
|
||||||
$props = $schema->getProperties();
|
|
||||||
foreach ($propNames as $propName) {
|
switch ($schema->type) {
|
||||||
switch ($props->$propName->type) {
|
case "object":
|
||||||
case 'array':
|
$this->validateSchemaObject($schema);
|
||||||
$this->validateSchemaArrayEntries($props->$propName);
|
break;
|
||||||
$items = $props->$propName->items;
|
case "array":
|
||||||
if ($items && $items->oneOf) {
|
$this->validateSchemaArray($schema);
|
||||||
foreach ($items->oneOf as $oneOfItem) {
|
break;
|
||||||
$this->validateSchemaRequirements($oneOfItem);
|
default:
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
} elseif ($items) {
|
|
||||||
$this->validateSchemaRequirements($items);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// traverse for nested properties
|
|
||||||
if ($props->$propName->getProperties()) {
|
|
||||||
$this->validateSchemaRequirements($props->$propName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user