Make WordPress Core

Changeset 62441


Ignore:
Timestamp:
06/01/2026 12:13:15 PM (2 months ago)
Author:
gziolo
Message:

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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/abilities-api/wpAbility.php

    r62440 r62441  
    2626                                'type'        => 'number',
    2727                                'description' => 'The result of performing a math operation.',
    28                                 'required'    => true,
    2928                        ),
    3029                        'execute_callback'    => static function (): int {
     
    275274                                        'type'        => array( 'null', 'integer' ),
    276275                                        'description' => 'The null or integer to convert to integer.',
    277                                         'required'    => true,
    278276                                ),
    279277                                static function ( $input ): int {
     
    287285                                        'type'        => 'boolean',
    288286                                        'description' => 'The boolean to convert to integer.',
    289                                         'required'    => true,
    290287                                ),
    291288                                static function ( bool $input ): int {
     
    299296                                        'type'        => 'integer',
    300297                                        'description' => 'The integer to add 5 to.',
    301                                         'required'    => true,
    302298                                ),
    303299                                static function ( int $input ): int {
     
    311307                                        'type'        => 'number',
    312308                                        'description' => 'The floating number to round.',
    313                                         'required'    => true,
    314309                                ),
    315310                                static function ( float $input ): int {
     
    323318                                        'type'        => 'string',
    324319                                        'description' => 'The string to measure the length of.',
    325                                         'required'    => true,
    326320                                ),
    327321                                static function ( string $input ): int {
     
    362356                                        'type'        => 'array',
    363357                                        'description' => 'An array containing two numbers to add.',
    364                                         'required'    => true,
    365358                                        'minItems'    => 2,
    366359                                        'maxItems'    => 2,
     
    402395
    403396                $this->assertSame( $result, $ability->execute( $input ) );
     397        }
     398
     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                }
    404491        }
    405492
     
    467554                                        'type'        => 'string',
    468555                                        'description' => 'Test input string.',
    469                                         'required'    => true,
    470556                                ),
    471557                                'execute_callback' => $execute_callback,
     
    562648                                        'type'        => 'integer',
    563649                                        'description' => 'Test input parameter.',
    564                                         'required'    => true,
    565650                                ),
    566651                                'execute_callback' => static function ( int $input ): int {
     
    646731                                        'type'        => 'integer',
    647732                                        'description' => 'Test input parameter.',
    648                                         'required'    => true,
    649733                                ),
    650734                                'execute_callback' => static function ( int $input ): int {
     
    814898                                        'type'        => 'string',
    815899                                        'description' => 'Expected string output.',
    816                                         'required'    => true,
    817900                                ),
    818901                                'execute_callback' => static function (): int {
     
    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 {
     
    899980                                        'type'        => 'string',
    900981                                        'description' => 'Test input string.',
    901                                         'required'    => true,
    902982                                ),
    903983                                'execute_callback' => static function ( string $input ) {
     
    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 {
     
    11061184                                        'type'        => 'integer',
    11071185                                        'description' => 'Test input integer.',
    1108                                         'required'    => true,
    11091186                                ),
    11101187                                'execute_callback'    => static function (): int {
     
    12731350                                        'type'        => 'integer',
    12741351                                        'description' => 'Test input integer.',
    1275                                         'required'    => true,
    12761352                                ),
    12771353                                'execute_callback' => static function ( int $input ): int {
     
    14171493                                        'type'        => 'string',
    14181494                                        'description' => 'Test input string.',
    1419                                         'required'    => true,
    14201495                                ),
    14211496                                'execute_callback' => static function ( string $input ): int {
     
    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 () {
     
    14971570                                        'type'        => 'integer',
    14981571                                        'description' => 'Test input integer.',
    1499                                         'required'    => true,
    15001572                                ),
    15011573                                'execute_callback' => static function ( int $input ): int {
     
    15351607                                        'type'        => 'integer',
    15361608                                        'description' => 'Test input integer.',
    1537                                         'required'    => true,
    15381609                                ),
    15391610                                'execute_callback' => static function ( int $input ): int {
     
    15731644                                        'type'        => 'integer',
    15741645                                        'description' => 'The result integer.',
    1575                                         'required'    => true,
    15761646                                ),
    15771647                                'execute_callback' => static function (): int {
     
    16111681                                        'type'        => 'string',
    16121682                                        'description' => 'The result string.',
    1613                                         'required'    => true,
    16141683                                ),
    16151684                                'execute_callback' => static function (): int {
     
    16481717                                        'type'        => 'string',
    16491718                                        'description' => 'The result string.',
    1650                                         'required'    => true,
    16511719                                ),
    16521720                                'execute_callback' => static function (): int {
     
    16861754                                        'type'        => 'string',
    16871755                                        'description' => 'The result string.',
    1688                                         'required'    => true,
    16891756                                ),
    16901757                                'execute_callback' => static function (): int {
     
    18061873                                                'type'        => 'integer',
    18071874                                                'description' => 'Int input.',
    1808                                                 'required'    => true,
    18091875                                        ),
    18101876                                        'execute_callback' => static function ( int $input ): int {
Note: See TracChangeset for help on using the changeset viewer.