Skip to content

Commit 9a4f91d

Browse files
committed
Abilities API: Add coverage for top-level required in input validation
Add a data-provider-driven test documenting that a `required` flag at the root of a schema is never consulted by `WP_Ability::validate_input()`: * top-level `required: true`/`false` is inert; only `type` is enforced, and `false` does not permit `null`. * a draft-04 `required` array on an object type is honored and enforces property presence. Also remove the inert top-level `required => true` flags from simple-type test schemas, keeping the meaningful per-property flags. Test-only change. Follow-up [61032], #64098. See #64955. git-svn-id: https://develop.svn.wordpress.org/trunk@62441 602fd350-edb4-49c9-b593-d223f7449a82
1 parent ed7bdd2 commit 9a4f91d

1 file changed

Lines changed: 94 additions & 28 deletions

File tree

tests/phpunit/tests/abilities-api/wpAbility.php

Lines changed: 94 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ public function set_up(): void {
2525
'output_schema' => array(
2626
'type' => 'number',
2727
'description' => 'The result of performing a math operation.',
28-
'required' => true,
2928
),
3029
'execute_callback' => static function (): int {
3130
return 0;
@@ -274,7 +273,6 @@ public function data_execute_input() {
274273
array(
275274
'type' => array( 'null', 'integer' ),
276275
'description' => 'The null or integer to convert to integer.',
277-
'required' => true,
278276
),
279277
static function ( $input ): int {
280278
return null === $input ? 0 : (int) $input;
@@ -286,7 +284,6 @@ static function ( $input ): int {
286284
array(
287285
'type' => 'boolean',
288286
'description' => 'The boolean to convert to integer.',
289-
'required' => true,
290287
),
291288
static function ( bool $input ): int {
292289
return $input ? 1 : 0;
@@ -298,7 +295,6 @@ static function ( bool $input ): int {
298295
array(
299296
'type' => 'integer',
300297
'description' => 'The integer to add 5 to.',
301-
'required' => true,
302298
),
303299
static function ( int $input ): int {
304300
return 5 + $input;
@@ -310,7 +306,6 @@ static function ( int $input ): int {
310306
array(
311307
'type' => 'number',
312308
'description' => 'The floating number to round.',
313-
'required' => true,
314309
),
315310
static function ( float $input ): int {
316311
return (int) round( $input );
@@ -322,7 +317,6 @@ static function ( float $input ): int {
322317
array(
323318
'type' => 'string',
324319
'description' => 'The string to measure the length of.',
325-
'required' => true,
326320
),
327321
static function ( string $input ): int {
328322
return strlen( $input );
@@ -361,7 +355,6 @@ static function ( array $input ): int {
361355
array(
362356
'type' => 'array',
363357
'description' => 'An array containing two numbers to add.',
364-
'required' => true,
365358
'minItems' => 2,
366359
'maxItems' => 2,
367360
'items' => array(
@@ -403,6 +396,100 @@ public function test_execute_input( $input_schema, $execute_callback, $input, $r
403396
$this->assertSame( $result, $ability->execute( $input ) );
404397
}
405398

399+
/**
400+
* Data provider for top-level `required` validation behavior.
401+
*
402+
* Each schema variant is paired with both a valid and an invalid input so the
403+
* inert behavior of a top-level `required` boolean — and the meaningful
404+
* behavior of a draft-04 `required` array on an object — are sealed.
405+
*
406+
* @return array<string, array{0: array, 1: mixed, 2: bool}> Data sets.
407+
*/
408+
public function data_validate_input_top_level_required() {
409+
$required_true = array(
410+
'type' => 'string',
411+
'required' => true,
412+
);
413+
$required_false = array(
414+
'type' => 'string',
415+
'required' => false,
416+
);
417+
$required_unset = array(
418+
'type' => 'string',
419+
);
420+
$object_required = array(
421+
'type' => 'object',
422+
'properties' => array(
423+
'a' => array( 'type' => 'integer' ),
424+
'b' => array( 'type' => 'integer' ),
425+
),
426+
'required' => array( 'a', 'b' ),
427+
);
428+
429+
return array(
430+
// A top-level `required: true` is inert: only `type` is enforced.
431+
'required true: valid input' => array( $required_true, 'hello', true ),
432+
'required true: invalid input' => array( $required_true, 123, false ),
433+
434+
// A top-level `required: false` is equally inert and does not permit null.
435+
'required false: valid input' => array( $required_false, 'hello', true ),
436+
'required false: invalid input' => array( $required_false, 123, false ),
437+
'required false: null still invalid' => array( $required_false, null, false ),
438+
439+
// Omitting `required` behaves identically to setting it.
440+
'required unset: valid input' => array( $required_unset, 'hello', true ),
441+
'required unset: invalid input' => array( $required_unset, 123, false ),
442+
443+
// A draft-04 `required` array on an object type IS honored.
444+
'object required array: valid input' => array(
445+
$object_required,
446+
array(
447+
'a' => 1,
448+
'b' => 2,
449+
),
450+
true,
451+
),
452+
'object required array: invalid input' => array( $object_required, array( 'a' => 1 ), false ),
453+
);
454+
}
455+
456+
/**
457+
* Tests how a top-level `required` keyword is handled during input validation.
458+
*
459+
* For a non-object root type, a top-level `required` flag is inert: validation
460+
* gates solely on `type`, so the outcome is identical whether `required` is
461+
* `true`, `false`, or omitted — and `required: false` notably does not make a
462+
* `null` value acceptable. For an object root type, a draft-04 `required` array
463+
* of property names is honored and enforces the presence of those properties.
464+
*
465+
* @ticket 64955
466+
*
467+
* @covers WP_Ability::validate_input
468+
*
469+
* @dataProvider data_validate_input_top_level_required
470+
*
471+
* @param array $input_schema The input schema under test.
472+
* @param mixed $input The input value to validate.
473+
* @param bool $is_valid Whether the input is expected to pass validation.
474+
*/
475+
public function test_validate_input_top_level_required( $input_schema, $input, $is_valid ) {
476+
$ability = new WP_Ability(
477+
self::$test_ability_name,
478+
array_merge(
479+
self::$test_ability_properties,
480+
array( 'input_schema' => $input_schema )
481+
)
482+
);
483+
484+
$result = $ability->validate_input( $input );
485+
486+
if ( $is_valid ) {
487+
$this->assertTrue( $result, 'Expected the input to pass validation.' );
488+
} else {
489+
$this->assertWPError( $result, 'Expected the input to fail validation.' );
490+
}
491+
}
492+
406493
/**
407494
* A static method to be used as a callback in tests.
408495
*
@@ -466,7 +553,6 @@ public function test_execute_with_different_callbacks( $execute_callback ) {
466553
'input_schema' => array(
467554
'type' => 'string',
468555
'description' => 'Test input string.',
469-
'required' => true,
470556
),
471557
'execute_callback' => $execute_callback,
472558
)
@@ -561,7 +647,6 @@ public function test_before_execute_ability_action() {
561647
'input_schema' => array(
562648
'type' => 'integer',
563649
'description' => 'Test input parameter.',
564-
'required' => true,
565650
),
566651
'execute_callback' => static function ( int $input ): int {
567652
return $input * 2;
@@ -645,7 +730,6 @@ public function test_after_execute_ability_action() {
645730
'input_schema' => array(
646731
'type' => 'integer',
647732
'description' => 'Test input parameter.',
648-
'required' => true,
649733
),
650734
'execute_callback' => static function ( int $input ): int {
651735
return $input * 3;
@@ -813,7 +897,6 @@ public function test_after_action_not_fired_on_output_validation_error() {
813897
'output_schema' => array(
814898
'type' => 'string',
815899
'description' => 'Expected string output.',
816-
'required' => true,
817900
),
818901
'execute_callback' => static function (): int {
819902
return 42;
@@ -856,12 +939,10 @@ public function test_normalize_input_filter_can_transform_input() {
856939
'input_schema' => array(
857940
'type' => 'string',
858941
'description' => 'Test input string.',
859-
'required' => true,
860942
),
861943
'output_schema' => array(
862944
'type' => 'integer',
863945
'description' => 'Result integer.',
864-
'required' => true,
865946
),
866947
'execute_callback' => static function ( string $input ): int {
867948
return strlen( $input );
@@ -898,7 +979,6 @@ public function test_normalize_input_filter_wp_error_halts_execution() {
898979
'input_schema' => array(
899980
'type' => 'string',
900981
'description' => 'Test input string.',
901-
'required' => true,
902982
),
903983
'execute_callback' => static function ( string $input ) {
904984
return strlen( $input );
@@ -934,12 +1014,10 @@ public function test_permission_result_filter_can_grant_permission() {
9341014
'input_schema' => array(
9351015
'type' => 'integer',
9361016
'description' => 'Test input integer.',
937-
'required' => true,
9381017
),
9391018
'output_schema' => array(
9401019
'type' => 'integer',
9411020
'description' => 'Result integer.',
942-
'required' => true,
9431021
),
9441022
'execute_callback' => static function ( int $input ): int {
9451023
return $input;
@@ -1105,7 +1183,6 @@ public function test_pre_execute_ability_filter_short_circuits_pipeline() {
11051183
'input_schema' => array(
11061184
'type' => 'integer',
11071185
'description' => 'Test input integer.',
1108-
'required' => true,
11091186
),
11101187
'execute_callback' => static function (): int {
11111188
return 1;
@@ -1272,7 +1349,6 @@ public function test_execute_result_filter_can_transform_result() {
12721349
'input_schema' => array(
12731350
'type' => 'integer',
12741351
'description' => 'Test input integer.',
1275-
'required' => true,
12761352
),
12771353
'execute_callback' => static function ( int $input ): int {
12781354
return $input * 2;
@@ -1416,7 +1492,6 @@ public function test_validate_input_filter_receives_all_parameters() {
14161492
'input_schema' => array(
14171493
'type' => 'string',
14181494
'description' => 'Test input string.',
1419-
'required' => true,
14201495
),
14211496
'execute_callback' => static function ( string $input ): int {
14221497
return strlen( $input );
@@ -1454,12 +1529,10 @@ public function test_validate_input_filter_overrides_validation_failure() {
14541529
'input_schema' => array(
14551530
'type' => 'integer',
14561531
'description' => 'Test input integer.',
1457-
'required' => true,
14581532
),
14591533
'output_schema' => array(
14601534
'type' => 'integer',
14611535
'description' => 'Result integer.',
1462-
'required' => true,
14631536
),
14641537
'execute_callback' => static function () {
14651538
return 99;
@@ -1496,7 +1569,6 @@ public function test_validate_input_filter_receives_error_on_invalid_input() {
14961569
'input_schema' => array(
14971570
'type' => 'integer',
14981571
'description' => 'Test input integer.',
1499-
'required' => true,
15001572
),
15011573
'execute_callback' => static function ( int $input ): int {
15021574
return $input * 2;
@@ -1534,7 +1606,6 @@ public function test_validate_input_filter_replaces_error_with_custom() {
15341606
'input_schema' => array(
15351607
'type' => 'integer',
15361608
'description' => 'Test input integer.',
1537-
'required' => true,
15381609
),
15391610
'execute_callback' => static function ( int $input ): int {
15401611
return $input * 2;
@@ -1572,7 +1643,6 @@ public function test_validate_output_filter_receives_all_parameters() {
15721643
'output_schema' => array(
15731644
'type' => 'integer',
15741645
'description' => 'The result integer.',
1575-
'required' => true,
15761646
),
15771647
'execute_callback' => static function (): int {
15781648
return 42;
@@ -1610,7 +1680,6 @@ public function test_validate_output_filter_overrides_validation_failure() {
16101680
'output_schema' => array(
16111681
'type' => 'string',
16121682
'description' => 'The result string.',
1613-
'required' => true,
16141683
),
16151684
'execute_callback' => static function (): int {
16161685
return 42;
@@ -1647,7 +1716,6 @@ public function test_validate_output_filter_receives_error_on_invalid_output() {
16471716
'output_schema' => array(
16481717
'type' => 'string',
16491718
'description' => 'The result string.',
1650-
'required' => true,
16511719
),
16521720
'execute_callback' => static function (): int {
16531721
return 42;
@@ -1685,7 +1753,6 @@ public function test_validate_output_filter_replaces_error_with_custom() {
16851753
'output_schema' => array(
16861754
'type' => 'string',
16871755
'description' => 'The result string.',
1688-
'required' => true,
16891756
),
16901757
'execute_callback' => static function (): int {
16911758
return 42;
@@ -1805,7 +1872,6 @@ public function test_ability_invoked_action_fires_on_validation_failure() {
18051872
'input_schema' => array(
18061873
'type' => 'integer',
18071874
'description' => 'Int input.',
1808-
'required' => true,
18091875
),
18101876
'execute_callback' => static function ( int $input ): int {
18111877
return $input;

0 commit comments

Comments
 (0)