Files
2026-10-06 13:13:29 +00:00

106 lines
4.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Tests for scripts/is-template.sh
#
# This guard decides whether a checkout is still a raw template. Every job in
# cd.yml keys off it, so when it answers wrongly the entire pipeline skips and
# reports green — which is how `cad-atlas` and `openbanking-api` were born with
# CI that had never built, tested or deployed them (#9, #10).
#
# The defect both times was the same: the guard grepped for a literal
# placeholder, and the scaffolder substitutes placeholders in EVERY file,
# including the guard itself. So in a generated repo the check became
# `grep -q 'git.d-ma.be/mathias/<name>' go.mod` against a go.mod that reads
# `module git.d-ma.be/mathias/<name>` — a match that can never fail.
#
# The third test below is the one that matters: it runs a scaffolder-style
# substitution OVER THE GUARD and asserts it still answers correctly. Both
# shipped bugs would have failed it.
#
# Usage: ./scripts/tests/is-template_test.sh
set -uo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
SCRIPT="$ROOT/scripts/is-template.sh"
pass=0; fail=0
ok() { printf ' ok %s\n' "$1"; pass=$((pass+1)); }
bad() { printf ' FAIL %s\n %s\n' "$1" "${2:-}"; fail=$((fail+1)); }
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT
echo "is-template tests"
# Placeholder tokens are ASSEMBLED, never written whole. The scaffolder rewrites
# every file in the tree, this one included -- a fixture containing a literal
# token would be substituted before the test ran, and the suite would fail in
# every generated repo while passing here. Which is the same class of bug the
# guard itself had.
U="__"
ph() { printf '%s%s%s' "$U" "$1" "$U"; }
MODULE_PH="$(ph MODULE_PATH)"
PROJECT_PH="$(ph PROJECT_NAME)"
OTHER_PH="$(ph SOME_OTHER)"
if [ -f "$SCRIPT" ]; then ok "script exists"
else bad "script exists" "$SCRIPT missing"; echo; echo "$pass passed, $fail failed"; exit 1; fi
# A raw template still carries placeholders.
printf 'module git.d-ma.be/mathias/%s\n\ngo 1.26\n' "$MODULE_PH" > "$tmp/raw.go.mod"
got="$(bash "$SCRIPT" "$tmp/raw.go.mod")"
[ "$got" = "true" ] && ok "raw template is detected" \
|| bad "raw template is detected" "got '$got', want 'true'"
# A generated repo does not. This is the assertion both shipped guards failed.
printf 'module git.d-ma.be/mathias/openbanking-api\n\ngo 1.26\n' > "$tmp/real.go.mod"
got="$(bash "$SCRIPT" "$tmp/real.go.mod")"
[ "$got" = "false" ] && ok "a generated repo is NOT detected as a template" \
|| bad "a generated repo is NOT detected as a template" "got '$got', want 'false'"
# THE ONE THAT MATTERS.
#
# Copy the guard and run a scaffolder-style substitution over it, exactly as
# create_project_from_template and new-project.sh do to every file in the tree.
# The substituted guard must still answer correctly. A guard that greps a whole
# placeholder token gets rewritten here and starts matching forever.
cp "$SCRIPT" "$tmp/substituted.sh"
sed -i \
-e "s|$MODULE_PH|git.d-ma.be/mathias/openbanking-api|g" \
-e "s|$PROJECT_PH|openbanking-api|g" \
"$tmp/substituted.sh"
got="$(bash "$tmp/substituted.sh" "$tmp/real.go.mod")"
if [ "$got" = "false" ]; then
ok "the guard survives being substituted itself"
else
bad "the guard survives being substituted itself" \
"after substitution it answered '$got' for a generated repo — this is #9/#10"
fi
got="$(bash "$tmp/substituted.sh" "$tmp/raw.go.mod")"
[ "$got" = "true" ] && ok "the substituted guard still detects a raw template" \
|| bad "the substituted guard still detects a raw template" "got '$got'"
# Belt and braces: the source must not contain a complete substitutable token.
if grep -qE "$(printf '%s(MODULE_PATH|PROJECT_NAME)%s' "$U" "$U")" "$SCRIPT"; then
bad "the guard contains no substitutable placeholder token" \
"found one in $SCRIPT — the scaffolder will rewrite it"
else
ok "the guard contains no substitutable placeholder token"
fi
# Any leftover placeholder counts, not just the two the scaffolder knows about —
# a half-substituted tree is not a working repo either.
printf 'module git.d-ma.be/mathias/thing\n\n// %s\ngo 1.26\n' "$OTHER_PH" > "$tmp/partial.go.mod"
got="$(bash "$SCRIPT" "$tmp/partial.go.mod")"
[ "$got" = "true" ] && ok "any leftover placeholder counts" \
|| bad "any leftover placeholder counts" "got '$got', want 'true'"
# A missing file is not "this is a real repo" — failing open here would skip CI.
bash "$SCRIPT" "$tmp/nope.go.mod" >/dev/null 2>&1
rc=$?
[ "$rc" -ne 0 ] && ok "a missing target fails loudly rather than answering false" \
|| bad "a missing target fails loudly rather than answering false" "exit 0"
echo
echo "$pass passed, $fail failed"
[ "$fail" -eq 0 ]