mirror of
https://github.com/cozystack/cozystack.git
synced 2026-10-26 13:07:04 +09:00
The ban on EXIT-trap cleanup in hack/*.bats was enforced by two lists inside hack/cozyreport.bats: one naming the files known to be clean, one freezing an exact trap count for each file that was not. Both had to be edited from whatever change moved them, and that is where the guard kept failing. A change adding a trap to its own file had to edit a string in a suite it otherwise never touches, so two changes sharing no line still invalidated each other: each stayed green against its own base, git merged both cleanly, and the guard went red only once the second landed. A file arriving with traps hit the same wall from the other side -- the inventory did notice it, since the string it compared was built by scanning the directory, but absorbing it meant an edit in a file nothing in the author's diff pointed at. Replace both lists with a declaration each file makes about itself: one "# EXIT-TRAP DEBT: N" comment, exact rather than a ceiling. A file carrying none must install no EXIT trap, which covers the converted files, the files that never had one, and every file added later. Growing or shedding a trap now fails in the file the change already edits, so two changes that disagree about a count collide textually instead of silently. The declaration is read from the leading comment block, not from anywhere in the file. A .bats file is shell that writes shell, so the same line turns up inside a heredoc, a fixture writer or an expected-output string, where it is data belonging to one test. Reading it there as a statement about the whole file would let an unrelated fixture excuse a real trap, and would do it silently, since nothing in that test's own diff looks like a declaration. A comment block is the region with no interior: stopping instead at the first @test would still read a line out of a helper's heredoc. Not every counted handler is debt. A trap inside an explicit subshell does not replace the bats binary's own, so a test failing inside `( ... )` still prints its `not ok`. hack/e2e-test-openapi.bats kills a backgrounded kubectl proxy that way, and moving the kill to the end of the body would leak a process holding a fixed port. Its declaration records that rather than scheduling a conversion, and because the ratchet is exact, removing the trap fails too -- the count protects the construct. What the count cannot do is tell the two apart: substituting a test-level trap for the subshell one keeps the total at 1 and stays green, which the header states rather than leaves to be found. Counting bounds the keyword and the signal the same way, at any character that cannot be part of an identifier, and matches the signal in either case. Whitespace on the right missed `trap ... EXIT; cd "$tmp"`; whitespace on the left missed `tmp=$(mktemp -d);trap ... EXIT` and `(trap ... EXIT; true)`; upper case missed `trap ... exit`, which bash and dash both install. All are real handlers that scored zero, and the left boundary matters most, since the inventory being replaced had none and caught the semicolon form. A bare word boundary is not enough either way: `bootstrap ` ends in `trap `, and it must keep scoring nothing. A quoted signal counts for the same reason as the rest. Two handlers sharing one line are reported rather than counted, since the count is a count of lines and the second would otherwise arrive without moving the total; splitting the line properly needs a shell parser, a semicolon inside a handler's own action not being a separator, and guessing wrong undercounts -- the one direction a ratchet cannot afford. The scan recurses, so hack/e2e-apps/*.bats is covered rather than sitting one directory below the guard that claims the tree. It reads .bats and nothing else, so a handler arriving through a sourced .sh stays outside it -- hack/e2e-chainsaw/_lib/run-kubernetes.sh installs two, each benign for its own reason rather than by design: one sits in a function declared with `(` and so runs in a subshell, the other in a brace function no @test calls. That boundary is stated in the header rather than papered over. The guard moves to hack/bats-no-exit-trap.bats: its subject is every unit suite under hack/, not the report collector it grew up in. Its fixture helpers assemble the trap keyword and the signal from separate arguments, because the guard scans its own source and a fixture written as a literal would be counted as a real trap in it. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
67 lines
2.7 KiB
Bash
67 lines
2.7 KiB
Bash
#!/usr/bin/env bats
|
|
# EXIT-TRAP DEBT: 1 -- see hack/bats-no-exit-trap.bats.
|
|
# This one is NOT debt and must not be converted. The trap sits inside an
|
|
# explicit subshell and kills a backgrounded `kubectl proxy`; a subshell trap
|
|
# does not replace the one the bats binary installs, so a failure there still
|
|
# prints its `not ok`. Moving the kill to the end of the body would leak the
|
|
# proxy on failure -- it holds a fixed port, so the next run wedges. The count
|
|
# is declared anyway because the ratchet is exact in both directions: it fails
|
|
# if a real test-level trap is added here, and it fails if this one is removed.
|
|
# -----------------------------------------------------------------------------
|
|
# Test OpenAPI endpoints in a Kubernetes cluster
|
|
# -----------------------------------------------------------------------------
|
|
|
|
@test "Test OpenAPI v2 endpoint" {
|
|
kubectl get -v7 --raw '/openapi/v2?timeout=32s' > /dev/null
|
|
}
|
|
|
|
@test "Test OpenAPI v3 endpoint" {
|
|
kubectl get -v7 --raw '/openapi/v3/apis/apps.cozystack.io/v1alpha1' > /dev/null
|
|
kubectl get -v7 --raw '/openapi/v3/apis/core.cozystack.io/v1alpha1' > /dev/null
|
|
}
|
|
|
|
@test "Test OpenAPI v2 endpoint (protobuf)" {
|
|
(
|
|
kubectl proxy --port=21234 &
|
|
proxy_pid=$!
|
|
trap "kill $proxy_pid" EXIT
|
|
# Wait for the proxy to actually be listening rather than guessing with
|
|
# a fixed sleep. nc -z is non-destructive and exits 0 the moment the
|
|
# listener accepts a connection.
|
|
timeout 10 sh -ec 'until nc -z localhost 21234; do sleep 0.1; done'
|
|
curl -sS --fail 'http://localhost:21234/openapi/v2?timeout=32s' -H 'Accept: application/com.github.proto-openapi.spec.v2@v1.0+protobuf' > /dev/null
|
|
)
|
|
}
|
|
|
|
@test "Test kinds" {
|
|
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/tenants | jq -r '.kind')
|
|
if [ "$val" != "TenantList" ]; then
|
|
echo "Expected kind to be TenantList, got $val"
|
|
exit 1
|
|
fi
|
|
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/tenants | jq -r '.items[0].kind')
|
|
if [ "$val" != "Tenant" ]; then
|
|
echo "Expected kind to be Tenant, got $val"
|
|
exit 1
|
|
fi
|
|
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/ingresses | jq -r '.kind')
|
|
if [ "$val" != "IngressList" ]; then
|
|
echo "Expected kind to be IngressList, got $val"
|
|
exit 1
|
|
fi
|
|
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/ingresses | jq -r '.items[0].kind')
|
|
if [ "$val" != "Ingress" ]; then
|
|
echo "Expected kind to be Ingress, got $val"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
@test "Create and delete namespace" {
|
|
kubectl create ns cozy-test-create-and-delete-namespace --dry-run=client -o yaml | kubectl apply -f -
|
|
if ! kubectl delete ns cozy-test-create-and-delete-namespace; then
|
|
echo "Failed to delete namespace"
|
|
kubectl describe ns cozy-test-create-and-delete-namespace
|
|
exit 1
|
|
fi
|
|
}
|