|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Tests for detect_spurious_complete() in run-loop.sh. |
| 3 | +# |
| 4 | +# The case that motivated these: a PLAN run emitted the completion promise while |
| 5 | +# plan-draft-writer was still running in the BACKGROUND. The loop ended, the |
| 6 | +# writer was abandoned, post-loop code review passed vacuously over an empty |
| 7 | +# diff, and the run exited 0 having written no plan.json at all -- so the user's |
| 8 | +# implementation-plan artifact looked done and was empty. The guard could not |
| 9 | +# see it: its checks only validate pendingTasks INSIDE an existing plan.json. |
| 10 | +# |
| 11 | +# run-loop.sh guards main() with [[ "${BASH_SOURCE[0]}" == "$0" ]], so sourcing |
| 12 | +# it defines the functions without running the loop. |
| 13 | +# |
| 14 | +# Usage: |
| 15 | +# bash plugins/code/scripts/tests/test_spurious_complete.sh |
| 16 | +# |
| 17 | +# Exit code: 0 if all tests pass, 1 if any test fails. |
| 18 | + |
| 19 | +set -uo pipefail |
| 20 | + |
| 21 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 22 | +RUN_LOOP="$SCRIPT_DIR/../run-loop.sh" |
| 23 | + |
| 24 | +PASS_COUNT=0 |
| 25 | +FAIL_COUNT=0 |
| 26 | + |
| 27 | +pass() { |
| 28 | + echo " PASS: $1" |
| 29 | + PASS_COUNT=$(( PASS_COUNT + 1 )) |
| 30 | +} |
| 31 | + |
| 32 | +fail() { |
| 33 | + echo " FAIL: $1 -- $2" |
| 34 | + FAIL_COUNT=$(( FAIL_COUNT + 1 )) |
| 35 | +} |
| 36 | + |
| 37 | +assert_subcode() { |
| 38 | + local name="$1" json="$2" expected="$3" |
| 39 | + local actual |
| 40 | + actual=$(echo "$json" | jq -r '.subcode // ""' 2>/dev/null || echo "") |
| 41 | + if [[ "$actual" == "$expected" ]]; then |
| 42 | + pass "$name (subcode=$actual)" |
| 43 | + else |
| 44 | + fail "$name" "expected subcode '$expected', got '$actual' in: $json" |
| 45 | + fi |
| 46 | +} |
| 47 | + |
| 48 | +assert_not_spurious() { |
| 49 | + local name="$1" json="$2" |
| 50 | + local actual |
| 51 | + actual=$(echo "$json" | jq -r '.subcode // ""' 2>/dev/null || echo "") |
| 52 | + if [[ -z "$actual" ]]; then |
| 53 | + pass "$name (not flagged)" |
| 54 | + else |
| 55 | + fail "$name" "expected no subcode, got '$actual' in: $json" |
| 56 | + fi |
| 57 | +} |
| 58 | + |
| 59 | +# shellcheck source=/dev/null |
| 60 | +source "$RUN_LOOP" |
| 61 | + |
| 62 | +echo "detect_spurious_complete" |
| 63 | + |
| 64 | +# --- A --prd run that produced no plan is the reported defect --------------- |
| 65 | +WORKDIR=$(mktemp -d) |
| 66 | +printf '%s' '{"phase":"Phase 1: Planning","status":"IN_PROGRESS"}' > "$WORKDIR/state.json" |
| 67 | +assert_subcode "COMPLETE with no plan.json on a --prd run is spurious" \ |
| 68 | + "$(detect_spurious_complete "$WORKDIR" "/tmp/prd.md")" \ |
| 69 | + "PLAN_MISSING_AT_COMPLETION" |
| 70 | +rm -rf "$WORKDIR" |
| 71 | + |
| 72 | +# --- A run that never owed a plan is left alone ---------------------------- |
| 73 | +WORKDIR=$(mktemp -d) |
| 74 | +assert_not_spurious "COMPLETE with no plan.json and no PRD is not spurious" \ |
| 75 | + "$(detect_spurious_complete "$WORKDIR" "")" |
| 76 | +rm -rf "$WORKDIR" |
| 77 | + |
| 78 | +# --- The AWAITING_USER hard stop must keep winning ------------------------- |
| 79 | +# A drafted plan parked for review legitimately has no finished plan yet; the |
| 80 | +# new branch must not turn that documented stop into a failure. |
| 81 | +WORKDIR=$(mktemp -d) |
| 82 | +printf '%s' '{"phase":"Phase 1.1","status":"AWAITING_USER"}' > "$WORKDIR/state.json" |
| 83 | +assert_not_spurious "AWAITING_USER outranks the missing-plan check" \ |
| 84 | + "$(detect_spurious_complete "$WORKDIR" "/tmp/prd.md")" |
| 85 | +rm -rf "$WORKDIR" |
| 86 | + |
| 87 | +# --- Existing behaviour: a complete plan is still clean --------------------- |
| 88 | +WORKDIR=$(mktemp -d) |
| 89 | +printf '%s' '{"pendingTasks":[],"openQuestions":[]}' > "$WORKDIR/plan.json" |
| 90 | +assert_not_spurious "a plan with no pending tasks is not spurious" \ |
| 91 | + "$(detect_spurious_complete "$WORKDIR" "/tmp/prd.md")" |
| 92 | +rm -rf "$WORKDIR" |
| 93 | + |
| 94 | +# --- Existing behaviour: pending tasks still flagged, and not as the new code - |
| 95 | +WORKDIR=$(mktemp -d) |
| 96 | +printf '%s' '{"pendingTasks":[{"id":"T-1.1"}],"openQuestions":[]}' > "$WORKDIR/plan.json" |
| 97 | +assert_subcode "pending tasks at completion still flagged" \ |
| 98 | + "$(detect_spurious_complete "$WORKDIR" "/tmp/prd.md")" \ |
| 99 | + "PENDING_TASKS_AT_COMPLETION" |
| 100 | +rm -rf "$WORKDIR" |
| 101 | + |
| 102 | +WORKDIR=$(mktemp -d) |
| 103 | +printf '%s' '{"pendingTasks":[{"id":"T-1.1"}],"openQuestions":[{"id":"Q-1"}]}' > "$WORKDIR/plan.json" |
| 104 | +assert_subcode "pending tasks blocked by open questions still flagged" \ |
| 105 | + "$(detect_spurious_complete "$WORKDIR" "/tmp/prd.md")" \ |
| 106 | + "PENDING_TASKS_BLOCKED_BY_QUESTIONS" |
| 107 | +rm -rf "$WORKDIR" |
| 108 | + |
| 109 | +echo |
| 110 | +echo "passed: $PASS_COUNT failed: $FAIL_COUNT" |
| 111 | +[[ "$FAIL_COUNT" -eq 0 ]] |
0 commit comments