Skip to content

Commit

Permalink
[tests-only] check for invalid schema validators (#8533)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
saw-jan authored Feb 28, 2024
1 parent b8fcf7c commit 9b8ec4e
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Feature: Notification
},
"id": {
"type": "string",
"enim": ["%user_id%"]
"pattern": "^%user_id_pattern%$"
},
"name": {
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ Feature: Notification
},
"id": {
"type": "string",
"enim": [
"%user_id%"
]
"pattern": "^%user_id_pattern%$"
},
"name": {
"type": "string",
Expand Down Expand Up @@ -232,9 +230,7 @@ Feature: Notification
},
"id": {
"type": "string",
"enim": [
"%user_id%"
]
"pattern": "^%user_id_pattern%$"
},
"name": {
"type": "string",
Expand Down Expand Up @@ -368,9 +364,7 @@ Feature: Notification
},
"id": {
"type": "string",
"enim": [
"%user_id%"
]
"pattern": "^%user_id_pattern%$"
},
"name": {
"type": "string",
Expand Down
2 changes: 0 additions & 2 deletions tests/acceptance/features/apiSharingNg/linkShare.feature
Original file line number Diff line number Diff line change
Expand Up @@ -553,8 +553,6 @@ Feature: Create a share link for a resource
},
"link": {
"type": "object",
"minItems": 5,
"maxItems": 5,
"required": [
"@libre.graph.displayName",
"@libre.graph.quickLink",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,6 @@ Feature: Update permission of a share
"""
{
"type": "object",
"minItems": 3,
"maxItems": 3,
"required": [
"grantedToV2",
"id",
Expand Down
121 changes: 94 additions & 27 deletions tests/acceptance/features/bootstrap/FeatureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ class FeatureContext extends BehatVariablesContext {
use Sharing;
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
*/
Expand Down Expand Up @@ -572,6 +578,8 @@ public function __construct(
$this->publicLinkSharePassword = $publicLinkSharePasswordFromEnvironment;
}
$this->originalAdminPassword = $this->adminPassword;

$this->jsonSchemaValidators = \array_keys(JsonSchema::properties()->getDataKeyMap());
}

/**
Expand Down Expand Up @@ -1206,6 +1214,56 @@ function ($subArray) {
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
*
Expand All @@ -1214,7 +1272,13 @@ function ($subArray) {
* @return void
* @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);
Assert::assertFalse($hasTwoElementValidator, "'items', 'enum' and 'const' should not be used together");
if ($schemaObj->enum || $schemaObj->const) {
Expand All @@ -1224,20 +1288,26 @@ private function validateSchemaArrayEntries(JsonSchema $schemaObj): void {

$requiredValidators = ["maxItems", "minItems"];
$optionalValidators = ["items", "uniqueItems"];
$notAllowedValidators = ["properties", "minProperties", "maxProperties", "required"];
$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) {
Assert::assertNotNull($schemaObj->$validator, \sprintf($errMsg, $validator));
}

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) {
$value = $schemaObj->$validator;
switch ($validator) {
case 'items':
case "items":
if ($schemaObj->maxItems === 0) {
break;
}
Expand Down Expand Up @@ -1267,6 +1337,15 @@ private function validateSchemaArrayEntries(JsonSchema $schemaObj): void {
break;
}
}

$items = $schemaObj->items;
if ($items !== null && $items->oneOf !== null) {
foreach ($items->oneOf as $oneOfItem) {
$this->validateSchemaObject($oneOfItem);
}
} elseif ($items !== null) {
$this->validateSchemaObject($items);
}
}

/**
Expand All @@ -1278,29 +1357,17 @@ private function validateSchemaArrayEntries(JsonSchema $schemaObj): void {
* @throws Exception
*/
public function validateSchemaRequirements(JsonSchema $schema): void {
$propNames = $schema->getPropertyNames();
$props = $schema->getProperties();
foreach ($propNames as $propName) {
switch ($props->$propName->type) {
case 'array':
$this->validateSchemaArrayEntries($props->$propName);
$items = $props->$propName->items;
if ($items && $items->oneOf) {
foreach ($items->oneOf as $oneOfItem) {
$this->validateSchemaRequirements($oneOfItem);
}
break;
} elseif ($items) {
$this->validateSchemaRequirements($items);
}
break;
default:
break;
}
// traverse for nested properties
if ($props->$propName->getProperties()) {
$this->validateSchemaRequirements($props->$propName);
}
Assert::assertNotNull($schema->type, "'type' is required for root level schema");

switch ($schema->type) {
case "object":
$this->validateSchemaObject($schema);
break;
case "array":
$this->validateSchemaArray($schema);
break;
default:
break;
}
}

Expand Down

0 comments on commit 9b8ec4e

Please sign in to comment.