syntaxai/tdd.md · main · tools / sama-cli / src / b32_checks.test.sh
#!/usr/bin/env bash
# Sibling test for b32_checks.sh. Builds a fixture tree under a
# temp dir, runs each of the seven §4 checks against it, and
# asserts the expected verdict. Cross-validates the checks against
# the same kind of "synthetic small repo" the TS verifier tests in
# src/b32_sama_v2_verify.test.ts — same shapes, different language.
SAMA_SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
# shellcheck disable=SC1091
. "$SAMA_SRC_DIR/a31_constants.sh"
# shellcheck disable=SC1091
. "$SAMA_SRC_DIR/b32_utils.sh"
# shellcheck disable=SC1091
. "$SAMA_SRC_DIR/b32_checks.sh"
TESTS_RUN=0
TESTS_FAILED=0
assert_eq() {
local expected="$1" actual="$2" label="$3"
TESTS_RUN=$((TESTS_RUN + 1))
if [ "$expected" = "$actual" ]; then
printf " ok %s\n" "$label"
else
TESTS_FAILED=$((TESTS_FAILED + 1))
printf " FAIL %s\n expected: %s\n actual: %s\n" "$label" "$expected" "$actual"
fi
}
assert_contains_any() {
local label="$1"
shift
local needle="$1"
shift
local found=0
for item in "$@"; do
case "$item" in
*"$needle"*) found=1; break ;;
esac
done
TESTS_RUN=$((TESTS_RUN + 1))
if [ "$found" = "1" ]; then
printf " ok %s\n" "$label"
else
TESTS_FAILED=$((TESTS_FAILED + 1))
printf " FAIL %s — needle \`%s\` not in any violation\n" "$label" "$needle"
fi
}
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
setup_clean_repo() {
rm -rf "$TMP_DIR/repo"
mkdir -p "$TMP_DIR/repo/src"
cat > "$TMP_DIR/repo/sama.profile.toml" <<'EOF'
sama_version = "2.0"
profile = "fixture"
extension = ".ts"
[layers.0]
prefixes = ["a_"]
[layers.1]
prefixes = ["b_"]
[layers.2]
prefixes = ["c_"]
[layers.3]
prefixes = ["d_"]
EOF
}
# — Check 1: Sorted (passing case) -------------------------------
setup_clean_repo
printf "export const x = 1;\n" > "$TMP_DIR/repo/src/a_pure.ts"
parse_profile "$TMP_DIR/repo/sama.profile.toml"
collect_input "$TMP_DIR/repo" "$TMP_DIR/repo/src"
run_check_sorted
assert_eq "0" "${#CHECK_VIOLATIONS[@]}" "Sorted: clean fixture has no violations"
# — Check 2: Architecture (unprefixed file fails) -----------------
setup_clean_repo
printf "export const x = 1;\n" > "$TMP_DIR/repo/src/orphan.ts"
parse_profile "$TMP_DIR/repo/sama.profile.toml"
collect_input "$TMP_DIR/repo" "$TMP_DIR/repo/src"
run_check_architecture
v_count="${#CHECK_VIOLATIONS[@]}"
assert_eq "1" "$v_count" "Architecture: unprefixed file flagged"
assert_contains_any "Architecture: detail mentions unprefixed" "unprefixed" "${CHECK_VIOLATIONS[@]}"
# — Check 3: Modeled (tests) (b_ file without sibling test) ------
setup_clean_repo
printf "export const f = 1;\n" > "$TMP_DIR/repo/src/b_core.ts"
parse_profile "$TMP_DIR/repo/sama.profile.toml"
collect_input "$TMP_DIR/repo" "$TMP_DIR/repo/src"
run_check_modeled_tests
assert_eq "1" "${#CHECK_VIOLATIONS[@]}" "Modeled (tests): b_ file without sibling test fails"
# Pass when sibling test exists.
setup_clean_repo
printf "export const f = 1;\n" > "$TMP_DIR/repo/src/b_core.ts"
printf "// test\n" > "$TMP_DIR/repo/src/b_core.test.ts"
parse_profile "$TMP_DIR/repo/sama.profile.toml"
collect_input "$TMP_DIR/repo" "$TMP_DIR/repo/src"
run_check_modeled_tests
assert_eq "0" "${#CHECK_VIOLATIONS[@]}" "Modeled (tests): sibling test satisfies"
# — Check 4: Modeled (boundary) (JSON.parse in Layer 1 fails) ----
setup_clean_repo
printf "export const f = (s) => JSON.parse(s);\n" > "$TMP_DIR/repo/src/b_naughty.ts"
parse_profile "$TMP_DIR/repo/sama.profile.toml"
collect_input "$TMP_DIR/repo" "$TMP_DIR/repo/src"
run_check_modeled_boundary
v_count="${#CHECK_VIOLATIONS[@]}"
assert_eq "1" "$v_count" "Modeled (boundary): JSON.parse in Layer 1 flagged"
# Layer 2 is the legitimate site — same content should not fire.
setup_clean_repo
printf "export const f = (s) => JSON.parse(s);\n" > "$TMP_DIR/repo/src/c_adapter.ts"
parse_profile "$TMP_DIR/repo/sama.profile.toml"
collect_input "$TMP_DIR/repo" "$TMP_DIR/repo/src"
run_check_modeled_boundary
assert_eq "0" "${#CHECK_VIOLATIONS[@]}" "Modeled (boundary): JSON.parse in Layer 2 is OK"
# String literal containing JSON.parse should NOT false-positive.
setup_clean_repo
printf 'const x = "JSON.parse(input)";\nexport const y = x.length;\n' > "$TMP_DIR/repo/src/b_safe.ts"
parse_profile "$TMP_DIR/repo/sama.profile.toml"
collect_input "$TMP_DIR/repo" "$TMP_DIR/repo/src"
run_check_modeled_boundary
assert_eq "0" "${#CHECK_VIOLATIONS[@]}" "Modeled (boundary): JSON.parse inside string literal ignored"
# — Check 5: Atomic (oversized file fails) -----------------------
setup_clean_repo
# Make a file with 701 newlines.
yes "x" 2>/dev/null | head -n 701 > "$TMP_DIR/repo/src/b_huge.ts"
parse_profile "$TMP_DIR/repo/sama.profile.toml"
collect_input "$TMP_DIR/repo" "$TMP_DIR/repo/src"
run_check_atomic
v_count="${#CHECK_VIOLATIONS[@]}"
assert_eq "1" "$v_count" "Atomic: 701-line file flagged"
# — Check 6: Law (§1.2) — upward import fails --------------------
setup_clean_repo
printf 'import { x } from "./d_entry.ts";\nexport const y = x;\n' > "$TMP_DIR/repo/src/b_bad.ts"
printf 'export const x = 1;\n' > "$TMP_DIR/repo/src/d_entry.ts"
parse_profile "$TMP_DIR/repo/sama.profile.toml"
collect_input "$TMP_DIR/repo" "$TMP_DIR/repo/src"
run_check_law
v_count="${#CHECK_VIOLATIONS[@]}"
# Should be ≥ 1: the upward edge from b_bad → d_entry.
if [ "$v_count" -ge 1 ]; then
TESTS_RUN=$((TESTS_RUN + 1))
printf " ok Law: upward import flagged (%d violation(s))\n" "$v_count"
else
TESTS_RUN=$((TESTS_RUN + 1))
TESTS_FAILED=$((TESTS_FAILED + 1))
printf " FAIL Law: upward import not flagged\n"
fi
# Downward import is OK.
setup_clean_repo
printf 'import { x } from "./a_pure.ts";\nexport const y = x;\n' > "$TMP_DIR/repo/src/b_good.ts"
printf 'export const x = 1;\n' > "$TMP_DIR/repo/src/a_pure.ts"
parse_profile "$TMP_DIR/repo/sama.profile.toml"
collect_input "$TMP_DIR/repo" "$TMP_DIR/repo/src"
run_check_law
assert_eq "0" "${#CHECK_VIOLATIONS[@]}" "Law: downward import passes"
# — Check 7: Consistency (declared layer < actual import ceiling) ----
setup_clean_repo
printf 'import { x } from "./d_entry.ts";\nexport const y = x;\n' > "$TMP_DIR/repo/src/b_lies.ts"
printf 'export const x = 1;\n' > "$TMP_DIR/repo/src/d_entry.ts"
parse_profile "$TMP_DIR/repo/sama.profile.toml"
collect_input "$TMP_DIR/repo" "$TMP_DIR/repo/src"
run_check_consistency
v_count="${#CHECK_VIOLATIONS[@]}"
if [ "$v_count" -ge 1 ]; then
TESTS_RUN=$((TESTS_RUN + 1))
printf " ok Consistency: layer-lie flagged\n"
else
TESTS_RUN=$((TESTS_RUN + 1))
TESTS_FAILED=$((TESTS_FAILED + 1))
printf " FAIL Consistency: layer-lie not flagged\n"
fi
# — Summary -------------------------------------------------------
echo
if [ "$TESTS_FAILED" -eq 0 ]; then
printf "b32_checks.test.sh: %d/%d passed ✓\n" "$TESTS_RUN" "$TESTS_RUN"
exit 0
else
printf "b32_checks.test.sh: %d/%d passed, %d FAILED ✗\n" \
"$((TESTS_RUN - TESTS_FAILED))" "$TESTS_RUN" "$TESTS_FAILED"
exit 1
fi