fleetd #555: lift 8 main-flow decisions into tested predicate/dispatch functions #565
Reference in New Issue
Block a user
Delete Branch "worker/555-redeploy-main-flow-seam-65c2f5-2"
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?
Closes #555.
What changed
scripts/redeploy-fleetd.sh's main flow had 8 bareif/casedecisionsliving outside any function, so
scripts/test-redeploy-fleetd.sh(67 tests)could not reach them — any one could be silently inverted with the whole
suite green. This follows the existing
swap_if_built/refuse_drain_gatepattern: each bare guard becomes a small predicate or dispatch function,
called unconditionally by the main flow, so the decision is unit-testable.
The 8 lifted decisions (line numbers as measured on this branch, before the
refactor moved things around):
if [ "$CHECK_ONLY" = 1 ]→should_stop_for_check/stop_if_check_onlyif [ -n "$OLD_PID" ] && [ "$ASSUME_YES" = 0 ]→drain_gate_requiredif [ "$reply" != "yes" ]→drain_confirmedcase "$SUPERVISOR_KIND"→report_supervisor_statecase "$SUPERVISOR_KIND"→dispatch_stopcase "$SUPERVISOR_KIND"→dispatch_startif [ -z "$HEALTH_BODY" ]→poll_health_body/health_is_up/report_healthHAD_OLD_PID=0; [ -n "$OLD_PID" ] && HAD_OLD_PID=1→compute_had_old_pidThe structural guard
Adds
test_no_untested_main_flow_conditionals, which scans the main flow(everything after the
SOURCEDguard) for bareif/elif/caselinesoutside any function body — function boundaries are tracked via this file's
one consistent
name() {/}convention. Any bare conditional not in theexplicit
MAIN_FLOW_ALLOWED_CONDITIONALSallowlist (report-only/displayconditionals, plus the two
#504-family supervisorelifbranches that areout of scope here) fails the test, naming the offending line. This is the
"shape, not the eight sites" guard the ticket asked for.
Testing
Full suite run directly (no pipe into
tail/head), exit code checkedexplicitly rather than trusting
grep -c '^FAIL:'alone (a dead/abortedsuite reports 0 the same as a clean one — the #550 bug in this same file):
bash scripts/test-redeploy-fleetd.sh(bash 5.3.9): exit 0, 0 linesmatching
^FAIL:, reached the finalPASS: redeploy log classifierline./bin/bash scripts/test-redeploy-fleetd.sh(macOS bash 3.2.57): exit 0,0 lines matching
^FAIL:, reached the same PASS line.bash -nand/bin/bash -nboth pass on both changed files.Each of the 8 lifted decisions was proven with a mutation test: a
line-anchored
sedmutation of the pristine source line (neverregex/perl), exact pristine-line-count via
awkbefore and after, observedRED with the exact new-test failure message, restored the file and verified
byte-identical via
shasum -a 256against the pre-mutation copy, thenre-ran the suite green as a control. The same procedure proved the
structural guard itself: a deliberate unguarded
ifwas inserted into themain flow, the guard test went RED naming that exact line, the line was
removed, and the suite went green again with a shasum-verified restore.
Out of scope
#504items 2/3/4 and#528item 2 are the same "untested main flow"family and were not touched — this seam generalizes to make them
testable too, but lifting them is left for their own tickets, per the
brief.
redeploy-fleetd.sh's main flow had 8 bare if/case decisions (CHECK_ONLY short-circuit, drain-gate entry+confirm, supervisor report/stop/start dispatch, health-poll decision, HAD_OLD_PID computation) that lived outside any function, so the 67-test suite could not reach them and any one could be silently inverted with the whole suite green. Follows the existing swap_if_built/refuse_drain_gate pattern: each bare guard becomes a small predicate or dispatch function (should_stop_for_check, drain_gate_required/drain_confirmed/run_drain_gate, report_supervisor_state, dispatch_stop, dispatch_start, health_is_up/report_health, compute_had_old_pid), called unconditionally by the main flow so the decision itself is unit-testable in isolation. Adds a structural guard, test_no_untested_main_flow_conditionals, that scans the main flow (everything after the SOURCED guard) for bare if/elif/case lines outside any function body, tracking function boundaries via this file's one consistent name() { / } convention. It fails on any new bare conditional not covered by MAIN_FLOW_ALLOWED_CONDITIONALS, an explicit exact-text allowlist of the report-only/display conditionals and the two #504-family supervisor elif branches that stay out of scope for this ticket. This is the "shape, not the eight sites" guard the ticket asked for: a ninth bare decision fails immediately, naming its line. Out of scope, not touched: #504 items 2/3/4 and #528 item 2 (same untested-main-flow family) — the seam here generalizes to make them testable too, but lifting them was left for their own tickets.