141 lines
4.7 KiB
Bash
141 lines
4.7 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# phase-test.sh — Integration test for the phase-signaling protocol
|
||
|
|
#
|
||
|
|
# Simulates a Claude session writing phases and an orchestrator reading them.
|
||
|
|
# Tests all phase values and verifies the read/write contract.
|
||
|
|
#
|
||
|
|
# Usage: bash dev/phase-test.sh
|
||
|
|
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
PROJECT="testproject"
|
||
|
|
ISSUE="999"
|
||
|
|
PHASE_FILE="/tmp/dev-session-${PROJECT}-${ISSUE}.phase"
|
||
|
|
|
||
|
|
PASS=0
|
||
|
|
FAIL=0
|
||
|
|
|
||
|
|
ok() {
|
||
|
|
printf '[PASS] %s\n' "$1"
|
||
|
|
PASS=$((PASS + 1))
|
||
|
|
}
|
||
|
|
|
||
|
|
fail() {
|
||
|
|
printf '[FAIL] %s\n' "$1"
|
||
|
|
FAIL=$((FAIL + 1))
|
||
|
|
}
|
||
|
|
|
||
|
|
# Cleanup
|
||
|
|
rm -f "$PHASE_FILE"
|
||
|
|
|
||
|
|
# ── Test 1: phase file path convention ────────────────────────────────────────
|
||
|
|
expected_path="/tmp/dev-session-${PROJECT}-${ISSUE}.phase"
|
||
|
|
if [ "$PHASE_FILE" = "$expected_path" ]; then
|
||
|
|
ok "phase file path follows /tmp/dev-session-{project}-{issue}.phase convention"
|
||
|
|
else
|
||
|
|
fail "phase file path mismatch: got $PHASE_FILE, expected $expected_path"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# ── Test 2: write and read each phase sentinel ─────────────────────────────────
|
||
|
|
check_phase() {
|
||
|
|
local sentinel="$1"
|
||
|
|
echo "$sentinel" > "$PHASE_FILE"
|
||
|
|
local got
|
||
|
|
got=$(cat "$PHASE_FILE" | tr -d '[:space:]')
|
||
|
|
if [ "$got" = "$sentinel" ]; then
|
||
|
|
ok "write/read: $sentinel"
|
||
|
|
else
|
||
|
|
fail "write/read: expected '$sentinel', got '$got'"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
check_phase "PHASE:awaiting_ci"
|
||
|
|
check_phase "PHASE:awaiting_review"
|
||
|
|
check_phase "PHASE:needs_human"
|
||
|
|
check_phase "PHASE:done"
|
||
|
|
check_phase "PHASE:failed"
|
||
|
|
|
||
|
|
# ── Test 3: write overwrites (not appends) ─────────────────────────────────────
|
||
|
|
echo "PHASE:awaiting_ci" > "$PHASE_FILE"
|
||
|
|
echo "PHASE:awaiting_review" > "$PHASE_FILE"
|
||
|
|
line_count=$(wc -l < "$PHASE_FILE")
|
||
|
|
if [ "$line_count" -eq 1 ]; then
|
||
|
|
ok "phase file overwrite (single line after two writes)"
|
||
|
|
else
|
||
|
|
fail "phase file should have 1 line, got $line_count"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# ── Test 4: failed phase with reason ──────────────────────────────────────────
|
||
|
|
printf 'PHASE:failed\nReason: %s\n' "shellcheck failed on ci.sh" > "$PHASE_FILE"
|
||
|
|
first_line=$(head -1 "$PHASE_FILE")
|
||
|
|
second_line=$(sed -n '2p' "$PHASE_FILE")
|
||
|
|
if [ "$first_line" = "PHASE:failed" ] && echo "$second_line" | grep -q "^Reason:"; then
|
||
|
|
ok "PHASE:failed with reason line"
|
||
|
|
else
|
||
|
|
fail "PHASE:failed format: first='$first_line' second='$second_line'"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# ── Test 5: orchestrator read function ────────────────────────────────────────
|
||
|
|
read_phase() {
|
||
|
|
local pfile="$1"
|
||
|
|
# Allow cat to fail (missing file) — pipeline exits 0 via || true
|
||
|
|
{ cat "$pfile" 2>/dev/null || true; } | head -1 | tr -d '[:space:]'
|
||
|
|
}
|
||
|
|
|
||
|
|
echo "PHASE:awaiting_ci" > "$PHASE_FILE"
|
||
|
|
phase=$(read_phase "$PHASE_FILE")
|
||
|
|
if [ "$phase" = "PHASE:awaiting_ci" ]; then
|
||
|
|
ok "orchestrator read_phase() extracts first line"
|
||
|
|
else
|
||
|
|
fail "orchestrator read_phase() got: '$phase'"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# ── Test 6: missing file returns empty ────────────────────────────────────────
|
||
|
|
rm -f "$PHASE_FILE"
|
||
|
|
phase=$(read_phase "$PHASE_FILE")
|
||
|
|
if [ -z "$phase" ]; then
|
||
|
|
ok "missing phase file returns empty string"
|
||
|
|
else
|
||
|
|
fail "missing phase file should return empty, got: '$phase'"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# ── Test 7: all valid phase values are recognized ─────────────────────────────
|
||
|
|
is_valid_phase() {
|
||
|
|
local p="$1"
|
||
|
|
case "$p" in
|
||
|
|
PHASE:awaiting_ci|PHASE:awaiting_review|PHASE:needs_human|PHASE:done|PHASE:failed)
|
||
|
|
return 0 ;;
|
||
|
|
*)
|
||
|
|
return 1 ;;
|
||
|
|
esac
|
||
|
|
}
|
||
|
|
|
||
|
|
for p in "PHASE:awaiting_ci" "PHASE:awaiting_review" "PHASE:needs_human" \
|
||
|
|
"PHASE:done" "PHASE:failed"; do
|
||
|
|
if is_valid_phase "$p"; then
|
||
|
|
ok "is_valid_phase: $p"
|
||
|
|
else
|
||
|
|
fail "is_valid_phase rejected valid phase: $p"
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
if ! is_valid_phase "PHASE:unknown"; then
|
||
|
|
ok "is_valid_phase rejects unknown phase"
|
||
|
|
else
|
||
|
|
fail "is_valid_phase should reject PHASE:unknown"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# ── Cleanup ───────────────────────────────────────────────────────────────────
|
||
|
|
rm -f "$PHASE_FILE"
|
||
|
|
|
||
|
|
# ── Summary ───────────────────────────────────────────────────────────────────
|
||
|
|
echo ""
|
||
|
|
printf 'Results: %d passed, %d failed\n' "$PASS" "$FAIL"
|
||
|
|
if [ "$FAIL" -eq 0 ]; then
|
||
|
|
echo "All tests passed."
|
||
|
|
exit 0
|
||
|
|
else
|
||
|
|
echo "Some tests failed."
|
||
|
|
exit 1
|
||
|
|
fi
|