#!/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