#!/bin/bash # test-hooks.sh — Unit tests for ms-ai-architect hook scripts # Usage: bash tests/test-hooks.sh set -euo pipefail PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)" SCRIPTS_DIR="$PLUGIN_ROOT/hooks/scripts" PASS=0 FAIL=0 pass() { echo -e "\033[0;32m ✓ $1\033[0m"; PASS=$((PASS + 1)); } fail() { echo -e "\033[0;31m ✗ $1\033[0m"; FAIL=$((FAIL + 1)); } echo "=== Hook Script Tests ===" echo "" # NB: the pre-edit-secrets.mjs hook was removed in v1.7.0 (secrets scanning # consolidated to the llm-security plugin — see CHANGELOG). Its tests were # removed with it; secrets-scanning coverage now lives in llm-security. # ------------------------------------------------------- # session-start-context.mjs # ------------------------------------------------------- echo "--- session-start-context.mjs ---" OUTPUT=$(CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" node "$SCRIPTS_DIR/session-start-context.mjs" 2>/dev/null) EXIT_CODE=$? if [ $EXIT_CODE -eq 0 ]; then pass "Runs without error (exit 0)" else fail "Expected exit 0, got $EXIT_CODE" fi if echo "$OUTPUT" | grep -q "Architect:"; then pass "Output contains 'Architect:' prefix" else fail "Output missing 'Architect:' prefix" fi # C2.1: ambient org-context injection from the USER-owned org dir (HOME override). # Proves K1 — the hook injects org CONTENT, not just a status nudge. TMPHOME=$(mktemp -d) mkdir -p "$TMPHOME/.claude/ms-ai-architect/org" cat > "$TMPHOME/.claude/ms-ai-architect/org/organization-profile.md" <<'ORGEOF' --- category: organization-profile completed: true --- # Virksomhetsprofil ## Sektor Statlig ## Virksomhet Testetaten ORGEOF # C2.2: optional free-prose file — read for the ambient summary, NOT counted # toward onboarding completeness. Proves K3 (free-text visible in interaction). cat > "$TMPHOME/.claude/ms-ai-architect/org/free-context.md" <<'FREEEOF' --- category: free-context completed: true --- # Fri kontekst ## Fri kontekst Vi migrerer fra on-prem til Azure i 2026. FREEEOF OUTPUT=$(CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" HOME="$TMPHOME" node "$SCRIPTS_DIR/session-start-context.mjs" 2>/dev/null) if echo "$OUTPUT" | grep -q "Virksomhetskontekst (auto-injisert"; then pass "Injects ambient org-context block when user org/ has content (K1)" else fail "Missing ambient org-context block when user org/ has content" fi if echo "$OUTPUT" | grep -q "Sektor: Statlig"; then pass "Ambient block reflects an org fact (Sektor: Statlig)" else fail "Ambient block missing org fact" fi if echo "$OUTPUT" | grep -q "Fri kontekst: Vi migrerer fra on-prem til Azure"; then pass "Ambient block surfaces optional free-context (K3)" else fail "Ambient block missing free-context content (K3)" fi # Empty home (no user org, no plugin org) => onboarding nudge, NO ambient block. TMPHOME2=$(mktemp -d) OUTPUT=$(CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" HOME="$TMPHOME2" node "$SCRIPTS_DIR/session-start-context.mjs" 2>/dev/null) if echo "$OUTPUT" | grep -q "Ingen virksomhetstilpasning"; then pass "No org content => onboarding nudge" else fail "Expected onboarding nudge when no org content" fi if echo "$OUTPUT" | grep -q "Virksomhetskontekst (auto-injisert"; then fail "Should NOT inject ambient block when org is empty" else pass "No ambient block when org is empty" fi rm -rf "$TMPHOME" "$TMPHOME2" echo "" # ------------------------------------------------------- # stop-assessment-reminder.mjs # ------------------------------------------------------- echo "--- stop-assessment-reminder.mjs ---" # Test from a temp dir without .work/ TMPDIR_TEST=$(mktemp -d) OUTPUT=$(cd "$TMPDIR_TEST" && node "$SCRIPTS_DIR/stop-assessment-reminder.mjs" 2>/dev/null) EXIT_CODE=$? if [ $EXIT_CODE -eq 0 ]; then pass "No .work/: exit 0" else fail "No .work/: expected exit 0, got $EXIT_CODE" fi if [ "$OUTPUT" = "{}" ]; then pass "No .work/: returns {}" else fail "No .work/: expected {}, got: $OUTPUT" fi # Test with a fresh .work/ session mkdir -p "$TMPDIR_TEST/.work/test-session" touch "$TMPDIR_TEST/.work/test-session/state.json" OUTPUT=$(cd "$TMPDIR_TEST" && node "$SCRIPTS_DIR/stop-assessment-reminder.mjs" 2>/dev/null) if echo "$OUTPUT" | grep -q "systemMessage"; then pass "Fresh .work/: returns systemMessage" else fail "Fresh .work/: expected systemMessage in output" fi if echo "$OUTPUT" | grep -q "architect:adr"; then pass "Fresh .work/: suggests /architect:adr" else fail "Fresh .work/: missing /architect:adr suggestion" fi rm -rf "$TMPDIR_TEST" echo "" # ------------------------------------------------------- # Summary # ------------------------------------------------------- TOTAL=$((PASS + FAIL)) echo "=== Results: $PASS/$TOTAL passed, $FAIL failed ===" if [ "$FAIL" -gt 0 ]; then exit 1 fi