Repository navigation
test(tooling): harden loyalty e2e — unique-phone isolation + PHPStan level 5 on tools - #59
Merged
Merged
Conversation
The loyalty e2e reused fixed test phones (0600000000/0600000001). Loyalty aggregates by identity (email OR phone) and autoLink() ties a fixed phone to the first run's email for good, so on a persistent DB (prod, local dev) a run interrupted before its cleanup leaves residual ledger data that later runs re-aggregate — inflating baselines (the 1.4.0 prod sweep had to purge it by hand). Each run now draws a unique test phone, so a run's identity can never link to another run's. The three prod-runnable cores (loyalty-prod, offered-pot, exclusion) additionally clear the identity-link rows for the test contact in their teardown (the finally only deleted ledger rows before), so the links table no longer accumulates. Verified: e2e-offered-pot-local passes 24/24 twice in a row on a persistent DB, and the full local loyalty suite stays green.
The prod-only e2e cores (tools/*-prod.php) were never run locally nor in CI, so a drifting constructor/method signature rotted silently — the loyalty prod core had called GetCustomerLoyaltyHandler with an obsolete Clock argument since 1.3.0, only surfacing as an empty prod response during the 1.4.0 e2e sweep. tools/ is procedural WordPress/WP-CLI glue (global $wpdb, WC getters returning mixed): holding it at the theme's level max would drown the signal under hundreds of mixed warnings. A dedicated phpstan-tools.neon.dist analyses it at level 5 — the level that checks argument types, so it catches exactly that class of rot — without the mixed noise. src/ and inc/ stay at level max. - Add a small committed WP_CLI stub (php-stubs/wp-cli-stubs is not installable here) and scan wordpress/woocommerce stubs; scan inc/ for its luziapi_* helpers. - Fix the real findings: add_order_note expects int (0/1, not bool), set_total expects string, narrow WC_Order_Item to WC_Order_Item_Product before get_total, drop redundant nullsafe accesses PHPStan proved non-null. - A small, documented ignoreErrors block covers genuine PHPStan blind spots on test glue (the deploy-time REPLACE_WITH_TOKEN guard, $GLOBALS mail/attachment arrays populated by hooks, a reference-captured property) — never the rot class. - composer stan now runs both configs; CI (composer stan) enforces it. Fixes #57
Drago37
marked this pull request as ready for review
September 23, 2026 13:39
This was referenced Sep 23, 2026
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.
Two follow-ups surfaced by the 1.4.0 production e2e sweep, both in test tooling (
tools/, never deployed). Kept in one PR because they touch the same e2e cores.#58 — Isolate loyalty e2e with a unique phone per run
The loyalty e2e reused fixed test phones (
0600000000/0600000001). Loyalty aggregates by identity (email OR phone), andautoLink()ties a fixed phone to the first run's email for good — so on a persistent DB (prod, local dev), a run interrupted before cleanup leaves residual ledger data that later runs re-aggregate, inflating baselines (the 1.4.0 sweep had to purge it by hand).e2e-loyalty-prod,e2e-offered-pot,e2e-exclusion) also clear the identity-link rows for the test contact in teardown (thefinallyonly deleted ledger rows).#57 — Analyse
tools/with PHPStan (level 5)The prod-only e2e cores were never run locally nor in CI, so a drifting signature rotted silently — the loyalty prod core had called
GetCustomerLoyaltyHandlerwith an obsoleteClockargument since 1.3.0, only surfacing as an empty prod response during the sweep.phpstan-tools.neon.distanalysestools/at level 5 (argument-type checking → catches exactly that class of rot) instead of the theme's level max, which would drown the procedural glue undermixednoise.src/+inc/stay at level max.wordpress/woocommercestubs andinc/scanned for symbols.add_order_noteint,set_totalstring, narrowWC_Order_Itembeforeget_total, drop redundant nullsafe) + a small documentedignoreErrorsblock for genuine PHPStan blind spots (the deploy-timeREPLACE_WITH_TOKENguard,$GLOBALSmail arrays populated by hooks, a reference-captured property) — never the rot class.composer stannow runs both configs; CI enforces it.Verified: the 14 touched local e2e targets all pass, and
composer stanis green on both configs.Fixes #58
Fixes #57
🤖 Generated with Claude Code