refactor: move the plugin decisions into src - #11
Merged
vitormattos merged 17 commits intoSep 18, 2026
Merged
Conversation
YvesCesar
marked this pull request as draft
September 14, 2026 22:31
YvesCesar
marked this pull request as ready for review
September 15, 2026 21:23
vitormattos
requested changes
Sep 16, 2026
YvesCesar
force-pushed
the
test/integration-harness
branch
from
September 16, 2026 23:02
b01079f to
29699fc
Compare
YvesCesar
force-pushed
the
refactor/decision-logic-in-src
branch
from
September 16, 2026 23:02
2852b63 to
011fb74
Compare
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
YvesCesar
force-pushed
the
refactor/decision-logic-in-src
branch
from
September 17, 2026 20:58
011fb74 to
a8eb740
Compare
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
Signed-off-by: YvesCesar <yvesamorim73@gmail.com>
test: add a playwright suite for the account and subscription flows
vitormattos
approved these changes
Sep 18, 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.
The plugin was entirely procedural: hook callbacks that read
global $wp,$_SERVERand the options, and decided in the middle of applying the effect. This separates the two, on top of the characterization suite from #10 (hence the base branch; it retargets tomainonce #9 and #10 are merged).src/src/holds the decisions,includes/and the main plugin file hold the wiring. A decision receives values and returns values, so it is covered by a data provider and no test double.The plugin is installed by cloning the repository, so Composer never runs on the server and its autoloader does not exist at runtime:
src/Autoloader.phpmaps the namespace tosrc/and is the only file the plugin requires by hand. Theautoloadsection ofcomposer.jsondescribes the same mapping for PHPStan and PHPUnit.What each rewired file kept:
libresign_receive_github_site_deploy_webhook()went from seven nested branches to building aWebhookGate, asking it for a decision and turning that decision into a response. The transient guarding duplicated deliveries, the fragment sync and the recorded result stayed, since those are effects.libresign_filter_account_menu_items(),libresign_filter_account_menu_item_classes()andlibresign_filter_account_endpoint_title()became one line each, passinglibresign_get_current_query_vars()toNavigationinstead of having it readglobal $wpfor them.libresign_is_root_my_account_endpoint_request()keeps reading and sanitizing$_SERVER['REQUEST_URI']and hands the path toRootEndpoint::matches().admin_initmoved from an anonymous function tolibresign_register_settings().tests/Support/RegistersPluginSettings.phpno longer has to walk$wp_filterwith Reflection to find that closure, which its own docblock asked for.The cipher was written out three times — decrypting the webhook secret, decrypting the deploy token and encrypting both on save — and only the webhook copy handled a value stored in plain text.
Settings\Secretis now the single copy, andSecret::from_salts()takes the salts as arguments instead of reading the constants, which is what makes it testable.The two uses do not read a stored value the same way, so there are two methods.
decrypt()returns a value it cannot decipher untouched, which is what the webhook secret has always done and is safe because that value never leaves the site.decrypt_or_discard(), used for the deploy token, returns it only when it was never encrypted at all — a token saved by hand has an underscore, which base64 does not, so it still works — and drops it when it is base64 that does not decipher, which means it was encrypted with salts this installation no longer has. That value is not the token and it is on its way to GitHub, so it must not leave the site.DeployDispatch::triggers_deploy()carries the current condition with the parentheses PHP already applies,&&binding tighter than||. The behaviour is unchanged and the test names it; the bug is still there to be decided on separately.Tests
227 tests, up from 143.
tests/Unit/now mirrorssrc/andtests/Integration/mirrors the plugin files, in both cases file by file withTest.phpappended.The matrices that used to go through WordPress moved to unit tests with a data provider: the endpoint titles and the highlighted entry no longer need
global $wp, and every answer the webhook endpoint can give — unconfigured secret, another client, wrong signature, ping, unsupported event, unparsable payload, a run that is not the production deploy, and the deploy itself — is decided without a REST server. The integration tests kept one case each, which is what proves the wiring hands over the right values.New coverage that did not exist:
WorkflowRunagainst payloads GitHub fills in only partially,SiteDeployagainst a different configured repository,RootEndpointagainst the paths of the front page setup, andSecretround tripping, refusing another installation's salts and passing through a value stored in plain text.Behaviour
Unchanged, except for two details the suite does not cover: the fields echoed back in the ignored and synced responses are now trimmed, like the fields already compared, and a GitHub response without a
messageno longer raises an undefined index while building the failure notice.Two bugs are fixed, each with a test that fails without the fix:
0is stored.empty()took it for a blank field and silently kept the previous value, which predates this branch.WebhookDecision::workflow_run()no longer returns null. Only a deploy carries a run and the four outcomes are exhaustive, so the endpoint dereferences it without a guard; asking any other decision for the run now throws instead of failing on null, and the accessor stops being documented as nullable.Verification
composer ciis green: lint, PHPCS, PHPStan level 5 oversrc/as well, and 239 tests passing. In the local stack the plugin still loads without a Composer autoloader, the webhook endpoint answers the same, the cipher round trips and the My Account navigation is still filtered.