test: No action's behaviour is covered by anything that runs again #48
Labels
No labels
priority/P0
priority/P1
priority/P2
priority/P3
size/L
size/M
size/S
size/XL
size/XS
state/blocked
state/done
state/in-progress
state/in-review
state/needs-refinement
state/ready
type/bug
type/chore
type/feature
type/refactor
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
amtronics/platform-actions#48
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
check-actions.pyparses everyaction.yml, checks the fields a composite action needs, and runsbash -nover eachrun:body with${{ }}substituted. It cannot execute one, so no action's behaviour is tested by anything that runs again.Every recent change here has been verified by hand, by extracting a
run:body into a file, stubbing the commands it calls, and driving it through its cases:Each of those found something a reading would not have: a
|| echo 000that concatenated with curl's own000to give000000, a mutation that was a syntax error rather than a behavioural change, and an unguardedcurlreportingHTTPwith an empty status. None of that verification survives the pull request it was written in.This is the repository whose
v3tag every other repository consumes, and a change here reaches all of them unexecuted — the README says as much. Hand verification that is discarded is the weakest possible answer to that.Scope
run:bodies against stubbed commands and asserts on output and exit status, plus the existing verifications forrelease,report-job-failureanddeploy-vpswritten into it.Acceptance criteria
run:body, when the harness runs, then that body can be executed with named commands stubbed and its stdout, stderr and exit status asserted.deploy-vpsthat refuses an undeclared profile, when the harness runs, then all four of its states are covered.check-actions.py.Notes
The third criterion is the one that matters and the one most likely to be quietly skipped. Every verification listed above was written by first watching the case fail against the unfixed code; a harness that only ever ran against working code would have recorded four passing tables and caught none of the four defects.
Extraction is the fiddly part: a
run:body inside a heredoc keeps the heredoc's indentation, sotextwrap.dedenton the YAML-parsed string is not always enough — thedeploy-vpsguard had to be pulled from the raw file by line range. Worth solving once in the harness rather than in each verification.check-actions.pyalready substitutes${{ }}and knows how to find every action; the harness should reuse that rather than reimplement it.Escalating rather than implementing: measured honestly, this lands well over the loop's ~5-file/~150-line ceiling, so per the ground rules it stops here — with the shape written down so the next start is immediate.
Measured scope. A harness module (extraction reusing
check-actions.py's${{ }}substitution and action discovery, stub-command PATH injection, stdout/stderr/exit assertions — ~100 lines), the three existing verifications rewritten into it (release retry loop against stub curl 500/000/409/201; report-job-failure's four log states and three cannot-post paths; deploy-vps's four profile-guard states — ~60-100 lines each), a meta-test proving criterion 3 the waytest-check-actions.pydoes (drive a case asserting behaviour a body does not have, assert the harness fails — ~30 lines), and thepr.ymlstep. Total ~350-450 lines across ~6 files.Proposed layout, matching the repo's no-framework style:
.forgejo/scripts/behaviour_harness.py(library),.forgejo/scripts/behaviour-tests.py(the cases, one function per action),.forgejo/scripts/test-behaviour-harness.py(the criterion-3 meta-test, run first in CI like the checker's own test). Each case declares: action, step name, env, stub scripts, expected stdout/stderr patterns and exit status. Kill-vs-guard is recorded per case (the knowledge file's discipline): a kill case is documented with the mutation it was watched failing against.Two ways to proceed — your call:
(a) Authorize the loop to implement despite the ceiling — cleanest as two PRs: harness + meta-test + deploy-vps suite + CI wiring (
Refs #48), then the release and report-job-failure suites (Closes #48); each is independently green and reviewable.(b) Take it yourself from this shape.
Leaving it
state/readyand unassigned; nothing regresses while it waits — hand verification continues as now.Human decision recorded (2026-09-08): option (a) — the loop implements the two-PR sequence from the design comment (harness + meta-test + deploy-vps suite + CI wiring, then the remaining suites).
Starting under the recorded authorization, as PR 1 of the design's two-PR sequence (
Refs #48):behaviour_harness.py(run-body extraction reusing check-actions.py's${{ }}substitution, stub commands via PATH injection, stdout/stderr/exit assertions, kill-vs-guard recorded per case),test-behaviour-harness.py(criterion 3 the way test-check-actions.py does it: a case asserting behaviour a body does not have must fail), the deploy-vps profile-guard suite (all four states), and the pr.yml step running meta-test → suites. PR 2 adds the release retry-loop and report-job-failure suites and closes the issue.PR 1 (#62) is merged: the harness, its meta-test, and the deploy-vps profile-guard suite — six cases, five of them
kill, each watched failing against the mutant it was written for. PR 2 (the release retry-loop and report-job-failure suites) is still to come, so this stays open andstate/ready.