mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-04 02:57:27 +09:00
The layer below moved the unit suite to bats(1), which enforces `set -e` but not `set -u`. cozytest.sh ran every test body under `set -eu -x`, so without this a test that reads an unset variable silently sees an empty string and passes. There is no runner-level way to inject it, which is why this needs a per-file load rather than a Makefile flag. Two mechanisms were measured and rejected: a setup() defined in --setup-suite-file does not reach test files at all, and SHELLOPTS=nounset does reach them but is exported, so it also applies to the hack/*.sh scripts the tests exercise as subprocesses -- changing the behaviour of the code under test rather than the tests, and dropping two tests outright. `set -u` inside setup() is a shell option rather than an exported one, so it stays scoped to the test body. hack/test_helper.bash therefore defines the shared setup(), and all 32 unit files load it. The restoration is mutation-checked, not assumed: a canary test reading an unset variable aborts with "unbound variable" with the load in place and passes vacuously without it. Suite is 328/328 green either way, so nothing in the tree was relying on the laxer behaviour. The headers are the other half. 27 of the 32 files documented the cozytest contract in prose -- "there is no bats `run` or `$status`", "setup()/teardown() are not honored", "Run with: hack/cozytest.sh ...", title-sanitization rules from the awk parser, and instructions to keep `}` off column 0 so the parser would not truncate a test. Every one of those statements is now false or vacuous, and a false comment about the harness is worse than none: the next author reads "setup() is not honored" and hand-rolls per-test cleanup that the runner would have done. Where a constraint merely stopped binding rather than reversing, the comment says so instead of deleting the line -- the column-0 heredoc indentation and the manual status capture in migration-seaweedfs-db-adopt are both kept, now marked belt-and-braces. Claims that survived the move are left alone: `! cmd` really is still vacuous under `set -e`, since bash suppresses errexit for a negated command exactly as dash did. hack/seaweedfs-naming-audit.bats gains a note that its subject is POSIX sh executed by /bin/sh but sourced into bats' bash, so shellcheck's shell=sh directive is now the only thing guarding that gap. Refs: #3453 Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
147 lines
6.2 KiB
Bash
147 lines
6.2 KiB
Bash
#!/usr/bin/env bats
|
|
# -----------------------------------------------------------------------------
|
|
# Unit tests for
|
|
# packages/core/platform/images/migrations/migrations/lib/cozystack-version.sh
|
|
#
|
|
# render_cozystack_version_manifest <version> emits the cozystack-version
|
|
# ConfigMap manifest on stdout, always carrying the
|
|
# platform.cozystack.io/no-delete=true label that the
|
|
# cozystack-no-delete-guardrail ValidatingAdmissionPolicy keys on. Migrations
|
|
# apply it under the default kubectl field manager; a label-less apply by that
|
|
# same manager would strip the label, so the manifest is centralized here and
|
|
# every migration sources it instead of copy-pasting a heredoc.
|
|
#
|
|
# Runs under bats(1). `run` and `$status` are available but unused here:
|
|
# assertions are expressed as direct shell tests that exit non-zero on
|
|
# failure. bats supplies `set -e`, and hack/test_helper.bash restores the
|
|
# `set -u` that cozytest.sh applied before #3453. Each test runs in its own
|
|
# subshell, so NAMESPACE set/unset in one test does not leak into another.
|
|
#
|
|
# Run with: bats hack/cozystack-version-stamp.bats
|
|
# -----------------------------------------------------------------------------
|
|
|
|
load test_helper
|
|
|
|
@test "renders parseable YAML for a version" {
|
|
. packages/core/platform/images/migrations/migrations/lib/cozystack-version.sh
|
|
render_cozystack_version_manifest 45 | yq . >/dev/null
|
|
}
|
|
|
|
@test "carries the no-delete label set to true" {
|
|
. packages/core/platform/images/migrations/migrations/lib/cozystack-version.sh
|
|
val=$(render_cozystack_version_manifest 45 \
|
|
| yq -r '.metadata.labels."platform.cozystack.io/no-delete"')
|
|
[ "$val" = "true" ]
|
|
}
|
|
|
|
@test "names the ConfigMap cozystack-version" {
|
|
. packages/core/platform/images/migrations/migrations/lib/cozystack-version.sh
|
|
name=$(render_cozystack_version_manifest 45 | yq -r '.metadata.name')
|
|
[ "$name" = "cozystack-version" ]
|
|
}
|
|
|
|
@test "stamps the requested version as a quoted string" {
|
|
# The version MUST render as a quoted YAML string; an unquoted numeric
|
|
# scalar (version: 45) would change the ConfigMap data type the rest of
|
|
# the platform reads as a string.
|
|
. packages/core/platform/images/migrations/migrations/lib/cozystack-version.sh
|
|
out=$(render_cozystack_version_manifest 45)
|
|
printf '%s\n' "$out" | grep -qx ' version: "45"'
|
|
v=$(printf '%s\n' "$out" | yq -r '.data.version')
|
|
[ "$v" = "45" ]
|
|
}
|
|
|
|
@test "defaults namespace to cozy-system when NAMESPACE is unset" {
|
|
unset NAMESPACE
|
|
. packages/core/platform/images/migrations/migrations/lib/cozystack-version.sh
|
|
ns=$(render_cozystack_version_manifest 45 | yq -r '.metadata.namespace')
|
|
[ "$ns" = "cozy-system" ]
|
|
}
|
|
|
|
@test "honors a NAMESPACE override" {
|
|
. packages/core/platform/images/migrations/migrations/lib/cozystack-version.sh
|
|
out=$(export NAMESPACE=cozy-other; render_cozystack_version_manifest 45)
|
|
ns=$(printf '%s\n' "$out" | yq -r '.metadata.namespace')
|
|
[ "$ns" = "cozy-other" ]
|
|
}
|
|
|
|
@test "requires a version argument" {
|
|
. packages/core/platform/images/migrations/migrations/lib/cozystack-version.sh
|
|
if ( render_cozystack_version_manifest ) 2>/dev/null; then
|
|
echo "expected non-zero exit when version arg is missing" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
@test "stamp_cozystack_version requires a version argument" {
|
|
# The guard fires before the kubectl pipe, so this needs no cluster: a
|
|
# missing version must abort rather than apply empty input to kubectl.
|
|
. packages/core/platform/images/migrations/migrations/lib/cozystack-version.sh
|
|
if ( stamp_cozystack_version ) 2>/dev/null; then
|
|
echo "expected non-zero exit when version arg is missing" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
@test "render output matches the canonical labeled manifest" {
|
|
# Golden test: pins the exact bytes migrations 42/43/44 apply today, so the
|
|
# refactor that routes them through this helper is provably manifest-stable.
|
|
unset NAMESPACE
|
|
. packages/core/platform/images/migrations/migrations/lib/cozystack-version.sh
|
|
expected=$(cat <<'EOF'
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: cozystack-version
|
|
namespace: cozy-system
|
|
labels:
|
|
platform.cozystack.io/no-delete: "true"
|
|
data:
|
|
version: "45"
|
|
EOF
|
|
)
|
|
actual=$(render_cozystack_version_manifest 45)
|
|
if [ "$actual" != "$expected" ]; then
|
|
printf 'expected:\n%s\n---\nactual:\n%s\n' "$expected" "$actual" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
@test "no go-forward stamp bypasses the helper (migrations >= 42 and run-migrations.sh)" {
|
|
# The helper exists so go-forward stamps cannot drift back to a label-less
|
|
# apply that drops the no-delete label. Migrations 1-41 are frozen and
|
|
# backfilled by 42, so they are exempt; migration 42 onward and the
|
|
# bootstrap path must each source the helper, call its API, and never stamp
|
|
# cozystack-version directly with kubectl. Blocking only the historical
|
|
# 'kubectl create configmap' shape is too weak — an inline 'kubectl apply'
|
|
# of a hand-rolled label-less manifest would slip past it.
|
|
mig_dir=packages/core/platform/images/migrations/migrations
|
|
files=""
|
|
for f in "$mig_dir"/*; do
|
|
n=$(basename "$f")
|
|
case "$n" in *[!0-9]*) continue ;; esac # skip lib/ and other non-numeric
|
|
[ "$n" -ge 42 ] || continue
|
|
files="$files $f"
|
|
done
|
|
files="$files packages/core/platform/images/migrations/run-migrations.sh"
|
|
|
|
for f in $files; do
|
|
if ! grep -Eq 'cozystack-version\.sh' "$f"; then
|
|
echo "$f does not source the cozystack-version helper" >&2
|
|
exit 1
|
|
fi
|
|
if ! grep -Eq '(render_cozystack_version_manifest|stamp_cozystack_version)' "$f"; then
|
|
echo "$f does not call the cozystack-version helper API" >&2
|
|
exit 1
|
|
fi
|
|
# Strip full-comment lines first: a comment that explains why a stamp
|
|
# routes through the helper must not read as a direct stamp itself. A
|
|
# real bypass command never starts with '#', so the guard keeps its
|
|
# strength against actual direct stamping.
|
|
if grep -v '^[[:space:]]*#' "$f" | grep -Eq 'kubectl[[:space:]].*(create[[:space:]]+configmap|apply).*cozystack-version'; then
|
|
echo "$f stamps cozystack-version directly instead of via the helper" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
}
|