Skip to content

Build/Test Tools: Use strict assertions in the XML-RPC tests. - #13503

Closed
haritpanchal wants to merge 2 commits into
WordPress:trunkfrom
haritpanchal:64895-xmlrpc-strict-assertions
Closed

Build/Test Tools: Use strict assertions in the XML-RPC tests.#13503
haritpanchal wants to merge 2 commits into
WordPress:trunkfrom
haritpanchal:64895-xmlrpc-strict-assertions

Conversation

@haritpanchal

Copy link
Copy Markdown

Replaces the loose assertions in tests/phpunit/tests/xmlrpc with type-strict
counterparts, so the tests assert the type of each value as well as its value.

43 assertions across 15 files: 40 assertEquals() and 3 assertNotEquals().
This is the xmlrpc directory claimed on the ticket. None of the three open
pull requests (#12583, #11707, #12613) touch this directory, so there is no
overlap with work already in review.

The contract that shapes most of this

XML-RPC deliberately returns IDs as strings, so that they cannot exceed what an
XML-RPC integer can describe. _prepare_term() states it directly:

For integers which may be larger than XML-RPC supports ensure we return strings.

Accordingly, in class-wp-xmlrpc-server.php:

Prepared field Type
_prepare_user()user_id (string)
_prepare_post()post_id, post_parent (string)
_prepare_media_item()attachment_id (string)
_prepare_term()term_id, term_group, term_taxonomy_id, parent (string)
_prepare_term()count (int)
_prepare_post()menu_order (int)
wp_newPost(), mw_newPost() return value (string) $post_id

So assertSame( (string) $id, ... ) here documents an intentional API contract
rather than papering over a type bug. These are not casts added to make a
conversion pass: the tests already assert the same thing themselves, via
pre-existing assertIsString( $result['post_id'] ) and
assertStringMatchesFormat( '%d', $result['term_id'] ) lines that pass on trunk
today. wp/editPost.php likewise already used
assertSame( '', get_post_meta( $post_id, '_thumbnail_id', true ) ).

Values read back from the database follow the same rule and are treated the same
way: WP_Post::$post_author is documented @var string
(@phpstan-var numeric-string|'', default '0'), and get_post_meta() returns
the stored string.

One docblock is imprecise and worth flagging rather than relying on:
WP_Comment::$comment_ID and $comment_post_ID are documented as
@var string|int. _prepare_comment() passes them through with no cast, so the
type comes from the database — strings. The conversions there rest on the file's
own existing assertIsString() assertions, not on that union.

Two findings, not conversions

1. wp/editProfile.phptest_ignore_email_change() was asserting nothing.

It read $user_data->email. WP_User has no email column — it is
user_email — and email is not in WP_User::$back_compat_keys, so __get()
fell through to get_user_meta( $editor_id, 'email', true ) and returned ''.
The assertion was therefore 'notaneditor@example.com' !== '', which is true no
matter what wp_editProfile() does with the submitted email. The test could
never have caught a regression in the behaviour its name describes.

Changed to user_email, which is the property that holds the value under test.
Confirmed by mutation: with the old expression the actual value is literally
'', and with the fix a flipped assertion fails against the real stored
address.

2. wp/getTerm.php — one assertion stays loose, deliberately.

In test_valid_term(), $term comes from get_term( ..., ARRAY_A ) with
integer term_id/parent, while $result is the prepared struct with those as
strings. A strict comparison of the two arrays would be wrong, so
assertEquals() stays, with a comment recording why, and the individual types
continue to be asserted on the lines below.

The arguments were also reversed — the actual value was being passed in the
expected position — so that is now assertEquals( $term, $result ).

wp/editTerm.php goes the other way for the same reason: WP_Term::$parent is
@var int, so assertEquals( '0', $term->parent ) was comparing against a
string and is now assertSame( 0, $term->parent ).

Testing

npm run test:php -- --group xmlrpc
OK (348 tests, 1308 assertions)

The whole group is run, not just the changed files, to confirm nothing
collateral broke.

composer lint is clean on all 15 files, and php -l passes on each.

Note for anyone reproducing this: npm run test:php -- tests/phpunit/tests/xmlrpc
reports No tests executed! and exits 0. PHPUnit's default test-file suffix is
*Test.php, and the suffix=".php" in phpunit.xml.dist applies only to the
configured <testsuite>, not to a positional directory argument — so a
directory path silently collects zero tests from this suite. --group xmlrpc
(or explicit file paths) is the working form.

Test-only change; no production code is touched.

Trac ticket: https://core.trac.wordpress.org/ticket/64895

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: Inventorying the loose assertions, reading each prepared-struct field
back to its cast in class-wp-xmlrpc-server.php to decide the expected type per
assertion, and drafting this description. I verified every conversion against the
server source and the declared property types, confirmed the three open pull
requests do not overlap this directory, established the editProfile.php finding
from WP_User::$back_compat_keys and __get() and confirmed by mutation that
the replacement assertion can actually fail, ran the full xmlrpc group plus
lint and php -l, and I take responsibility for the result.


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

Replaces the loose assertions in tests/phpunit/tests/xmlrpc with type-strict
counterparts, so the tests also assert the type of each value.

The XML-RPC server deliberately returns IDs as strings, so that they cannot
exceed what an XML-RPC integer can describe, and the expected values are cast
accordingly to document that contract. Two assertions are left loose on
purpose, with comments explaining why.

Also corrects test_ignore_email_change(), which read a WP_User property that
does not exist and so passed regardless of the behaviour under test.

See #64895.
@github-actions

github-actions Bot commented Sep 12, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props haritpanchal, lancewillett.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@lancewillett lancewillett left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overall approach looks sound. Please add the explicit type guards identified inline in the two ignored-ID tests and getTerm() before landing.

Local Docker validation passed: 348 tests / 1,308 assertions in default order and two seeded orders; multisite passed 349 tests / 1,312 assertions. PHPCS passed all 15 changed files.

The user_email correction also held up to a production mutation: accepting the submitted email failed the corrected test, while the original assertion still passed.


Adversarial review · gpt-6

Comment thread tests/phpunit/tests/xmlrpc/wp/newPost.php
Comment thread tests/phpunit/tests/xmlrpc/mw/newPost.php
Comment thread tests/phpunit/tests/xmlrpc/wp/getTerm.php
Add explicit assertIsString() guards alongside the strict assertions, so the
string return contract is enforced rather than implied. assertNotSame() passes
on a type mismatch, and assertStringMatchesFormat() coerces an integer
argument, so neither caught an ID returned as an integer.

See #64895.
@haritpanchal

Copy link
Copy Markdown
Author

Thanks — all three applied.

Reproduced both findings by mutation first:

  • _insert_post() / mw_newPost() returning (int) $post_id fails both
    test_ignore_id tests at the new assertIsString(), and passes without it.
  • _prepare_term() returning all four IDs as integers fails test_valid_term.
    With the mutation in place but the new assertIsString() lines removed it
    passes with 16 assertions — matching your result.

--group xmlrpc: OK, 348 tests / 1314 assertions (up 6 from 1308, matching the
6 added). Multisite OK, 349 / 1318. PHPCS 3/3 on the changed files.

@lancewillett
lancewillett self-requested a review September 12, 2026 19:32
wporg-sync pushed a commit that referenced this pull request Sep 12, 2026
Use type-strict comparisons for XML-RPC results and persisted values, matching the documented string ID contracts. Add explicit string checks where negative comparisons and numeric-format assertions do not enforce types.

Correct the profile email test to inspect user_email, so it detects an unintended email change. Retain the loose term-array comparison because the internal and XML-RPC representations use different ID types.

Developed in: #13503

Props haritpanchal.
See #64895.


git-svn-id: https://develop.svn.wordpress.org/trunk@63606 602fd350-edb4-49c9-b593-d223f7449a82
@lancewillett

Copy link
Copy Markdown
Member

@github-project-automation github-project-automation Bot moved this from In progress to Done in WordPress Project Build Tooling Sep 12, 2026
wporg-sync pushed a commit to WordPress/WordPress that referenced this pull request Sep 12, 2026
Use type-strict comparisons for XML-RPC results and persisted values, matching the documented string ID contracts. Add explicit string checks where negative comparisons and numeric-format assertions do not enforce types.

Correct the profile email test to inspect user_email, so it detects an unintended email change. Retain the loose term-array comparison because the internal and XML-RPC representations use different ID types.

Developed in: WordPress/wordpress-develop#13503

Props haritpanchal.
See #64895.

Built from https://develop.svn.wordpress.org/trunk@63606


git-svn-id: http://core.svn.wordpress.org/trunk@62782 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

2 participants