Files
cozystack/hack/check-opensearch-readme.bats
Myasnikov Daniil cc942fed68 test(tests): run unit suites with Bats and retain POSIX coverage
The shell translator rewrites test bodies and loses Bats reporting.
The unit lane needs complete TAP results and JUnit reports without
changing how live-cluster suites run.

Sourced production helpers still need a dash pass, and unit test bodies
need nounset without exporting shell options into subprocesses.

Assisted-by: LLM
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-10-07 11:04:00 +03:00

118 lines
5.9 KiB
Bash

#!/usr/bin/env bats
# Tests that the opensearch README.md has the correct structure after generation.
#
# These are static checks on the committed README.md — they do not invoke
# make generate (which requires cozyvalues-gen in PATH). They verify that:
# 1. tls.issuer description is not truncated (ends with "on and off")
# 2. topologySpreadPolicy appears outside the TLS section
# 3. opensearch-rd cozyrds labels the tenant CA and no server certificate
load test_helper
REPO_ROOT="$(cd "$(dirname "${BATS_TEST_FILENAME:-$0}")/.." && pwd)"
README="$REPO_ROOT/packages/apps/opensearch/README.md"
COZYRDS="$REPO_ROOT/packages/system/opensearch-rd/cozyrds/opensearch.yaml"
@test "tls.issuer description is not truncated" {
# The last clause of the @field line in values.yaml. cozyvalues-gen copies only
# the first line of a description into the table, so a wrapped @field silently
# loses its tail and the row still renders.
grep -q "does switch TLS on and off" "$README"
}
@test "topologySpreadPolicy is not in TLS configuration section" {
# TLS section should contain only tls/tls.issuer rows, not topology spread.
# The awk pattern skips the heading line itself, then prints until the next
# section heading so the range is non-vacuous.
#
# if/exit rather than a `!` prefix, for the reason spelled out on the
# publish-ca-cert check below: under hack/cozytest.sh a negated pipeline cannot
# fail the test at all.
if awk '/### TLS configuration/{found=1; next} found && /^### /{exit} found' "$README" | grep -q "topologySpreadPolicy"; then
echo "topologySpreadPolicy appears inside the TLS configuration section" >&2
exit 1
fi
}
@test "topologySpreadPolicy appears in README at all" {
# Deliberately only presence. This is what stops the test above passing
# vacuously: a README that lost the row entirely would satisfy "not in the TLS
# section" without documenting the parameter anywhere. The "outside the TLS
# section" half is that test's job, not this one's.
grep -q "topologySpreadPolicy" "$README"
}
@test "cozyrds opensearch.yaml labels the tenant CA for the tenant registry" {
# The key-free trust anchor the CA-extraction controller publishes, selected by
# label rather than by name. This entry is the whole tenant read path: it drives the
# lineage webhook's tenantresource verdict, and the tenant reads the projection
# through core.cozystack.io/tenantsecrets, which the base tenant roles grant. The
# chart Role grants no name for it — tests/dashboard-resourcemap_test.yaml pins that.
grep -q "internal.cozystack.io/tenant-ca" "$COZYRDS"
}
@test "cozyrds opensearch.yaml exposes no server certificate to the tenant" {
# A tenant needs ca.crt to verify the endpoint and nothing more. Both the leaf
# Secret and the cert-manager CA Secret carry a private key under tls.key, so
# neither may be listed here — only the controller's key-free projection.
#
# Asserted on the SHAPE of secrets.include, not by grepping one spelling: this is
# the only thing standing between the CA private key and the tenant, and a name
# written a different way, or a selector that happens to match a key-bearing
# Secret, has to fail this too. Every entry must be one of exactly two known-safe
# forms — the credentials Secret by name, or the tenant-ca projection by label.
command -v yq >/dev/null || { echo "yq (mikefarah v4+) is required" >&2; exit 1; }
credentials='{"resourceNames":["opensearch-{{ .name }}-credentials"]}'
tenant_ca='{"matchLabels":{"internal.cozystack.io/tenant-ca":"true"}}'
entries="$(yq --output-format=json -I=0 '.spec.secrets.include[]' "$COZYRDS")"
# Guard against a vacuous pass. A loop over nothing is a green test, and this one
# guards tenant read access to a Secret holding the HTTP CA private key — so assert
# the list is the size we think it is before concluding anything about its members.
# Without this the test would still report ok if the path were restructured away.
# `|| true` because grep exits non-zero on no match, which under set -e would kill
# the test before the diagnostic below could explain what was expected.
count="$(printf '%s\n' "$entries" | grep -c '^{' || true)"
if [ "$count" -ne 2 ]; then
echo "expected exactly 2 secrets.include entries, found $count" >&2
printf '%s\n' "$entries" >&2
return 1
fi
while IFS= read -r entry; do
[ -n "$entry" ] || continue
if [ "$entry" != "$credentials" ] && [ "$entry" != "$tenant_ca" ]; then
echo "secrets.include has an entry that is neither the credentials Secret" >&2
echo "nor the key-free tenant-ca projection: $entry" >&2
return 1
fi
done <<EOF
$entries
EOF
}
@test "cozyrds opensearch.yaml selects the projection, never a publish source" {
# publish-ca-cert was a label-driven extraction leg that never shipped: no
# controller on main reads it. What ships is the TenantProjection sentinel, which
# names its source Secret outright, and the only label in the mechanism is
# tenant-ca on the projection the controller writes — which is what the selector
# above matches.
#
# The assertion outlives the leg it was written for. A publish-ca-cert selector
# here would be inert rather than dangerous today, but the name marked a
# key-bearing SOURCE — the cert-manager CA Secret, private key under tls.key — so
# reviving it as a selector is the one edit that would hand the tenant that key.
# Cheaper to keep the name out of this file than to re-derive that each time.
#
# NOT written as `! grep -q`. These files run under hack/cozytest.sh, which
# injects `return 0` ahead of the closing brace, and a `!`-prefixed pipeline is
# exempt from set -e — so a bare negation reports green under the runner CI uses
# while real bats fails it. if/exit is the form that survives both.
if grep -q "internal.cozystack.io/publish-ca-cert" "$COZYRDS"; then
echo "cozyrds selects on publish-ca-cert, which marks the key-bearing CA Secret" >&2
exit 1
fi
}