From 3030b83dbd0a0016006731f615cbcc1725aab72b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20M=C3=A9ndez?= Date: Mon, 28 Sep 2026 08:08:44 +0000 Subject: [PATCH] test: a negated command only asserts in final position `! cmd` on its own line asserts nothing in a bats body unless it happens to be the last command of that body: bash does not apply errexit to a negated command, so the verdict is the exit status of the final command and every earlier `! cmd` is inert. shellcheck names the class SC2314 and grades the two cases apart, error for the inert ones and note for the rest. Ten negations here were inert, the largest group in one file in the organization: - "is_global_ipv6 refuses link local, loopback, multicast and IPv4": five of the six cases, so only the empty string ran. This is the predicate that picks the address the boot test then talks to, and `fe80::1`, `::1`, `ff02::1` and an IPv4 address were all unchecked. - "container_name and the name predicates": a leading dot and an empty string, the two LXC refuses. - "is_ssh_banner accepts an OpenSSH banner and nothing else": `220 ready`, which is the answer a mail server gives, so the test that says only SSH counts did not say it. - "firstboot_done_in reads the flag 98finalize clears": the case where the flag is still `true`, meaning the whole point of the predicate. - "password_in_config compares the declared password with the one in the file": the wrong password. The test read as though a mismatch was proven and only the empty file was. All of them become `run !`, together with the seven that were in final position and did assert, because a line whose meaning depends on its position is the trap itself. No assertion changed its verdict; every predicate answers as the test believed. Suite green, 196 tests. Coverage unchanged, threshold 97: zz-project-packages 100.00, zzz-keel-archive 100.00, wordpress.sh 99.00, 40wordpress 97.73, boot-test-lib.sh 98.95, keel-archive-check 100.00. --- tests/boot-test.bats | 34 ++++++++++++++++++---------------- tests/hook.bats | 4 +++- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/tests/boot-test.bats b/tests/boot-test.bats index 8631e19..8dd37f8 100644 --- a/tests/boot-test.bats +++ b/tests/boot-test.bats @@ -4,6 +4,8 @@ # and secret paths, and every verdict this appliance adds. lxc-info is a stub # first in PATH; the clock and sleep are functions. No root, no network, no LXC. +bats_require_minimum_version 1.5.0 + setup() { LIB="$BATS_TEST_DIRNAME/lib/boot-test-lib.sh" S="$BATS_TEST_TMPDIR" @@ -97,10 +99,10 @@ setup() { [ "$(bt_container_name wordpress)" = keel-wordpress-boot-test ] bt_is_container_name demo.one bt_is_container_name 9lives - ! bt_is_container_name ".hidden" - ! bt_is_container_name "" + run ! bt_is_container_name ".hidden" + run ! bt_is_container_name "" bt_is_appliance_name wordpress - ! bt_is_appliance_name 9wordpress + run ! bt_is_appliance_name 9wordpress } # --- addresses --------------------------------------------------------------- @@ -112,12 +114,12 @@ setup() { } @test "is_global_ipv6 refuses link local, loopback, multicast and IPv4" { - ! bt_is_global_ipv6 fe80::1 - ! bt_is_global_ipv6 FE80::1 - ! bt_is_global_ipv6 ::1 - ! bt_is_global_ipv6 ff02::1 - ! bt_is_global_ipv6 10.0.3.1 - ! bt_is_global_ipv6 "" + run ! bt_is_global_ipv6 fe80::1 + run ! bt_is_global_ipv6 FE80::1 + run ! bt_is_global_ipv6 ::1 + run ! bt_is_global_ipv6 ff02::1 + run ! bt_is_global_ipv6 10.0.3.1 + run ! bt_is_global_ipv6 "" } @test "global_ipv6 takes the first global address from lxc-info output" { @@ -157,7 +159,7 @@ EOF @test "deadline_passed compares the elapsed time with the timeout" { bt_deadline_passed 100 10 110 bt_deadline_passed 100 10 115 - ! bt_deadline_passed 100 10 105 + run ! bt_deadline_passed 100 10 105 } @test "wait_for returns as soon as the command succeeds" { @@ -193,16 +195,16 @@ EOF @test "is_ssh_banner accepts an OpenSSH banner and nothing else" { bt_is_ssh_banner "SSH-2.0-OpenSSH_10.0p2 Debian-8" - ! bt_is_ssh_banner "220 ready" - ! bt_is_ssh_banner "" + run ! bt_is_ssh_banner "220 ready" + run ! bt_is_ssh_banner "" } @test "firstboot_done_in reads the flag 98finalize clears" { printf 'RUN_FIRSTBOOT=true\n' > "$S/defaults" - ! bt_firstboot_done_in "$S/defaults" + run ! bt_firstboot_done_in "$S/defaults" printf 'RUN_FIRSTBOOT=false\n' > "$S/defaults" bt_firstboot_done_in "$S/defaults" - ! bt_firstboot_done_in "$S/nothing-here" + run ! bt_firstboot_done_in "$S/nothing-here" } # --- the container config and the container marks ---------------------------- @@ -404,9 +406,9 @@ EOF @test "password_in_config compares the declared password with the one in the file" { _config wordpress wordpress 'S3cret-value' bt_password_in_config "$S/wp-config.php" 'S3cret-value' - ! bt_password_in_config "$S/wp-config.php" 'something-else' + run ! bt_password_in_config "$S/wp-config.php" 'something-else' printf " "$S/wp-config.php" - ! bt_password_in_config "$S/wp-config.php" 'S3cret-value' + run ! bt_password_in_config "$S/wp-config.php" 'S3cret-value' } # --- the login verdicts ----------------------------------------------------- diff --git a/tests/hook.bats b/tests/hook.bats index 4ccb600..df93f91 100644 --- a/tests/hook.bats +++ b/tests/hook.bats @@ -8,6 +8,8 @@ # library, so kcov measures the file the appliance ships. No test needs root, a # database, a web server or a network. +bats_require_minimum_version 1.5.0 + setup() { REPO="$BATS_TEST_DIRNAME/.." HOOK="$REPO/overlay/usr/lib/inithooks/firstboot.d/40wordpress" @@ -144,7 +146,7 @@ EOF grep -q "define('DB_NAME', 'wordpress');" "$WPROOT/wp-config.php" grep -q "define('DB_USER', 'wordpress');" "$WPROOT/wp-config.php" grep -q "define('DB_HOST', '\[::1\]');" "$WPROOT/wp-config.php" - ! grep -q "turnkey" "$WPROOT/wp-config.php" + run ! grep -q "turnkey" "$WPROOT/wp-config.php" } @test "the password never reaches the command line of wp core install" {