Uh oh!
There was an error while loading. Please reload this page.
test(chart): errorPattern asserted nothing, so 7 refusal tests asserted nothing (backend#2606) - #862
Conversation
…ed nothing (backend#2606)
helm-unittest 0.5.2 SILENTLY IGNORES every key under `failedTemplate`
except `errorMessage`. Re-measured here before touching anything:
`errorPattern: "ZZZ_THIS_STRING_APPEARS_NOWHERE_ZZZ"` on rbac_test.yaml's
perDatasetPvcs case ran 19 passed, 19 total. So each of these assertions
was exactly equivalent to a bare `failedTemplate: {}` while reading, in
review, like a pinned refusal.
`--strict` is not a substitute, and that was checked rather than assumed:
with `errorPattern` AND an invented `bogusKeyThatDoesNotExist` both set on
one assertion, `helm unittest --strict ./client` reported 19 passed, 19
total -- identical to the non-strict run.
Two kinds of failure, two remedies:
* Template `fail` (2, not the 1 the ticket table listed -- secrets_test.yaml's
bootstrapDbReparent case was missed there): converted to `errorMessage`
with the template's EXACT full string. Both mutation-proven -- reword the
message in the template, 1 failed / 18 passed and 1 failed / 29 passed
respectively; restore, 19 passed and 30 passed.
* values.schema.json rejection (5): NOT message-assertable at all. The
rejection fails at chart load, before any template renders, so
`errorMessage` compares against a render error that never happened.
These become a bare `failedTemplate: {}` with a comment saying why, so
nobody "strengthens" them back into vacuity. Each was probed in the other
direction too -- made the values legal, confirmed the assertion reddens --
so a bare assertion is not vacuous in the opposite sense.
The guard: scripts/tests/helm-unittest-error-assertions.sh, added to the
Makefile's DRIFT_GUARDS, so it runs in `Source-of-truth drift` -- REQUIRED
on develop and main. `Helm unit tests` is required on neither, so the guard
could only advise from there. It parses the YAML rather than grepping the
token, because the token is now named on purpose in several comments warning
people off it, and it allowlists `errorMessage` rather than blocklisting
`errorPattern`, because a typo (`errorMesage`) fails just as silently.
Mutation-proven both ways, plus all three fail-closed paths.
581 tests, 581 passed, before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>LukasWodka
commented
Aug 27, 2026
CI note for the audit trail, not a silent dismissal.
That is the metrics-server APIService not being registered yet in the freshly-created k3d cluster — a race in the job's own cluster bring-up, not something this diff can reach. This PR touches only helm-unittest suite files (never read by Checked rather than asserted: the same job is If it flakes the same way again on an unrelated PR it is worth its own ticket (wait on the |
# Conflicts: # Makefile
saadqbal
left a comment
There was a problem hiding this comment.
Seven assertions that read in review as pinned refusals and were equivalent to failedTemplate: {} is the worst kind of test bug, because the diff that introduces it looks more rigorous than the honest version. And secrets_test.yaml:326 carrying the comment "Assert the stable prefix via errorPattern, not the full message" is the detail that makes the case: someone chose it deliberately, believing it worked, and every reviewer since read the comment as evidence.
Three things I checked rather than took:
The allowlist-not-blocklist choice is load-bearing, and the reason is measured.errorPatternplus an invented bogusKeyThatDoesNotExist, under --strict, still passes 19/19 — so the plugin validates no key names under failedTemplate at all, and a typo fails exactly as silently as the key you happened to notice. Blocklisting errorPattern would have closed one instance of an open class.
The guard explains why it isn't a grep, which is the trap I'd have expected it to fall into: errorPattern is named on purpose in several test-file comments — including the ones explaining this very bug — so a grep would flag its own documentation. Choosing a parser over a pattern for that stated reason is the right call.
It states its own blind spot — the case where the template errors rather than fails needs a layer that can see stderr, and the comment says so instead of implying full coverage.
Finding the seventh occurrence by grep when the ticket's table said six is the difference between fixing the instances and fixing the class.
The .cursor/BUGBOT.md entry is the most valuable part of this PR and I want to be explicit about why. "Spelling is not evidence that anything reads it" is a genuinely new shape in that taxonomy — the fixture reaches the code, the mutation is live, the assertion is spelled right, and the runner ignores the key. And generalising it to "flag any new assertion key on a plugin/tool whose honoured set was not checked" makes it reusable beyond helm-unittest. I've twice this week said a recurring finding deserved a rule in that file and not written one; doing it in the same PR as the fix is the standard.
Green, no threads. 👍
Uh oh!
There was an error while loading. Please reload this page.
…nstall (#863) (#871) `Seal-check egress-enforcement (k3d)` failed intermittently BEFORE it tested anything: "resourceMonitor is enabled but the metrics.k8s.io/v1beta1 API is not registered." This is a harness race, not a chart defect. k3s registers its bundled metrics-server addon — and the v1beta1.metrics.k8s.io APIService the resource-monitor preflight looks up (client#823) — ASYNCHRONOUSLY, after nodes report Ready. The e2e harnesses gated only on `kubectl wait --for=condition=Ready nodes` and then helm-installed, so on a fast runner the install beat the addon and the preflight `fail`ed the whole release (#862 false-failed at 26s while #861 passed at 51s, neither touching the chart or these scripts). Fix: add e2e_wait_for_metrics_apiservice to scripts/tests/lib/e2e-common.sh and call it after node-Ready, before the helm install, in every harness that installs a preflight-carrying chart directly: e2e-seal-check.sh, e2e-full-seal.sh, and e2e-auto-upgrade.sh (whose first install is the last PUBLISHED chart, which carries the preflight too). The helper POLLS for the APIService to EXIST first — `kubectl wait` errors NotFound on a not-yet-created named object, so a bare `kubectl wait --for=condition=Available` would just swap one red for another in the same window — then best-effort waits for Available. It mirrors the production installer's _wait_for_metrics_apiservice (lib/install-client-helm.sh, client#553), which faces the identical race. Rejected the weaker options (resourceMonitor:false / metricsServerPreflight:false): both go green only by deleting the #823 preflight coverage this seal-check exists to exercise on a real cluster. New e2e-metrics-apiservice-wait.bats pins the invariant (all three harnesses wait before their first install; the wait polls-for-existence before the condition wait) so the guard cannot silently drift back out. Verified on real k3d (rancher/k3s:v1.36.3-k3s1): reproduced the exact resource-monitor-daemonset.yaml:69 fail when the APIService is absent; confirmed the helper blocks until registered+Available and the preflight then renders satisfied-by-apiservice. `make lint` clean; full bats suite failure set identical to develop tip (zero failures added). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Summary
failedTemplate+errorPatternasserts nothing in helm-unittest 0.5.2 — the plugin never reads the key. Seven such assertions across five suites were therefore exactly equivalent to a barefailedTemplate: {}while reading, in review, like a pinned refusal. This PR converts them, and gates the pattern so it cannot come back.Closes tracebloc/backend#2606
The proof, re-run here before anything was changed
Probe on
rbac_test.yaml's perDatasetPvcs case,errorPatternreplaced with a string that appears nowhere:--strictis not a substitute, and that was checked rather than assumed. WitherrorPatternand an inventedbogusKeyThatDoesNotExistboth set on one assertion:The plugin validates no key names under
failedTemplateat all. That is why the guard below allowlistserrorMessagerather than blocklistingerrorPattern— a typo fails just as silently.Ticket table was one short: 7, not 6
grep -rn errorPattern client/tests/found a seventh live assertion the ticket's table missed —secrets_test.yaml:326, thebootstrapDbReparentcase, also a templatefail. It carried the comment "Assert the stable prefix via errorPattern, not the full message", i.e. someone chose it deliberately, believing it worked. Converted here too. (Its full message names the credential's source indirectly, backend#947, and carries no literal value — so reproducing it in a public repo is safe for the same reason the template's own text is.)Remedy 1 — the 2 template
fails:errorMessage, mutation-provenclient/tests/rbac_test.yamlperDatasetPvcs requires clusterScope→perDatasetPvcs needs something else entirelyclient/tests/secrets_test.yaml...bootstrapDbPassword is unset→...something completely differentBoth mutation anchors were asserted applied before the run (
grep -con the mutated string = 1), and both failure outputs printed the expected-vs-actual strings, so the mutation was not inert. Aftercp-restore,git diff --quieton each template confirmed no residue and both suites returned 19 passed / 30 passed.Remedy 2 — the 5 schema rejections: bare, and documented as bare
A
values.schema.jsonrejection fails at chart load, before any template renders; the plugin reports an "errored" test anderrorMessagecompares against a render error that never happened. No message assertion can work. These becomefailedTemplate: {}with a comment stating why and pointing aterrorMessage/ the outside-the-plugin route, so nobody "strengthens" them back into vacuity.They were probed in the other direction too — a bare assertion that passes even on a legal config would prove nothing. Each
set:block was made legal and the assertion reddened:auto_upgrade_test.yaml— image.tag=latesttag: "3.16.4"image_refresh_test.yaml— ingestor.tag=latesttag: "0.8"image_refresh_test.yaml— image.tag=latesttag: "1.30.5"network_policy_test.yaml— empty clusterCidrs["10.0.0.0/8"]priority_class_pdb_test.yaml— empty name"tracebloc-training"Every anchor was asserted unique (
assert s.count(a) == 1) before substitution, and every file was restored from a copy afterwards.The guard
scripts/tests/helm-unittest-error-assertions.sh, added to the Makefile'sDRIFT_GUARDS(now 22 entries). It runs inSource-of-truth drift— a required status check ondevelopandmain. Deliberately not in helm-ci'sunittestjob:Helm unit testsis required on neither branch, so a guard there could only advise (backend#1729 rule 2).drift-checks.yamlhas nopaths:filter onpull_request, so it reports on every PR.Not a grep, for a concrete reason.
errorPatternis now named on purpose in several test-file comments — including the ones this PR added, warning people off it — so a text match on the token flags the warnings and not the violations. The guard parses the YAML and inspects the keys of afailedTemplatemapping, derived from*/tests/*_test.yamlso aningestor/testssuite added later is covered without editing the script.Mutation-proven, both directions of the rule:
Both offender paths name the exact assertion index, so the mutation demonstrably applied rather than the guard failing for some other reason.
All three fail-closed paths exercised (backend#1729 rule 3), in a throwaway tree:
Test counts — before / after
Unchanged by design: no test was added or removed, seven assertions changed from asserting nothing to asserting something (or to saying honestly that they cannot).
Other checks run
make check(parse + shellcheck + drift + helm-lint + helm-vocab): green.all 58 shell scripts parse;shellcheck: 64 file(s), severity=errorclean — the new script is picked up by the derived file set.chart-env-vocabulary: all 50 checks passed.make drift: all 22 guards green.scripts/check-style.sh: green after the.cursor/BUGBOT.mdedit.bats scripts/tests/bats-hygiene.bats check-style.bats chart-version-guard.bats: 72 ok, 0 not ok.make batssuite. It hung on this Mac for 20+ minutes insideinstall-client-helm.bats(host proxy -> values.yaml carries split proxy keys) and was killed. That file is untouched by this PR — no shell library, installer, or template changed — but I did not confirm the hang is pre-existing, soUnit testsin CI is the evidence for that tier, not a local run.Chart.yamlbump:scripts/chart-version-guard.sh:79recordstests/**as deliberately not chart content, and no template changed in the final diff.Docs kept true
.cursor/BUGBOT.mdgains a fourth shape of vacuous test alongside the existing three: an assertion key the test runner never reads — the fixture reaches the code, the mutation is live, the spelling is right, and the runner ignores the key.driftcomment described the list as "duplicated-declaration guards". That was already loose (the R8 manifest and cross-OS facts checks are house rules, not duplicated values) and this guard makes it looser, so the comment now states the real membership rule: a guard belongs there because it must block.Test plan
helm unittest ./client— 581/581, before and aftererrorMessageconversions mutation-proven red, then green on restoremake check/make driftgreenNote
Low Risk
Test and CI guard changes only; no chart templates or runtime behavior. Risk is limited to false positives in the new YAML key scanner if suite structure changes.
Overview
Helm-unittest 0.5.2 ignores
errorPatternunderfailedTemplate, so seven refusal tests were green on any failure while looking pinned in review. This PR fixes those assertions and blocks the pattern from returning.Template
failcases inrbac_test.yamlandsecrets_test.yamlnow useerrorMessagewith the exact template text. Schema-at-load rejections (five cases across auto-upgrade, image-refresh, network policy, priority class) stayfailedTemplate: {}with comments explaining why message matching cannot work inside the plugin.A new required drift guard,
scripts/tests/helm-unittest-error-assertions.sh, parses*/tests/*_test.yamland allowlists onlyerrorMessageunderfailedTemplate(not grep, because warning comments mentionerrorPattern). It is wired intoMakefileDRIFT_GUARDSsoSource-of-truth driftenforces it on develop/main..cursor/BUGBOT.mddocuments a fourth vacuous-test shape: assertion keys the runner never reads.Reviewed by Cursor Bugbot for commit a61186c. Bugbot is set up for automated code reviews on this repo. Configure here.