Skip to content

test(tests): make negated BATS assertions able to fail - #3790

Open
Aleksei Sviridkin (lexfrei) wants to merge 5 commits into
mainfrom
fix/cozytest-negated-assertions
Open

Aleksei Sviridkin (lexfrei) wants to merge 5 commits into
mainfrom
fix/cozytest-negated-assertions

Conversation

@lexfrei

@lexfrei Aleksei Sviridkin (lexfrei) commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Seventy-seven assertions across seven BATS suites begin with !. Seventy-two of them pass no matter what they find. POSIX exempts a command whose return value is inverted with ! from set -e, so the failing status is dropped where it is produced, and hack/cozytest.sh closes each test function with return 0, so it never resurfaces as the test's own status either. The other five are followed by || { ... exit 1; }, which reads the inverted status and does work; they are rewritten too, because telling the two apart needs the tail of every statement parsed, and a guard that guesses wrong there is green on a real one.

Switching to the bats binary does not rescue the seventy-two, which is the part worth knowing before reviewing this. Its ERR trap carries the identical exemption: bash -c 'set -eE; trap "echo TRAP" ERR; ! true; echo REACHED' prints REACHED, runs no trap and exits zero. The only negated assertion bats reports is one standing last in the test body, where the inverted status happens to be what the function returns. One of the seventy-two stood last; seventy-one did not.

What was lost is specific rather than diffuse. ! is how you spell "this must not appear", and an absence is what no other assertion in a test covers. The affected suites assert that a migration ran nothing destructive, that no image was copied, that a flag was not passed, that an object does not exist. On that half of their contract they held for any input.

Each site is now written as if cmd; then echo "FAIL: ..."; false; fi, the form already used in the files where this had been noticed one at a time, and each carries a message naming what must not have happened. hack/bats-no-negated-assert.bats scans every hack/**/*.bats and refuses the old form, so a new one cannot be added silently. Inverted conditions (if !, while !, until !) are untouched, because set -e is not what decides a condition. [ ! -f x ] is untouched for a different reason and remains an ordinary failing assertion: the ! there is an operator of test, not negation of a command, so nothing exempts its status.

The guard pins its own premise instead of describing it, on both sides. One test asserts the runner still passes a bare negated assertion; a second, that neither set -e nor the ERR trap sees an inverted status; a third, that the prescribed if cmd; then ...; false; fi does fail, under both runners, and at the form rather than somewhere before it, since a run that died early would satisfy a bare status check and pin nothing.

The third is the one every rewrite here rests on, and its failure mode is the reason it exists rather than being left implied. Were false inside a then body ever to stop propagating, the first test would still pass, because a negated assertion would still be vacuous, while all seventy-seven converted absences went silently green. A ban that prescribes a replacement owes an observer on the replacement, or it is the defect it removes, one level up.

Every claim the guard makes is pinned by removing the thing it claims, because on this file reading was repeatedly not enough. The recursion is exercised through the audit function against a fixture in a subdirectory, not through a second copy of its find expression: a copy keeps passing while the function loses its recursion, and an audit that returns empty reads as a clean tree. hack/e2e-apps/ is what makes recursion matter, since BATS_UNIT_FILES is a non-recursive wildcard and that directory is the one place make unit-tests never reaches. It is not what the test pins against, though, because it is emptying as suites move to Chainsaw and would eventually fail the test for a reason unrelated to recursion.

The rule's surface was checked in the other direction too. The same shape appears nowhere in hack/**/*.sh (including the libraries bats files source) or in the Chainsaw script: steps, so .bats is the whole surface rather than the part that was convenient to guard. Both zeros were taken twice, line-by-line and with continuations folded, because a line-oriented search cannot see a statement split across a continuation. The first attempt at the folded pass disagreed until its if-context filter was restored, which is why the answer is stated as measured twice rather than as measured.

The scan is lexical, so its header names the statement boundaries it owns rather than implying it understands shell: ; & && || { (, the ) closing a case pattern, and the words then do else, each measured against the runner before being added. A keyword may legally be preceded only by whitespace or by ;, since every other separator in front of then, do or else is a shell syntax error, checked with sh -n rather than recalled. The scan therefore admits exactly those two, and that part of the class is closed rather than patched one spelling at a time. All three keywords are vacuous after either spelling, so missing one is a silent miss, the single direction this scan must not err in, given that its whole design trades noise for safety.

Four over-counts remain and are deliberate, each named in the header: ( ! cmd ) does propagate its inverted status and is reported anyway; so would a ! opening a statement after a redirection &; x=$(true || ! false) is reported although the || reads the inverted status; and a condition continued by a trailing && instead of a backslash is reported, because the strip only recognises a condition whose end it can see. Each costs one rewritten line, where the opposite error costs a silent pass. They are enumerated rather than engineered away on purpose, because a removed error leaves no trace while a listed one tells the next reader what they are looking at. For the same reason, the one place the scan errs quiet is named too: a file cut off midway through a line continuation loses its last buffered statement. That input is not a runnable test, since sh -n rejects it with unexpected end of file, so nothing executable can hide there. The header still claims the scan errs loud or not at all, and this is the exception to that claim rather than to its coverage. The test naming the shapes claims only what its fixture holds. A lexical scan over shell cannot promise it has enumerated the language, and a name that promised it would be false whether or not a gap existed today.

The branch name is narrower than the subject and I am leaving it as it is rather than churn it. This is not a defect in hack/cozytest.sh that moving to the bats binary would fix. It is a POSIX rule both runners inherit, and moving to bats would close one of the seventy-two sites.

One suite went red for real once its assertions could fail. seaweedfs-guard-parity forbids four mutable discriminators by grepping each chart, and both charts carry a template comment block arguing why those signals were rejected, so the grep matched the argument and would have reported a chart for documenting the rule it follows. The charts are correct; the assertion was not. It now strips {{/* ... */}} before matching. Two details there are load-bearing and neither is obvious. The strip removes the comment span, not the line that opens it: dropping the line is shorter and silently loses a discriminator sharing a line with a comment, which is a chart classifying on a forbidden signal and a test reporting that it does not. And the delimiters are checked to balance first, because every {{/* is read as an opener, so one inside a quoted string (these charts carry long fail (printf ...) messages) would swallow the rest of the file and leave the checks reading nothing.

A last commit widens six comments that explained this trap as applying to a "negated pipeline". It is not about pipelines, and the narrower wording invites the conclusion that the other shapes are safe. Comment text only; each of those files already uses the working form.

Verification

make bats-unit-tests passes 556 tests and then stops at migration-seaweedfs-db-adopt, so the suites ordered after it were run in a second pass: 387 more, 943 green in total, measured in a worktree separate from the one being edited.

Three suites are red on this macOS host and each one reproduces at the merge base, so none is this branch's. migration-seaweedfs-db-adopt: its container cannot write into the bind-mounted temp directory. seaweedfs-naming-audit: fails the same test with the same counts at the merge base. release-changelog-behaviour: gated on command -v act and docker info, it skipped silently while this host had no Docker running, and began failing once Docker came up, on an act incompatibility with the Colima socket path. That last red is a property of the host having Docker at all, which is worth knowing before reading a local run as a regression.

Each suite whose assertions changed and that runs on this host was measured before the edit and matches that baseline now. The six suites that received only a reworded comment were run after the change and not before, which is the weaker claim and the true one, since nothing executable in them was touched.

migration-seaweedfs-db-adopt was then run on Linux, where it passes 15 of 15. Its nine rewritten sites also carry a two-sided check, because a suite that goes green proves nothing on its own: with the pattern swapped for one the same test proves is present, the rewritten form fails the suite and the original ! form passes it. Same file, same test, same pattern, same runner, only the spelling differs.

Two sites are still unverified and I would rather say so than imply otherwise: both are in e2e-apps/monitoring-oidc-customconfig, which needs a live cluster. It will not get one soon either, because the directory holding it is wired into no workflow, no Makefile target and no suite selector, a leftover of the move to Chainsaw that is filed as #3786. Those two assertions were vacuous before this change and are real now, so whichever way that issue is resolved, they stop lying in the meantime.

Screenshots

Not a UI change.

Downstream repositories

Walked the trigger map against the diff file by file. The change touches seven BATS suites, adds one, and adds a convention to docs/agents/e2e-testing.md. It does not touch hack/e2e-prepare-cluster.bats, which is the node contract ansible-cozystack and talm restate; it moves and renames nothing under hack/ and changes no make target's behaviour, which is what ccp gates on; and it touches no package, schema, enum, default, CRD, label, annotation or metric.

Release note

NONE

Summary by CodeRabbit

  • Documentation

    • Added guidance prohibiting negated Bats assertions that can mask test failures.
    • Documented the required explicit conditional failure pattern and updated the reviewer checklist.
  • Tests

    • Added automated checks to detect unsafe negated assertions in end-to-end tests.
    • Updated existing tests with clearer failure messages and more reliable conditional checks.
    • Expanded Helm template comment handling coverage in SeaweedFS guard tests.

Closes #3565

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cebe3e36-ba30-45f2-8b52-8c83cb0d6942

📥 Commits

Reviewing files that changed from the base of the PR and between 3f24b65 and f7e64af.

📒 Files selected for processing (1)
  • docs/agents/e2e-testing.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/agents/e2e-testing.md

📝 Walkthrough

Walkthrough

The PR documents a rule against negated BATS assertions, adds a repository-wide scanner, converts existing assertions to explicit failure branches, and strengthens SeaweedFS Helm template parity checks.

Changes

Negated BATS assertion enforcement

Layer / File(s) Summary
Policy and repository audit
AGENTS.md, docs/agents/e2e-testing.md, hack/bats-no-negated-assert.bats
Documents the prohibited ! assertion form, adds the explicit if ...; then ...; false; fi pattern, and tests recursive scanning, exclusions, line reporting, and runner behavior.
Explicit failure assertion migration
hack/*.bats, hack/e2e-apps/*, hack/migration-50-etcd-adopt.bats, hack/migration-seaweedfs-db-adopt.bats, hack/nightly-mirror_test.bats, hack/promote-retag_test.bats, hack/release-changelog-contract.bats
Replaces negated assertions with explicit conditional failures and diagnostic messages. Related comments explain the set -e behavior.

SeaweedFS template parity validation

Layer / File(s) Summary
Comment-stripped template validation
hack/seaweedfs-guard-parity.bats
Adds Helm comment stripping with inline and multiline fixture coverage. The discriminator checks validate balanced comments and inspect executable template content.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BatsRunner
  participant NegatedAssertionAudit
  participant BatsSuite
  BatsRunner->>NegatedAssertionAudit: Run repository audit
  NegatedAssertionAudit->>BatsSuite: Scan .bats statements
  BatsSuite-->>NegatedAssertionAudit: Report statements beginning with !
  NegatedAssertionAudit-->>BatsRunner: Fail with replacement guidance
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: myasnikovdaniil

Merge Risk: ⚪ Minimal · up to f7e64

The PR makes previously ineffective negated BATS assertions fail and adds a guard against regressions; no actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: fixing negated BATS assertions so that they correctly fail when prohibited conditions occur.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/cozytest-negated-assertions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/agents/e2e-testing.md`:
- Line 141: Qualify the negated-assertion guidance so it says ! cmd is
unreliable and may pass unexpectedly rather than claiming it cannot fail; retain
the prohibition, including the tail-position bats exception. Apply this wording
change in docs/agents/e2e-testing.md:141-141 and AGENTS.md:31-31.

In `@hack/e2e-apps/monitoring-oidc-customconfig.bats`:
- Around line 123-128: Update both absence-check blocks in
hack/e2e-apps/monitoring-oidc-customconfig.bats:123-128 and
hack/e2e-install-cozystack.bats:617-621 to run kubectl get with
--ignore-not-found -o name, explicitly fail when kubectl returns an error, and
fail when the command produces non-empty output. Preserve the existing failure
messages for detected KeycloakClient and KeycloakClientScope resources.

In `@hack/seaweedfs-guard-parity.bats`:
- Around line 53-70: Replace the raw opener/closer counting checks with the
stateful parsing in template_code. Make template_code return nonzero when inc
remains set at EOF, while preserving its existing output behavior for valid
templates, and add a fixture covering {{/* inside an active Helm comment.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1de527b3-132f-43fa-9a26-9bafd83c3d7e

📥 Commits

Reviewing files that changed from the base of the PR and between e944619 and 001d62c.

📒 Files selected for processing (16)
  • AGENTS.md
  • docs/agents/e2e-testing.md
  • hack/bats-no-negated-assert.bats
  • hack/build-matrix_test.bats
  • hack/capture-dataplane.bats
  • hack/capture-previous-logs.bats
  • hack/common-envs_test.bats
  • hack/cozyreport.bats
  • hack/e2e-apps/monitoring-oidc-customconfig.bats
  • hack/e2e-install-cozystack.bats
  • hack/migration-50-etcd-adopt.bats
  • hack/migration-seaweedfs-db-adopt.bats
  • hack/nightly-mirror_test.bats
  • hack/promote-retag_test.bats
  • hack/release-changelog-contract.bats
  • hack/seaweedfs-guard-parity.bats

Comment thread docs/agents/e2e-testing.md Outdated
Comment thread hack/e2e-apps/monitoring-oidc-customconfig.bats Outdated
Comment thread hack/seaweedfs-guard-parity.bats
@github-actions github-actions Bot added size/XL This PR changes 500-999 lines, ignoring generated files area/testing Issues or PRs related to testing (e2e, bats, unit tests) labels Aug 12, 2026
@lexfrei Aleksei Sviridkin (lexfrei) added the debug Debugging in progress label Aug 14, 2026
@lexfrei
Aleksei Sviridkin (lexfrei) force-pushed the fix/cozytest-negated-assertions branch 3 times, most recently from cd55ebf to 3c84002 Compare August 15, 2026 07:54
@lexfrei
Aleksei Sviridkin (lexfrei) force-pushed the fix/cozytest-negated-assertions branch 8 times, most recently from 22b8dd9 to 9cf097e Compare August 17, 2026 00:10
@lexfrei
Aleksei Sviridkin (lexfrei) force-pushed the fix/cozytest-negated-assertions branch 4 times, most recently from e450a5c to e54ccf9 Compare August 20, 2026 08:48
@lexfrei Aleksei Sviridkin (lexfrei) removed the debug Debugging in progress label Aug 20, 2026
IvanHunters
IvanHunters previously approved these changes Aug 21, 2026

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Empirically confirmed the crux under real bats: the old ! echo|grep -q FORBIDDEN passes vacuously (exit 0) when the token is present, while the new if grep; then false; fi fails under both runners. Completeness is proven: an independent grep over hack/**/*.bats finds zero remaining negated asserts outside the guard file, and the new lexical guard (9/9) blocks regressions. Ran six converted suites, no not-ok, so no new always-failing asserts.

  • [MINOR] hack/monitoring-oidc-customconfig.bats — the one conversion under hack/e2e-apps/ is not run by make unit-tests and needs a live cluster; pattern is identical to the rest, verified statically.

@github-actions github-actions Bot added size/XXL This PR changes 1000+ lines, ignoring generated files and removed size/XL This PR changes 500-999 lines, ignoring generated files labels Sep 7, 2026
The parity suite forbids four mutable discriminators -- claim
timestamps, ready-replica counts and the two birth-order flags -- by
grepping each chart's whole file. Both charts carry a template comment
block arguing why those signals were rejected, so the grep matched the
argument and would have reported a chart for documenting the rule it
follows. It never did report: the checks were spelled `! grep`, a form
POSIX exempts from errexit, so they could not fail whatever they found.

Strip the {{/* ... */}} spans before matching and let the loop fail
through `false`, naming the signal it matched.

The span and not the line. Dropping every line that opens a comment is
shorter and silently loses a discriminator that shares its line with
one -- `{{- $x := .creationTimestamp }} {{/* why */}}` leaves with the
comment, the checks find nothing, and the balance count stays even
because that line both opens and closes. The two spellings agree on
today's charts, so a fixture pins the difference rather than trusting
it.

Have the strip say when it ended inside a comment, and ask before
reading what it returned. Every `{{/*` is read as an opener, including
one inside a quoted string -- these charts carry long `fail (printf
...)` messages -- and a lone opener swallows the rest of the file,
leaving the checks reading nothing. A canary token cannot cover that:
it only proves whatever precedes IT survived, and the block most
likely to swallow is the last one in the file.

It says so on stdout rather than through an exit status, because
hack/cozytest.sh rewrites every `}` at column 0 into `return 0; }` --
the strip's own closing brace included -- so a status it returned
would never reach the caller.

Counting delimiters cannot stand in for the state the strip already
carries. Go template comments do not nest, so the first `*/}}` closes
the block and `{{/* the opener is spelled {{/* */}}` is a comment Helm
renders, while its two openers against one closer read as an imbalance
that is not one.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>
An assertion written `! cmd` cannot fail a test. POSIX exempts a
command whose return value is inverted with `!` from errexit, so the
status is dropped where it is produced. hack/cozytest.sh then closes
each test function with `return 0`, and the bats binary's ERR trap
carries the same exemption -- it reports only a negated assertion
standing last in the body, where the inverted status happens to be
what the function returns.

Every absence these suites assert was written that way: a command that
must not reach the log, an image that must not be copied, a flag that
must not be passed, an object that must not exist. On that half of
each suite's contract the tests held for any input.

Rewrite each as `if cmd; then echo "FAIL: ..."; false; fi`, the form
already used where the trap had been noticed one file at a time, and
give every one a message naming what must not have happened. The
message states the property, not the pattern: where the forbidden
string is a substring of a required value -- an https endpoint the
test requires in its http form -- naming the string would describe the
opposite of what is asserted.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>
A negated assertion is the natural way to spell "this must not
appear", and an absence is what nothing else in a test covers, so the
errexit exemption lands where it is least likely to be noticed and
invisible in a green run. Switching runners does not lift it: the ERR
trap the bats binary reports through carries the same exemption, so
neither runner sees a negated assertion that has any statement below
it.

Add a suite that scans every hack/**/*.bats, subdirectories included,
and fails on a statement whose first token is `!` -- naming the file,
the line the statement opens on, and the form to use instead.

Statement boundaries are the POSIX separators, each measured against
the runner before being admitted: `;` `&` `&&` `||` `{` `(`, the `)`
closing a case pattern, and the words `then` `do` `else`. A keyword may
legally be preceded only by whitespace or by `;` -- every other
separator in front of one is a shell syntax error, checked with `sh -n`
rather than recalled -- so the scan admits exactly those two and that
part of the class is closed rather than patched a spelling at a time.
Both spellings are vacuous under the runner, so missing either is a
silent miss, the one direction this scan must not err in. Four
over-counts remain, all erring loud, each named in the header: they are
enumerated rather than removed, because a removed error leaves no trace
while a named one tells the next reader what they are looking at.

Inverted conditions are untouched because errexit is not what decides
a condition. `[ ! -f x ]` is untouched for a different reason and
stays a normal failing assertion: the `!` there is an operator of
`test`, not negation of a command, so nothing exempts its status.

Each claim the suite makes is pinned against removing the thing it
claims, and the separator class is measured branch by branch rather than
only at its newest entries. The recursion is exercised through the audit
function on a fixture in a subdirectory rather than through a second
copy of its find expression, because a copy passes while the function
loses its recursion -- and an audit that returns empty reads as a clean
tree. The condition-strip carries fixtures that put the negation after
an `&&` inside the condition, where only the strip can save it. The
continuation fold is pinned by a fixture whose negation sits on the
continued line, so folded and unfolded name different lines. Both
keyword spellings have fixtures of their own. The subshell over-count
carries a fixture for both of its halves -- that the scan reports the
shape, and that the shape really does fail -- because `(` has no other
user in that class, so dropping it is otherwise a change nothing here
can see.

Both halves of the premise are pinned, not just the half about the
banned form. A test asserts the runner still passes a bare negated
assertion; a second asserts the prescribed `if cmd; then ...; false; fi`
does fail, under both runners, and fails AT the form rather than
somewhere before it. Every converted absence rests on that second
half, and its failure mode is asymmetric: were `false` in a `then`
body to stop propagating, the first test would still pass while every
conversion went silently green.

The rule joins the E2E conventions doc as its own section, and the
inline list in AGENTS.md that an agent may read instead of the doc --
it belongs beside the EXIT-trap ban for the same reason, both leaving
a green suite that checks nothing.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>
Six suites explain why they avoid `! cmd` by saying errexit is
suppressed for a "negated pipeline". The exemption is not about
pipelines: a bare `! true`, a negated `[ ... ]`, one inside a `for`
body or a brace group, one in a case branch, and one following `;`,
`&`, `&&` or `||` are all exempt too. Anyone reading the narrower
wording could conclude the other shapes are safe.

One of the six carries the sentence twice, and the second copy is
split across two physical lines -- `` `!` `` ending one and
`pipeline)` opening the next -- so a line-oriented search for the
phrase does not see it. Prose wraps; a grep for it has to fold the
lines first.

Comment text only; every one of these files already uses the working
form the comment recommends.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>
Three absence assertions decide on `kubectl get`'s exit status alone.
That status is 1 for a NotFound and 1 for a connection, auth or
discovery failure, so an api-server the test cannot reach reads as
proof the object is gone -- and the assertion that the no-delete
guardrail can be bypassed is one of the three.

`--ignore-not-found` collapses the NotFound alone into success with
empty output, which leaves the status free to mean a failed read.
Read the two separately: a non-zero status fails the test naming the
read that did not answer, non-empty output fails it naming the object
that must not be there.

Capture stdout only. kubectl writes deprecation and throttling notices
to stderr on a call that succeeded, and folding those into the value
under test would report an object that is not there.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>
@github-actions github-actions Bot added size/XL This PR changes 500-999 lines, ignoring generated files and removed size/XXL This PR changes 1000+ lines, ignoring generated files labels Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/testing Issues or PRs related to testing (e2e, bats, unit tests) size/XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Assertions written as ! command cannot fail a test

2 participants