diff --git a/packages/mosaic/framework/tools/codex/test-pr-diff-context.sh b/packages/mosaic/framework/tools/codex/test-pr-diff-context.sh index 0387875..db1d568 100644 --- a/packages/mosaic/framework/tools/codex/test-pr-diff-context.sh +++ b/packages/mosaic/framework/tools/codex/test-pr-diff-context.sh @@ -16,43 +16,43 @@ assert_contains() { [[ "$haystack" == *"$needle"* ]] || fail "expected output to contain: $needle" } -# A fresh clone is on main, while PR #795's changes exist only at the provider PR ref. -# The git stub returns the PR patch only when build_diff_context fetches and diffs that -# explicit ref; diffing main...HEAD reproduces the empty/wrong-branch bug. +# A fresh clone's local HEAD is unrelated to PR #795, whose changes target the +# synthetic non-main base release/next and exist only at the provider PR ref. +# The git stub returns the PR patch only for those two explicit fetched refs. if [[ "${1:-all}" != "fail-closed" ]]; then -FETCH_LOG="$TMP_DIR/fetch.log" -DIFF_LOG="$TMP_DIR/diff.log" -export FETCH_LOG DIFF_LOG + FETCH_LOG="$TMP_DIR/fetch.log" + DIFF_LOG="$TMP_DIR/diff.log" + export FETCH_LOG DIFF_LOG -( - # shellcheck source=common.sh - source "$SCRIPT_DIR/common.sh" + ( + # shellcheck source=common.sh + source "$SCRIPT_DIR/common.sh" - detect_platform() { - PLATFORM=gitea - export PLATFORM - } + detect_platform() { + PLATFORM=gitea + export PLATFORM + } - tea() { - if [[ "$*" == "pr list --fields index,base --output simple" ]]; then - printf '795 main\n' - return 0 - fi - return 1 - } + tea() { + if [[ "$*" == "pr list --fields index,base --output simple" ]]; then + printf '795 release/next\n' + return 0 + fi + return 1 + } - git() { - case "${1:-}" in - remote) - printf 'https://git.example.test/mosaicstack/stack.git\n' - ;; - fetch) - printf '%s\n' "$*" >> "$FETCH_LOG" - ;; - diff) - printf '%s\n' "$*" >> "$DIFF_LOG" - if [[ "$*" == *"refs/remotes/origin/pr/795/head"* && "$*" != *"HEAD"* ]]; then - cat <<'DIFF' + git() { + case "${1:-}" in + remote) + printf 'https://git.example.test/mosaicstack/stack.git\n' + ;; + fetch) + printf '%s\n' "$*" >> "$FETCH_LOG" + ;; + diff) + printf '%s\n' "$*" >> "$DIFF_LOG" + if [[ "$*" == *"refs/remotes/origin/release/next...refs/remotes/origin/pr/795/head"* && "$*" != *"HEAD"* ]]; then + cat <<'DIFF' diff --git a/pr-change.ts b/pr-change.ts --- a/pr-change.ts +++ b/pr-change.ts @@ -60,45 +60,51 @@ diff --git a/pr-change.ts b/pr-change.ts -old +actual-pr-change DIFF - fi - ;; - *) - return 1 - ;; - esac - } + fi + ;; + *) + return 1 + ;; + esac + } - diff_context=$(build_diff_context pr 795) - assert_contains "$diff_context" "actual-pr-change" -) + diff_context=$(build_diff_context pr 795) + assert_contains "$diff_context" "actual-pr-change" + ) -assert_contains "$(cat "$FETCH_LOG" 2>/dev/null || true)" "refs/pull/795/head" -assert_contains "$(cat "$DIFF_LOG" 2>/dev/null || true)" "refs/remotes/origin/pr/795/head" -if grep -q 'HEAD' "$DIFF_LOG" 2>/dev/null; then - fail "Gitea PR diff used local HEAD" -fi -echo "ok - Gitea PR mode fetches and diffs the explicit PR head" + assert_contains "$(cat "$FETCH_LOG" 2>/dev/null || true)" "refs/heads/release/next:refs/remotes/origin/release/next" + assert_contains "$(cat "$FETCH_LOG" 2>/dev/null || true)" "refs/pull/795/head" + assert_contains "$(cat "$DIFF_LOG" 2>/dev/null || true)" "refs/remotes/origin/release/next...refs/remotes/origin/pr/795/head" + if grep -q 'HEAD' "$DIFF_LOG" 2>/dev/null; then + fail "Gitea PR diff used local HEAD" + fi + echo "ok - Gitea PR mode fetches and diffs explicit non-main base and PR head refs" fi -# Exercise the CLI path with all provider/network mutations stubbed. An empty PR diff -# must be an actionable nonzero failure before Codex runs and before auto-post can run. +# Exercise both CLI paths with all provider/network mutations stubbed. An empty +# PR diff must fail before Codex runs and before either auto-post path can run. if [[ "${1:-all}" != "pr-head" ]]; then -SANDBOX="$TMP_DIR/sandbox" -mkdir -p "$SANDBOX/tools/codex/schemas" "$SANDBOX/tools/git" "$SANDBOX/bin" -cp "$SCRIPT_DIR/common.sh" "$SCRIPT_DIR/codex-code-review.sh" "$SANDBOX/tools/codex/" -cp "$SCRIPT_DIR/schemas/code-review-schema.json" "$SANDBOX/tools/codex/schemas/" + SANDBOX="$TMP_DIR/sandbox" + mkdir -p "$SANDBOX/tools/codex/schemas" "$SANDBOX/tools/git" "$SANDBOX/bin" + cp "$SCRIPT_DIR/common.sh" \ + "$SCRIPT_DIR/codex-code-review.sh" \ + "$SCRIPT_DIR/codex-security-review.sh" \ + "$SANDBOX/tools/codex/" + cp "$SCRIPT_DIR/schemas/code-review-schema.json" \ + "$SCRIPT_DIR/schemas/security-review-schema.json" \ + "$SANDBOX/tools/codex/schemas/" -cat > "$SANDBOX/tools/git/detect-platform.sh" <<'STUB' + cat > "$SANDBOX/tools/git/detect-platform.sh" <<'STUB' detect_platform() { PLATFORM=gitea; export PLATFORM; } get_repo_info() { printf 'mosaicstack/stack\n'; } STUB -cat > "$SANDBOX/tools/git/pr-review.sh" <<'STUB' + cat > "$SANDBOX/tools/git/pr-review.sh" <<'STUB' #!/bin/bash printf 'POST %s\n' "$*" >> "$POST_LOG" STUB -chmod +x "$SANDBOX/tools/git/pr-review.sh" + chmod +x "$SANDBOX/tools/git/pr-review.sh" -cat > "$SANDBOX/bin/git" <<'STUB' + cat > "$SANDBOX/bin/git" <<'STUB' #!/bin/bash case "${1:-}" in rev-parse) exit 0 ;; @@ -108,34 +114,40 @@ case "${1:-}" in *) exit 1 ;; esac STUB -cat > "$SANDBOX/bin/tea" <<'STUB' + cat > "$SANDBOX/bin/tea" <<'STUB' #!/bin/bash if [[ "$*" == "pr list --fields index,base --output simple" ]]; then - printf '795 main\n' + printf '795 release/next\n' exit 0 fi exit 1 STUB -cat > "$SANDBOX/bin/codex" <<'STUB' + cat > "$SANDBOX/bin/codex" <<'STUB' #!/bin/bash printf 'CODEX %s\n' "$*" >> "$CODEX_LOG" exit 99 STUB -chmod +x "$SANDBOX/bin/git" "$SANDBOX/bin/tea" "$SANDBOX/bin/codex" + chmod +x "$SANDBOX/bin/git" "$SANDBOX/bin/tea" "$SANDBOX/bin/codex" -POST_LOG="$TMP_DIR/post.log" -CODEX_LOG="$TMP_DIR/codex.log" -export POST_LOG CODEX_LOG -set +e -PATH="$SANDBOX/bin:$PATH" "$SANDBOX/tools/codex/codex-code-review.sh" -n 795 \ - >"$TMP_DIR/review.stdout" 2>"$TMP_DIR/review.stderr" -review_status=$? -set -e + POST_LOG="$TMP_DIR/post.log" + CODEX_LOG="$TMP_DIR/codex.log" + export POST_LOG CODEX_LOG -[[ "$review_status" -ne 0 ]] || fail "empty PR diff returned success instead of failing closed" -[[ ! -s "$CODEX_LOG" ]] || fail "Codex ran for an empty PR diff" -[[ ! -s "$POST_LOG" ]] || fail "review auto-post ran for an empty PR diff" -assert_contains "$(cat "$TMP_DIR/review.stderr")" "Error:" -assert_contains "$(cat "$TMP_DIR/review.stderr")" "PR #795" -echo "ok - empty PR diff fails closed before Codex and auto-post" + for review_kind in code security; do + : > "$POST_LOG" + : > "$CODEX_LOG" + review_script="$SANDBOX/tools/codex/codex-${review_kind}-review.sh" + set +e + PATH="$SANDBOX/bin:$PATH" "$review_script" -n 795 \ + >"$TMP_DIR/${review_kind}.stdout" 2>"$TMP_DIR/${review_kind}.stderr" + review_status=$? + set -e + + [[ "$review_status" -ne 0 ]] || fail "${review_kind} review returned success for an empty PR diff" + [[ ! -s "$CODEX_LOG" ]] || fail "Codex ran for an empty ${review_kind} PR diff" + [[ ! -s "$POST_LOG" ]] || fail "${review_kind} review auto-post ran for an empty PR diff" + assert_contains "$(cat "$TMP_DIR/${review_kind}.stderr")" "Error:" + assert_contains "$(cat "$TMP_DIR/${review_kind}.stderr")" "PR #795" + echo "ok - empty ${review_kind} PR diff fails closed before Codex and auto-post" + done fi diff --git a/packages/mosaic/package.json b/packages/mosaic/package.json index 47ec2e3..0de2b3d 100644 --- a/packages/mosaic/package.json +++ b/packages/mosaic/package.json @@ -24,7 +24,8 @@ "build": "tsc", "lint": "eslint src", "typecheck": "tsc --noEmit", - "test": "vitest run --passWithNoTests" + "test": "vitest run --passWithNoTests && pnpm run test:framework-shell", + "test:framework-shell": "bash framework/tools/codex/test-pr-diff-context.sh" }, "dependencies": { "@mosaicstack/brain": "workspace:*",