run.sh 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. #!/usr/bin/env bash
  2. # Offline self-test for the tailwind-ops skill — structure, frontmatter, and the
  3. # staleness-verifier contract (SKILL-RESOURCE-PROTOCOL.md §7, §10).
  4. #
  5. # Offline-deterministic (no network, no Tailwind install). Resolves paths
  6. # relative to itself so it works in the repo and once installed to ~/.claude/.
  7. #
  8. # Usage: bash tests/run.sh
  9. # Input: none (self-contained; no network)
  10. # Output: TAP-ish progress on stderr; final PASS/FAIL line.
  11. # Exit: 0 all pass, 1 any failure
  12. set -uo pipefail
  13. HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  14. SKILL="$(dirname "$HERE")"
  15. DOC="$SKILL/SKILL.md"
  16. PASS=0; FAIL=0
  17. ok() { PASS=$((PASS+1)); printf ' PASS %s\n' "$1" >&2; }
  18. no() { FAIL=$((FAIL+1)); printf ' FAIL %s\n' "$1" >&2; }
  19. # Resolve a *working* python (python3, else python). The bare `command -v` is
  20. # not enough on Windows, where `python3` is a Microsoft Store stub that exits
  21. # nonzero. Skip the whole verifier block if none works.
  22. PY=""
  23. for c in python3 python py; do
  24. if command -v "$c" >/dev/null 2>&1 && "$c" -c "" >/dev/null 2>&1; then PY="$c"; break; fi
  25. done
  26. echo "=== tailwind-ops self-test ===" >&2
  27. # ── SKILL.md frontmatter ───────────────────────────────────────────────────
  28. [[ -f "$DOC" ]] && ok "SKILL.md present" || { no "SKILL.md missing"; echo "=== $PASS passed, $FAIL failed ===" >&2; exit 1; }
  29. doc="$(cat "$DOC")"
  30. case "$doc" in *"name: tailwind-ops"*) ok "frontmatter declares name: tailwind-ops";; *) no "frontmatter name != tailwind-ops";; esac
  31. case "$doc" in *"license: MIT"*) ok "frontmatter declares license: MIT";; *) no "missing license: MIT";; esac
  32. case "$doc" in *"as of 20"*) ok "currency note carries a year";; *) no "no dated 'as of <year>' currency note";; esac
  33. # ── resources present + cited ──────────────────────────────────────────────
  34. for res in assets/tailwind-facts.json scripts/check-tailwind-facts.py; do
  35. [[ -f "$SKILL/$res" ]] && ok "resource present: $res" || no "missing resource: $res"
  36. done
  37. case "$doc" in *"scripts/check-tailwind-facts.py"*) ok "verifier cited from SKILL.md";; *) no "verifier uncited";; esac
  38. # ── staleness verifier: offline contract (§7) ───────────────────────────────
  39. if [[ -n "$PY" ]]; then
  40. V="$SKILL/scripts/check-tailwind-facts.py"
  41. ec() { local want="$1" lbl="$2"; shift 2; "$@" >/dev/null 2>&1; local got=$?
  42. [[ "$got" == "$want" ]] && ok "$lbl (exit $got)" || no "$lbl (want $want got $got)"; }
  43. TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
  44. ec 0 "py_compile" "$PY" -m py_compile "$V"
  45. ec 0 "--help" "$PY" "$V" --help
  46. ec 0 "--offline consistent" "$PY" "$V" --offline
  47. ec 2 "bad flag -> 2" "$PY" "$V" --bogus
  48. ec 2 "conflicting modes -> 2" "$PY" "$V" --offline --live
  49. jout="$("$PY" "$V" --offline --json 2>/dev/null)"
  50. case "$jout" in *"claude-mods.tailwind-ops.facts/v1"*) ok "--json envelope schema";; *) no "--json envelope schema missing";; esac
  51. ec 3 "missing facts -> 3" "$PY" "$V" --offline --facts "$TMP/nope.json"
  52. printf '{"schema":"claude-mods.tailwind-ops.facts/v1","packages":{"zzz":{"documented_major":1,"prose":["zzznotreal"]}}}' > "$TMP/drift.json"
  53. ec 10 "uncited package -> 10" "$PY" "$V" --offline --facts "$TMP/drift.json"
  54. else
  55. no "no working python to exercise the verifier"
  56. fi
  57. echo "=== $PASS passed, $FAIL failed ===" >&2
  58. [[ "$FAIL" -eq 0 ]] || exit 1