syntaxai/tdd.md · main · tools / sama-cli / src / b32_utils.test.sh

b32_utils.test.sh 143 lines · 4960 bytes raw
#!/usr/bin/env bash
# Sibling test for b32_utils.sh. Asserts the pure helpers behave
# as documented: profile parsing populates the PROFILE_* arrays,
# the file classifiers split on .test extension correctly, and
# the line counter matches the TS "split('\n').length" semantics.
#
# A minimal in-file harness (no bats dependency): each test calls
# `assert_eq` which prints PASS/FAIL and increments counters.
# Exit 0 if all assertions pass, 1 otherwise. Run from any cwd.

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"

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() {
  local haystack="$1" needle="$2" label="$3"
  TESTS_RUN=$((TESTS_RUN + 1))
  case "$haystack" in
    *"$needle"*) printf "  ok  %s\n" "$label" ;;
    *)
      TESTS_FAILED=$((TESTS_FAILED + 1))
      printf "  FAIL %s\n    haystack: %s\n    needle:   %s\n" "$label" "$haystack" "$needle"
      ;;
  esac
}

mkfile() {
  local content="$1" path="$2"
  printf "%s" "$content" > "$path"
}

# Run tests in a temp dir so we don't touch the real repo profile.
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT

# — parse_profile -------------------------------------------------
PROFILE_PATH="$TMP_DIR/test.profile.toml"
cat > "$PROFILE_PATH" <<'EOF'
sama_version = "2.0"
profile = "test-profile"
extension = ".sh"

[layers.0]
prefixes = ["a_"]

[layers.1]
sublayers = [
  { name = "core", prefix = "b_" },
]

[layers.2]
prefixes = ["c_"]

[layers.3]
prefixes = ["d_"]
EOF

parse_profile "$PROFILE_PATH"
assert_eq "test-profile" "$PROFILE_NAME" "parse_profile: PROFILE_NAME"
assert_eq "2.0" "$PROFILE_SAMA_VERSION" "parse_profile: PROFILE_SAMA_VERSION"
assert_eq ".sh" "$PROFILE_EXTENSION" "parse_profile: PROFILE_EXTENSION"
assert_eq "4" "${#PROFILE_PREFIXES[@]}" "parse_profile: 4 prefixes"
assert_eq "a_" "${PROFILE_PREFIXES[0]}" "parse_profile: layer-0 prefix"
assert_eq "b_" "${PROFILE_PREFIXES[1]}" "parse_profile: layer-1 prefix from sublayer"
assert_eq "core" "${PROFILE_SUBLAYER_NAMES[1]}" "parse_profile: layer-1 sublayer name"

# — file classifiers ----------------------------------------------
PROFILE_EXTENSION=".ts"
is_sama_file "src/a31_foo.ts" && r=0 || r=1
assert_eq "0" "$r" "is_sama_file: src/a31_foo.ts"
is_sama_file "src/a31_foo.test.ts" && r=0 || r=1
assert_eq "1" "$r" "is_sama_file rejects .test.ts"
is_test_file "src/a31_foo.test.ts" && r=0 || r=1
assert_eq "0" "$r" "is_test_file: .test.ts"
is_test_file "src/a31_foo.ts" && r=0 || r=1
assert_eq "1" "$r" "is_test_file rejects plain .ts"

# — declared_layer ------------------------------------------------
PROFILE_LAYERS=("0" "1" "1" "2" "3")
PROFILE_PREFIXES=("a31_" "b32_" "b51_" "c14_" "d21_")
PROFILE_SUBLAYER_NAMES=("default" "logic" "render" "io" "handlers")
PROFILE_SUBLAYER_INDEXES=("0" "0" "1" "0" "0")
result="$(declared_layer "src/b51_render_admin.ts")"
assert_eq "1 render 1 b51_" "$result" "declared_layer: b51_ → Layer 1 render"
result="$(declared_layer "src/unprefixed.ts")"
assert_eq "" "$result" "declared_layer: unprefixed returns empty"

# — count_lines ---------------------------------------------------
mkfile "a" "$TMP_DIR/one_no_nl.txt"
assert_eq "1" "$(count_lines "$TMP_DIR/one_no_nl.txt")" "count_lines: 'a' = 1"

mkfile "a\nb\n" "$TMP_DIR/two_with_nl.txt"
# printf %s doesn't interpret \n; use printf for real newlines
printf "a\nb\n" > "$TMP_DIR/two_with_nl.txt"
assert_eq "3" "$(count_lines "$TMP_DIR/two_with_nl.txt")" "count_lines: 'a\\nb\\n' = 3 (matches TS split)"

printf "" > "$TMP_DIR/empty.txt"
assert_eq "1" "$(count_lines "$TMP_DIR/empty.txt")" "count_lines: empty = 1"

# — strip_strings_and_comments -----------------------------------
PROFILE_EXTENSION=".ts"
mkfile "const x = \"hello\"; // a comment" "$TMP_DIR/strip.ts"
stripped="$(strip_strings_and_comments_file "$TMP_DIR/strip.ts")"
assert_contains "$stripped" "const x = " "strip preserves code"
# After stripping, "hello" should be whitespace and // ... should be whitespace.
case "$stripped" in
  *"hello"*)
    TESTS_FAILED=$((TESTS_FAILED + 1))
    printf "  FAIL strip removes string content\n    leaked: %s\n" "$stripped"
    ;;
  *)
    TESTS_RUN=$((TESTS_RUN + 1))
    printf "  ok  strip removes string content\n"
    ;;
esac

# — Summary -------------------------------------------------------
echo
if [ "$TESTS_FAILED" -eq 0 ]; then
  printf "b32_utils.test.sh: %d/%d passed ✓\n" "$TESTS_RUN" "$TESTS_RUN"
  exit 0
else
  printf "b32_utils.test.sh: %d/%d passed, %d FAILED ✗\n" \
    "$((TESTS_RUN - TESTS_FAILED))" "$TESTS_RUN" "$TESTS_FAILED"
  exit 1
fi