Review finding cf241526. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
161 lines
8.2 KiB
Bash
161 lines
8.2 KiB
Bash
#!/usr/bin/env bash
|
|
# tests/e2e-files-trial4.sh
|
|
# End to end for the trial-4 plan (steps 11 and 13): the four CLIs run the
|
|
# way a command runs them.
|
|
# 1. lib/validators/plan-validator.mjs --strict --json on three plans built
|
|
# with tests/helpers/plan-v18-builder.mjs: a valid 1.9 plan (exit 0), a
|
|
# 1.9 plan with an unticked Files entry (exit 1, PLAN_STEP_PATH_NOT_FIRST),
|
|
# a 1.8 plan with an unticked path (exit 0, PLAN_STEP_PATH_UNTICKED).
|
|
# 2. lib/util/artifact-commit.mjs in a temp git repo: with a trailer (exit 0,
|
|
# the trailer is the message's last line), without (exit 1, HEAD unchanged).
|
|
# 3. lib/review/verdict-gate.mjs on the legal (exit 0) and the illegal
|
|
# (exit 2) brief-conflict review fixtures.
|
|
# 4. plan-validator on a 1.8 plan whose Test first File line names a source
|
|
# path in its description (exit 0, no PLAN_STEP_TESTS_ONLY: decision 20).
|
|
# 5. plan-validator on a 1.9 plan whose Files is a backticked None (exit 1,
|
|
# PLAN_STEP_PATH_NOT_A_PATH).
|
|
# 6. lib/verification/red-proof.mjs with --plan/--step and a --tests that
|
|
# differs from the plan's list (exit 2, tests-mismatch).
|
|
# 7. artifact-commit with an empty-model trailer (exit 1, HEAD unchanged).
|
|
# Prints "e2e: ok" and exits 0, or names the failed check and exits 1.
|
|
# bash 3.2 compatible, ASCII only.
|
|
|
|
set -u
|
|
|
|
cd "$(dirname "$0")/.." || { echo "e2e: FAIL - cannot cd to the repo root" >&2; exit 1; }
|
|
ROOT="$(pwd)"
|
|
|
|
unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
|
|
|
|
TMP="$(mktemp -d "${TMPDIR:-/tmp}/e2e-files-trial4.XXXXXX")" || { echo "e2e: FAIL - mktemp" >&2; exit 1; }
|
|
trap 'rm -rf "$TMP"' EXIT
|
|
|
|
fail() {
|
|
echo "e2e: FAIL - $1" >&2
|
|
exit 1
|
|
}
|
|
|
|
TRAILER='Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>'
|
|
|
|
# ---- 1. the plan validator on three plans ----
|
|
|
|
node --input-type=module -e '
|
|
import { writeFileSync } from "node:fs";
|
|
import { buildPlan } from "./tests/helpers/plan-v18-builder.mjs";
|
|
const dir = process.argv[1];
|
|
const e2e = { endToEnd: "bash tests/e2e.sh" };
|
|
writeFileSync(dir + "/plan-1-9-valid.md", buildPlan({ version: "1.9" }));
|
|
writeFileSync(dir + "/plan-1-9-unticked.md",
|
|
buildPlan({ version: "1.9", steps: [{ filesRaw: "src/mod1.mjs - the parser" }, e2e] }));
|
|
writeFileSync(dir + "/plan-1-8-unticked.md",
|
|
buildPlan({ steps: [{ filesRaw: "`tests/mod1.test.mjs`, src/mod1.mjs" }, e2e] }));
|
|
' "$TMP" || fail "could not write the three plans with tests/helpers/plan-v18-builder.mjs"
|
|
|
|
out="$(node lib/validators/plan-validator.mjs --strict --json "$TMP/plan-1-9-valid.md" 2>&1)"
|
|
rc=$?
|
|
[ "$rc" -eq 0 ] || fail "valid 1.9 plan: plan-validator exit $rc, expected 0: $out"
|
|
|
|
out="$(node lib/validators/plan-validator.mjs --strict --json "$TMP/plan-1-9-unticked.md" 2>&1)"
|
|
rc=$?
|
|
[ "$rc" -eq 1 ] || fail "1.9 plan with an unticked Files entry: plan-validator exit $rc, expected 1: $out"
|
|
echo "$out" | grep -q 'PLAN_STEP_PATH_NOT_FIRST' \
|
|
|| fail "1.9 plan with an unticked Files entry: no PLAN_STEP_PATH_NOT_FIRST in: $out"
|
|
|
|
out="$(node lib/validators/plan-validator.mjs --strict --json "$TMP/plan-1-8-unticked.md" 2>&1)"
|
|
rc=$?
|
|
[ "$rc" -eq 0 ] || fail "1.8 plan with an unticked path: plan-validator exit $rc, expected 0: $out"
|
|
echo "$out" | grep -q 'PLAN_STEP_PATH_UNTICKED' \
|
|
|| fail "1.8 plan with an unticked path: no PLAN_STEP_PATH_UNTICKED in: $out"
|
|
|
|
# ---- 2. the approval commit in a temp git repo ----
|
|
|
|
REPO="$TMP/repo"
|
|
mkdir -p "$REPO" "$TMP/hooks-none" || fail "mkdir $REPO"
|
|
git -C "$REPO" init -q || fail "git init"
|
|
git -C "$REPO" config user.name 'E2E Test'
|
|
git -C "$REPO" config user.email 'test@example.invalid'
|
|
git -C "$REPO" config commit.gpgsign false
|
|
git -C "$REPO" config core.hooksPath "$TMP/hooks-none"
|
|
echo 'x' > "$REPO/README.md"
|
|
git -C "$REPO" add README.md && git -C "$REPO" commit -q -m 'init' || fail "initial commit in the temp repo"
|
|
|
|
echo '# brief' > "$REPO/brief.md"
|
|
H1='cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'
|
|
H2='dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
|
|
(cd "$REPO" && node "$ROOT/lib/util/approval-log.mjs" append --kind intent --subject brief.md --sha256 "$H1" >/dev/null) \
|
|
|| fail "approval-log append (first record)"
|
|
[ -f "$REPO/.claude/voyage/approvals.jsonl" ] || fail "no .claude/voyage/approvals.jsonl in the temp repo"
|
|
|
|
out="$(node lib/util/artifact-commit.mjs --repo "$REPO" --store local --message 'docs(voyage): approve intent e2e' --trailer "$TRAILER" 2>&1)"
|
|
rc=$?
|
|
[ "$rc" -eq 0 ] || fail "artifact-commit with a trailer: exit $rc, expected 0: $out"
|
|
last="$(git -C "$REPO" log -1 --format=%B | sed '/^$/d' | tail -n 1)"
|
|
[ "$last" = "$TRAILER" ] || fail "artifact-commit with a trailer: the last message line is '$last', expected '$TRAILER'"
|
|
|
|
(cd "$REPO" && node "$ROOT/lib/util/approval-log.mjs" append --kind intent --subject brief.md --sha256 "$H2" >/dev/null) \
|
|
|| fail "approval-log append (second record)"
|
|
before="$(git -C "$REPO" rev-parse HEAD)"
|
|
out="$(node lib/util/artifact-commit.mjs --repo "$REPO" --store local --message 'docs(voyage): approve intent e2e' 2>&1)"
|
|
rc=$?
|
|
[ "$rc" -eq 1 ] || fail "artifact-commit without a trailer: exit $rc, expected 1: $out"
|
|
after="$(git -C "$REPO" rev-parse HEAD)"
|
|
[ "$before" = "$after" ] || fail "artifact-commit without a trailer: HEAD moved from $before to $after"
|
|
|
|
# ---- 3. the verdict gate on the two brief-conflict fixtures ----
|
|
|
|
out="$(node lib/review/verdict-gate.mjs tests/fixtures/review-brief-conflict-legal.md 2>&1)"
|
|
rc=$?
|
|
[ "$rc" -eq 0 ] || fail "verdict-gate on review-brief-conflict-legal.md: exit $rc, expected 0: $out"
|
|
|
|
out="$(node lib/review/verdict-gate.mjs tests/fixtures/review-brief-conflict-illegal.md 2>&1)"
|
|
rc=$?
|
|
[ "$rc" -eq 2 ] || fail "verdict-gate on review-brief-conflict-illegal.md: exit $rc, expected 2: $out"
|
|
|
|
# ---- 4. and 5. the plan validator on decision 20 and on a placeholder ----
|
|
|
|
node --input-type=module -e '
|
|
import { writeFileSync } from "node:fs";
|
|
import { buildPlan } from "./tests/helpers/plan-v18-builder.mjs";
|
|
const dir = process.argv[1];
|
|
const e2e = { endToEnd: "bash tests/e2e.sh" };
|
|
writeFileSync(dir + "/plan-1-8-description.md", buildPlan({ steps: [{
|
|
file: "src/parser.mjs", test: "tests/parser.test.mjs",
|
|
filesRaw: "`src/parser.mjs`, `tests/parser.test.mjs`",
|
|
testFirstRaw: " - File: `tests/parser.test.mjs` (new) - covers `parse()` in `src/parser.mjs`",
|
|
}, e2e] }));
|
|
writeFileSync(dir + "/plan-1-9-none.md",
|
|
buildPlan({ version: "1.9", steps: [{ filesRaw: "`None`" }, e2e] }));
|
|
' "$TMP" || fail "could not write the plans for checks 4 and 5"
|
|
|
|
out="$(node lib/validators/plan-validator.mjs --strict --json "$TMP/plan-1-8-description.md" 2>&1)"
|
|
rc=$?
|
|
[ "$rc" -eq 0 ] || fail "1.8 plan with a source path in the File line description: plan-validator exit $rc, expected 0: $out"
|
|
if echo "$out" | grep -q 'PLAN_STEP_TESTS_ONLY'; then
|
|
fail "1.8 plan with a source path in the File line description: PLAN_STEP_TESTS_ONLY in: $out"
|
|
fi
|
|
|
|
out="$(node lib/validators/plan-validator.mjs --strict --json "$TMP/plan-1-9-none.md" 2>&1)"
|
|
rc=$?
|
|
[ "$rc" -eq 1 ] || fail "1.9 plan whose Files is a backticked None: plan-validator exit $rc, expected 1: $out"
|
|
echo "$out" | grep -q 'PLAN_STEP_PATH_NOT_A_PATH' \
|
|
|| fail "1.9 plan whose Files is a backticked None: no PLAN_STEP_PATH_NOT_A_PATH in: $out"
|
|
|
|
# ---- 6. red proof refuses a --tests that differs from the plan ----
|
|
|
|
out="$(node lib/verification/red-proof.mjs --red-only --base HEAD --cwd "$REPO" --cmd 'node --test tests/parser.test.mjs' --tests tests/other.test.mjs --plan "$TMP/plan-1-8-description.md" --step 1 --json 2>&1)"
|
|
rc=$?
|
|
[ "$rc" -eq 2 ] || fail "red-proof with a --tests that differs from the plan: exit $rc, expected 2: $out"
|
|
echo "$out" | grep -q 'tests-mismatch' \
|
|
|| fail "red-proof with a --tests that differs from the plan: no tests-mismatch in: $out"
|
|
|
|
# ---- 7. the approval commit refuses an empty-model trailer ----
|
|
|
|
before="$(git -C "$REPO" rev-parse HEAD)"
|
|
out="$(node lib/util/artifact-commit.mjs --repo "$REPO" --store local --message 'docs(voyage): approve intent e2e' --trailer 'Co-Authored-By: Claude <noreply@anthropic.com>' 2>&1)"
|
|
rc=$?
|
|
[ "$rc" -eq 1 ] || fail "artifact-commit with an empty-model trailer: exit $rc, expected 1: $out"
|
|
after="$(git -C "$REPO" rev-parse HEAD)"
|
|
[ "$before" = "$after" ] || fail "artifact-commit with an empty-model trailer: HEAD moved from $before to $after"
|
|
|
|
echo 'e2e: ok'
|
|
exit 0
|