Add host PHPUnit unit suite; refactor Query and Connectors for testability (XWPENG-42) - #1972
Open
shadyvb wants to merge 5 commits into
Open
Add host PHPUnit unit suite; refactor Query and Connectors for testability (XWPENG-42)#1972shadyvb wants to merge 5 commits into
shadyvb wants to merge 5 commits into
Conversation
shadyvb
requested review from
PatelUtkarsh and
bartoszgadomski
and
a balanced review from Copilot
August 30, 2026 07:43
There was a problem hiding this comment.
Pull request overview
Adds a host-native PHPUnit suite and refactors connector loading and query construction for testability.
Changes:
- Adds Brain Monkey unit testing and supporting commands/configuration.
- Refactors connector discovery and metadata APIs.
- Splits query construction into testable SQL-fragment methods.
Critical issues
- Meta sorting generates invalid SQL because
GROUP BYfollowsORDER BY. Swap the clauses. WP VIP ref: valid$wpdbquery construction.
Medium/Low issues
- Documentation references a nonexistent npm script; use
test:php-unit.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
classes/class-query.php |
Refactors query fragments and meta sorting. |
classes/class-connectors.php |
Refactors connector loading and accessors. |
classes/class-connector.php |
Makes label methods abstract. |
abilities/class-ability-get-connectors.php |
Uses the new connector metadata API. |
abilities/class-ability-create-exclusion-rule.php |
Uses the new slug API. |
tests/phpunit/unit/test-query.php |
Tests query fragments and assembly. |
tests/phpunit/unit/test-connectors.php |
Tests connector lifecycle behavior. |
tests/phpunit/unit/bootstrap.php |
Bootstraps host unit tests. |
phpunit-unit.xml |
Configures the unit suite. |
phpunit.xml |
Excludes unit tests from integration runs. |
phpunit-multisite.xml |
Excludes unit tests from multisite runs. |
composer.json |
Adds test dependencies and command. |
composer.lock |
Locks new testing dependencies. |
package.json |
Adds the Docker unit-test wrapper. |
contributing.md |
Documents unit-test commands. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
shadyvb
force-pushed
the
ticket/XWPENG-42-unit-test-tier
branch
from
September 1, 2026 10:43
bfdfaaf to
8e0b76b
Compare
…ad/register pipeline. Register gates and get_all(include_inactive) can now run without booting WordPress, so abilities see admin-only connectors on REST. Query stays the original single query() method.
…tion, and meta ordering can be verified without a full DB. Add host PHPUnit coverage for the Query family and preserve intentional sort and filter-args behavior documented in tests.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
shadyvb
force-pushed
the
ticket/XWPENG-42-unit-test-tier
branch
from
September 1, 2026 15:22
8e0b76b to
38fbc0a
Compare
bartoszgadomski
left a comment
Member
There was a problem hiding this comment.
@shadyvb Thank you for working on this PR! Please check AI-assisted inline comments below.
…ity copy. Meta ORDER BY uses MAX() so duplicate stream_meta rows sort consistently. load_connectors() is one-shot with _doing_it_wrong on a second call. Abilities document shipped inventory and skip connector validation when Connectors is unset.
Connectors such as Yoast SEO can return from register() without calling parent::register(), leaving is_registered() false while still present in $connectors — which broke Test_Connectors::test_unload_connectors in CI.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes XWPENG-42.
Introduces a fast, host-native PHPUnit unit tier (
composer test-unit) using Brain Monkey, then refactorsConnectorsandQueryinto testable, named SQL-fragment methods. Along the way, several latent bugs inQuery::where_in(), pagination, meta ordering, and filter-arg handling are fixed and locked in with data-provider tests.Summary
Host unit suite
phpunit-unit.xml,tests/phpunit/unit/bootstrap.php(Composer autoload +WP_Stream\class autoload mirroringPlugin::autoload).composer test-unitscript; dev dependencyyoast/wp-test-utils(Brain Monkey).phpunit.xml,phpunit-multisite.xml) excludetests/phpunit/unit/so Docker/integration runs stay unchanged.contributing.mddocumentscomposer test-unit(host) and Docker wrappernpm run test:php-unit.npm run testrunstest:php-unitas a separate step beforetest:php; unit tests are not merged into the integration PHPUnit config.Connectors refactor
Connectors::BUILTIN_CONNECTOR_SLUGSconstant replaces inline slug list.get_available_connectors()→instantiate_connector_classes()→register_connector_instances().get_slugs( $include_inactive = false )andget_all( $include_inactive = false )replaceget_all_including_admin_only()/get_all_slugs_including_admin_only().get_slugs( true ); get-connectors usesget_all( true )behind new filterwp_stream_abilities_connectors.Connector:get_label(),get_context_labels(),get_action_labels()promoted to abstract (all 22 built-in connectors already implement them).Query refactor
query()split into public fragment methods:where_columns(),where_dates(),where_in(),select(),orderby(),join(),limits().ORDERABLE_FIELDSconstant documents intentional sort behavior.$wpdbproperty set in constructor (enables partial mocks in unit tests).join()emitsLEFT JOIN streammetawhenorderbyismeta_value/meta_value_numandmeta_keyis set.GROUP BY stream.IDon the items query; count query usesCOUNT( DISTINCT stream.ID ).Bugs fixed in original
Querycodewhere_insingle-element arraysarray_shift()removed the only value, thenempty( $value )skipped the clause entirelyarray_values(); one-elementIN (1)worksrecord__infield mappingstr_replace( 'record_', … )left_insuffix → invalid columnrecord→IDmappingwhere_inprepare() with array arg$wpdb->prepare( …, $field, $value )passed the whole array as one scalar → empty/wrong SQL...$valueswith per-value%d/%splaceholders__not_infamilyarray_shift/ prepare bugs__inand__not_inpaged = 0paginationabsint(0)→ offset(0-1)*per_pagewrapped to large positive offset (page 2 behavior)$page < 1clamped to1→LIMIT 0, per_pageORDER BY streammeta.meta_valuewith no JOIN (broken SQL)join()addsLEFT JOIN streammetawhen meta sort +meta_keyGROUP BY stream.ID+COUNT( DISTINCT stream.ID )user_roleandconnector$argsdate leakdatemutated caller args withdate_from/date_tobefore filters ranwhere_dates()expands on a local copy only; filter hooks receive original argsNot restored (intentional): bare
__in/__not_inargs no longer map toID; userecord__ininstead.Intentional behavior preserved
orderby = dateandorderby = ipfall back to sorting byID.records_per_page = 0still emitsLIMIT 0, 0.Breaking changes
Connectorget_all_including_admin_only()methods removedget_all( true )/get_slugs( true )$argsno longer receivedate_from/date_towhen onlydatepassedTesting
Command:
composer test-unit→ OK (53 tests, 106 assertions)test-connectors.phptest-query.phpAccepted gaps: Connectors constructor/include loop; Query __construct, empty search_field fallback, invalid IP, date boundaries.
Gotchas
composer test-unitis host fast path; Docker usesnpm run test:php-unit.contributing.mdreferencesnpm run test-unitbut package.json script istest:php-unit.Commits
Checklist
contributing.md).Release Changelog
Query::where_in()silently dropped single-value filters, mis-mappedrecord__in, and produced invalid SQL with array prepare args.Querypagination treatedpaged = 0as large positive offset.streammetawithout JOIN; duplicate rows deduplicated via GROUP BY / COUNT(DISTINCT).datearg expansion no longer mutates filter-hook$args.composer test-unit).Connectors::get_slugs()/get_all()with$include_inactive.wp_stream_abilities_connectorsfilter.Release Checklist
masterbranch.readme.txt.stream.php.Stable taginreadme.txt.classes/class-plugin.php.