Skip to content

test: make negative bats assertions fail under cozytest - #3132

Merged
Aleksei Sviridkin (lexfrei) merged 1 commit into
mainfrom
fix/cozytest-negative-assertions
Jun 30, 2026
Merged

test: make negative bats assertions fail under cozytest#3132
Aleksei Sviridkin (lexfrei) merged 1 commit into
mainfrom
fix/cozytest-negative-assertions

Conversation

@lexfrei

@lexfrei Aleksei Sviridkin (lexfrei) commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

What this PR does

Every *.bats file in the repo runs through hack/cozytest.sh, not real bats — the e2e files via packages/core/testing/Makefile and the hack/*.bats unit tests via make bats-unit-tests. cozytest runs each @test under set -e, and POSIX errexit is suppressed for a pipeline beginning with !. So a bare ! cmd must-not-happen assertion never fails the test: when the forbidden condition actually occurs (the command succeeds), the negated pipeline returns non-zero but errexit ignores it and the test passes anyway. Ten such assertions across six files were silently vacuous.

This converts each to if cmd; then echo "<reason>"; false; fi. The if condition is exempt from errexit (so the probe itself never aborts the test), and the false in the taken branch is a plain command under set -e, so it aborts and fails the test with a legible message when the forbidden condition holds. This is the must-not-happen inverse of the cmd || { echo …; false; } pattern already used in the suite (e.g. harbor.bats, per docs/agents/e2e-testing.md). Original redirections and quoting are preserved, so happy-path behavior is unchanged — only the failure path now actually fails.

Converted assertions:

  • bucket: a read-only user must not be able to upload (access-control regression guard)
  • gateway: a child tenant must not have its own gateway HelmRelease (inheritance)
  • kuberture: external-dns public/internal hostname isolation
  • e2e-install-cozystack: tenant-name validation must reject rather than warn; the version ConfigMap must be gone after delete
  • build-matrix (unit test): talos/installer must be excluded from the parallel matrix
  • capture-dataplane (unit test): kube-dns and pending rows must be filtered out

A separate in-flight etcd test change owns two more such assertions in etcd.bats; they are left out of this PR to avoid a conflicting edit.

Release note

NONE

Summary by CodeRabbit

  • Tests
    • Strengthened several end-to-end/Bats assertions to fail more explicitly when expected cluster conditions aren’t met.
    • Improved matrix validation to reliably confirm excluded components are absent.
    • Updated dataplane load balancer filtering, bucket readonly upload, and gateway inheritance checks to avoid false/ambiguous pass cases.
    • Tightened DNS hostname isolation probes and cozystack installer validations (tenant name dashes, deletion protection) with clearer “expected vs found” failures.

@github-actions github-actions Bot added area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review size/M This PR changes 30-99 lines, ignoring generated files labels Jun 29, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a subtle issue in the test suite where negative assertions were failing to trigger test failures due to how cozytest.sh handles set -e and negated pipelines. By refactoring these assertions, the suite now correctly enforces negative constraints, preventing silent regressions in access control, resource inheritance, and validation logic.

Highlights

  • Assertion Logic Fix: Replaced vacuous ! cmd assertions with if cmd; then echo '...'; false; fi patterns to ensure test failures are correctly triggered under set -e in the cozytest environment.
  • Test Reliability: Fixed ten silent test regressions across six files, ensuring that negative assertions now properly abort the test suite when the forbidden condition is met.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8f39dc78-a979-4ded-a3eb-cea54b01d41f

📥 Commits

Reviewing files that changed from the base of the PR and between 0123b20 and 12fe325.

📒 Files selected for processing (6)
  • hack/build-matrix_test.bats
  • hack/capture-dataplane.bats
  • hack/e2e-apps/bucket.bats
  • hack/e2e-apps/gateway.bats
  • hack/e2e-apps/kuberture.bats
  • hack/e2e-install-cozystack.bats
🚧 Files skipped from review as they are similar to previous changes (6)
  • hack/capture-dataplane.bats
  • hack/e2e-install-cozystack.bats
  • hack/e2e-apps/gateway.bats
  • hack/e2e-apps/kuberture.bats
  • hack/build-matrix_test.bats
  • hack/e2e-apps/bucket.bats

📝 Walkthrough

Walkthrough

Across six Bats tests, negated pipeline assertions were replaced with explicit conditional failures so the checks fail reliably under set -e.

Changes

Bats negation assertion fixes

Layer / File(s) Summary
Explicit failure guards
hack/build-matrix_test.bats, hack/capture-dataplane.bats, hack/e2e-apps/bucket.bats, hack/e2e-apps/gateway.bats, hack/e2e-apps/kuberture.bats, hack/e2e-install-cozystack.bats
The tests now use explicit if ...; then ...; false; fi checks instead of negated grep or kubectl get assertions, with failure messages and comments updated where present.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • cozystack/cozystack#1283: Touches hack/e2e-apps/bucket.bats, including the mc cp upload assertion path.
  • cozystack/cozystack#2983: Touches hack/build-matrix_test.bats and the packages/core/talos / packages/core/installer matrix exclusion logic.

Suggested reviewers

  • kvaps
  • lllamnyp
  • IvanHunters

Poem

🐇 I hopped through tests with careful feet,
and turned each sly ! into defeat.
Now failures shout when bad things appear,
so silent passes disappear.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing negative Bats assertions so they fail correctly under cozytest.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/cozytest-negative-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.

@dosubot dosubot Bot added area/testing Issues or PRs related to testing (e2e, bats, unit tests) kind/bug Categorizes issue or PR as related to a bug labels Jun 29, 2026

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request updates several BATS test files to replace vacuous negative assertions (using !) with explicit if checks and false exits, ensuring that regressions are properly caught under set -e execution. The reviewer suggests using printf instead of echo when printing variables containing arbitrary command or log output to prevent potential parsing and portability issues.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +24 to +25
if echo "$out" | grep -q '"packages/core/talos"'; then echo "FAIL: packages/core/talos must be excluded from the parallel matrix"; false; fi
if echo "$out" | grep -q '"packages/core/installer"'; then echo "FAIL: packages/core/installer must be excluded from the parallel matrix"; false; fi

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.

low

Using echo to print variables containing arbitrary command output (like $out) can lead to portability and parsing issues if the output contains backslashes or starts with hyphens (interpreting them as options). It is safer and more robust to use printf '%s\n' instead, which is also consistent with the pattern used in hack/capture-dataplane.bats.

  if printf '%s\n' "$out" | grep -q '"packages/core/talos"'; then echo "FAIL: packages/core/talos must be excluded from the parallel matrix"; false; fi
  if printf '%s\n' "$out" | grep -q '"packages/core/installer"'; then echo "FAIL: packages/core/installer must be excluded from the parallel matrix"; false; fi

# would not fail the test; assert the absence via `if ... grep; then ...; false`.
echo "${pub_logs}" | grep -F "${pub_host}" >/dev/null
! echo "${pub_logs}" | grep -F "${int_host}" >/dev/null
if echo "${pub_logs}" | grep -F "${int_host}" >/dev/null; then echo "FAIL: public-prefix probe must NOT see the internal hostname ${int_host}"; false; fi

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.

low

Using echo to print variables containing arbitrary log output (like ${pub_logs}) can lead to portability and parsing issues if the logs contain backslashes or leading hyphens. It is safer and more robust to use printf '%s\n' instead.

  if printf '%s\n' "${pub_logs}" | grep -F "${int_host}" >/dev/null; then echo "FAIL: public-prefix probe must NOT see the internal hostname ${int_host}"; false; fi

# And the inverse for the internal-prefix probe.
echo "${int_logs}" | grep -F "${int_host}" >/dev/null
! echo "${int_logs}" | grep -F "${pub_host}" >/dev/null
if echo "${int_logs}" | grep -F "${pub_host}" >/dev/null; then echo "FAIL: internal-prefix probe must NOT see the public hostname ${pub_host}"; false; fi

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.

low

Using echo to print variables containing arbitrary log output (like ${int_logs}) can lead to portability and parsing issues if the logs contain backslashes or leading hyphens. It is safer and more robust to use printf '%s\n' instead.

  if printf '%s\n' "${int_logs}" | grep -F "${pub_host}" >/dev/null; then echo "FAIL: internal-prefix probe must NOT see the public hostname ${pub_host}"; false; fi

# into a "warn" variant, the server could still accept the object. A bare
# `! echo | grep` is vacuous under cozytest's `set -e` (suppressed for a `!`
# pipeline), so the regression would slip through; assert via `if ...; false`.
if echo "$output" | grep -qi "created"; then echo "FAIL: kubectl reported the tenant as created — validation must reject it, not warn"; false; fi

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.

low

Using echo to print variables containing arbitrary command output (like $output) can lead to portability and parsing issues if the output contains backslashes or leading hyphens. It is safer and more robust to use printf '%s\n' instead.

  if printf '%s\n' "$output" | grep -qi "created"; then echo "FAIL: kubectl reported the tenant as created — validation must reject it, not warn"; false; fi

@myasnikovdaniil myasnikovdaniil 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.

The fix is correct and complete within its stated scope. Under cozytest.sh, each @test runs with set -e, but POSIX exempts ! pipeline from errexit — making all ten bare ! cmd assertions silently vacuous. Replacing them with if cmd; then echo "FAIL: …"; false; fi is the right inverse of the || { …; false; } pattern already used in the suite for the must-succeed direction. All affected files are covered; the etcd.bats carve-out is clearly justified and noted in the PR body.

Two optional suggestions inline: multi-line if form for readability, and adding a kubectl describe to the failure branch per the project convention of dumping scoped diagnostics on failure. Neither is a blocker.

# is vacuous under cozytest's `set -e` (suppressed for a `!` pipeline), so if
# the child wrongly got its own HelmRelease the test would still pass; assert
# via `if kubectl get; then ...; false`.
if kubectl -n tenant-test-gwparent-gwchild get helmrelease gateway 2>/dev/null; then echo "FAIL: child tenant must NOT have its own gateway HelmRelease (gateway inheritance broken)"; false; fi

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.

Nit: the single-line form is 150+ characters and hard to extend. The e2e conventions recommend dumping scoped diagnostics on failure — if the child unexpectedly has its own HelmRelease, a describe in the failure branch shows why without needing to reproduce:

if kubectl -n tenant-test-gwparent-gwchild get helmrelease gateway 2>/dev/null; then
  echo "FAIL: child tenant must NOT have its own gateway HelmRelease (gateway inheritance broken)"
  kubectl -n tenant-test-gwparent-gwchild describe helmrelease gateway || true
  false
fi

# A bare `! kubectl get` is vacuous under cozytest's `set -e` (errexit is
# suppressed for a `!` pipeline), so a delete that silently failed would not
# fail the test; assert the absence via `if kubectl get; then ...; false`.
if kubectl get configmap cozystack-version -n cozy-system 2>/dev/null; then echo "FAIL: cozystack-version configmap must be gone after delete with the no-delete label removed"; false; fi

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.

Same style nit: multi-line form + a describe on failure would capture why the ConfigMap survived the delete (e.g. a re-protection race from the admission webhook or a finalizer):

if kubectl get configmap cozystack-version -n cozy-system 2>/dev/null; then
  echo "FAIL: cozystack-version configmap must be gone after delete with the no-delete label removed"
  kubectl describe configmap cozystack-version -n cozy-system || true
  false
fi

@lexfrei
Aleksei Sviridkin (lexfrei) force-pushed the fix/cozytest-negative-assertions branch from f17a31d to 0123b20 Compare June 30, 2026 13:12
These bats files are all run by hack/cozytest.sh, which executes each
@test under `set -e`. POSIX errexit is suppressed for a pipeline that
begins with `!`, so a bare `! cmd` must-not-happen assertion never fails
the test: when the forbidden condition actually occurs (cmd succeeds) the
negated pipeline returns non-zero but errexit ignores it and the test
passes anyway. These assertions were silently vacuous.

Replace each with `if cmd; then echo "<reason>"; false; fi`, the idiom
already used and documented elsewhere in these files — the if-condition
is exempt from errexit and the `false` in the taken branch fails the test
loudly. The forbidden condition now fails the test as intended:

- bucket: read-only user must not be able to upload (access control)
- gateway: child tenant must not have its own gateway HelmRelease
- kuberture: external-dns public/internal hostname isolation
- e2e-install: tenant-name validation must reject (not warn); the version
  ConfigMap must be gone after delete
- build-matrix: talos/installer excluded from the parallel matrix
- capture-dataplane: kube-dns and pending rows are filtered out

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
@lexfrei
Aleksei Sviridkin (lexfrei) force-pushed the fix/cozytest-negative-assertions branch from 0123b20 to 12fe325 Compare June 30, 2026 16:53
@lexfrei
Aleksei Sviridkin (lexfrei) merged commit 20e4eab into main Jun 30, 2026
15 checks passed
@lexfrei
Aleksei Sviridkin (lexfrei) deleted the fix/cozytest-negative-assertions branch June 30, 2026 18:27
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) area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review kind/bug Categorizes issue or PR as related to a bug size/M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants