Four defects in issue-view.sh, each pinned by the new hermetic suite
test-issue-view-comments.sh (mock tea + curl, sandboxed repo):
F1 tea exits 1 in any repo with extensions.worktreeconfig=true. The wrapper
now names that as a git-config condition and falls back to the API.
F2 The API fallback dumped raw issue JSON, which carries only a comment
COUNT. It now fetches /comments and renders issue + comment bodies.
F3 The tea path never passed --comments, so comment bodies were never shown
non-interactively. It now does.
F4 Every tea failure printed the REVOKED OR STALE TOKEN note. The wrapper now
relays tea's own error line and only hints at credentials when tea did.
The suite joins ci.yml and the verify-release canonical list (mirror test).
Closes #1357
138 lines
7.3 KiB
Bash
Executable File
138 lines
7.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Regression: issue-view.sh must show comment BODIES, on both paths, and must name
|
|
# the failure tea actually reported instead of guessing a credential cause (#1357).
|
|
#
|
|
# Four defects, each with its own case below:
|
|
# F1 tea exits 1 in any repo with extensions.worktreeconfig=true; the wrapper must
|
|
# say so (git-config condition) and fall back to the API.
|
|
# F2 the API fallback dumped raw issue JSON, which carries only a comment COUNT.
|
|
# F3 the tea path never passed --comments, so tea prompted (non-interactively: nothing).
|
|
# F4 on ANY tea failure the wrapper printed the REVOKED OR STALE TOKEN note.
|
|
#
|
|
# Verification bar (plan §6): assert a real comment BODY appears, not a count and not
|
|
# `grep -c comment` (that instrument matched the issue title and read inverted).
|
|
#
|
|
# Hermetic: mock tea and curl on PATH, sandboxed repo. Resolves no real credentials.
|
|
set -euo pipefail
|
|
|
|
WORK_ROOT="${AGENT_WORK_ROOT:-${TMPDIR:-/tmp}}"
|
|
SANDBOX="$WORK_ROOT/issue-view-comments-test-$$"
|
|
MOCK_BIN="$SANDBOX/bin"; REPO_DIR="$SANDBOX/repo"; CALLS="$SANDBOX/calls.log"
|
|
cleanup() { rm -rf "$SANDBOX"; }
|
|
trap cleanup EXIT
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
TARGET="$SCRIPT_DIR/issue-view.sh"
|
|
[ -f "$TARGET" ] || { echo "FAIL: issue-view.sh not found beside this test"; exit 1; }
|
|
fail() { echo "FAIL: $*"; exit 1; }
|
|
|
|
mkdir -p "$MOCK_BIN" "$REPO_DIR" || fail "setup: cannot create sandbox under $WORK_ROOT"
|
|
: > "$CALLS" || fail "setup: cannot write calls log at $CALLS"
|
|
cd "$REPO_DIR" || fail "setup: cannot cd into $REPO_DIR"
|
|
git init -q || fail "setup: git init failed"
|
|
git remote add origin https://git.mosaicstack.dev/mosaicstack/stack.git || fail "setup: git remote add failed"
|
|
export PATH="$MOCK_BIN:$PATH" CALLS
|
|
export GITEA_URL="https://git.mosaicstack.dev"
|
|
export GITEA_TOKEN="redacted-test-token"
|
|
# The identity ladder must not reach for this seat's real login; the mock tea below
|
|
# defines the only login that exists in this sandbox.
|
|
unset MOSAIC_GIT_IDENTITY
|
|
# No fleet in the sandbox: on a host that runs one, get_gitea_token fails closed for an
|
|
# identity-less caller (by design), which would make this test measure the host, not
|
|
# the wrapper. An empty brain home makes the sandbox the same on every host.
|
|
export MOSAIC_BRAIN_HOME="$SANDBOX/brain"
|
|
mkdir -p "$MOSAIC_BRAIN_HOME" || fail "setup: cannot create sandbox brain home"
|
|
|
|
# Distinctive strings: a comment body that appears nowhere else, and an issue title
|
|
# that contains the word "comment" so a count-of-the-word instrument would misread.
|
|
BODY_MARKER="zebra-quill-comment-body-7731"
|
|
ISSUE_TITLE="wrapper never shows a comment"
|
|
|
|
# --- mock curl: serves the issue and its comments; logs every call --------------
|
|
cat > "$MOCK_BIN/curl" <<EOF
|
|
#!/bin/bash
|
|
url=""
|
|
while [ \$# -gt 0 ]; do
|
|
case "\$1" in
|
|
http*) url="\$1"; shift ;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
printf 'curl %s\n' "\$url" >> "$CALLS"
|
|
case "\$url" in
|
|
*/issues/77/comments)
|
|
if [ "\${MOCK_NO_COMMENTS:-}" = "1" ]; then echo '[]'; else
|
|
echo '[{"id":1,"user":{"login":"alice"},"created_at":"2026-08-21T00:00:00Z","body":"$BODY_MARKER"}]'; fi ;;
|
|
*/issues/77)
|
|
echo '{"number":77,"title":"$ISSUE_TITLE","state":"open","user":{"login":"bob"},"created_at":"2026-08-21T00:00:00Z","labels":[],"milestone":null,"html_url":"https://git.mosaicstack.dev/mosaicstack/stack/issues/77","body":"issue body","comments":1}' ;;
|
|
*) echo '{}' ;;
|
|
esac
|
|
exit 0
|
|
EOF
|
|
chmod +x "$MOCK_BIN/curl"
|
|
|
|
# --- mock tea: MOCK_TEA_MODE selects the behaviour under test --------------------
|
|
# ok : prints the issue, and the comment body ONLY when --comments is passed (F3)
|
|
# wtconfig : exits 1 with the repositoryformatversion error (F1/F4)
|
|
# badtoken : exits 1 with tea's credential error (F4 control: credential wording allowed)
|
|
cat > "$MOCK_BIN/tea" <<EOF
|
|
#!/bin/bash
|
|
printf 'tea %s\n' "\$*" >> "$CALLS"
|
|
if [[ "\$*" == *"login list"* ]]; then
|
|
echo '[{"name":"git.mosaicstack.dev","url":"https://git.mosaicstack.dev"}]'; exit 0
|
|
fi
|
|
case "\${MOCK_TEA_MODE:-ok}" in
|
|
wtconfig) echo 'Error: core.repositoryformatversion does not support extension: worktreeconfig' >&2; exit 1 ;;
|
|
badtoken) echo 'Failed to create Gitea client: invalid username, password or token' >&2; exit 1 ;;
|
|
esac
|
|
echo "# #77 $ISSUE_TITLE (open)"
|
|
echo "issue body"
|
|
if [[ "\$*" == *"--comments"* ]]; then echo "$BODY_MARKER"; fi
|
|
exit 0
|
|
EOF
|
|
chmod +x "$MOCK_BIN/tea"
|
|
|
|
[ "$(command -v tea)" = "$MOCK_BIN/tea" ] || fail "setup: tea does not resolve inside the sandbox"
|
|
[ "$(command -v curl)" = "$MOCK_BIN/curl" ] || fail "setup: curl does not resolve inside the sandbox"
|
|
|
|
run() { bash "$TARGET" -i 77 >"$SANDBOX/out" 2>"$SANDBOX/err"; echo $?; }
|
|
|
|
# F3: tea path shows the comment body, which the mock emits only under --comments.
|
|
: > "$CALLS"
|
|
rc=$(MOCK_TEA_MODE=ok run)
|
|
[ "$rc" = 0 ] || fail "F3: expected rc=0 on the tea path, got $rc: $(cat "$SANDBOX/err")"
|
|
grep -q -- '--comments' "$CALLS" || fail "F3: tea was not invoked with --comments: $(cat "$CALLS")"
|
|
grep -q "$BODY_MARKER" "$SANDBOX/out" || fail "F3: comment body missing from tea-path output"
|
|
if grep -q '^curl' "$CALLS"; then fail "F3: tea path succeeded but the API fallback ran anyway"; fi
|
|
|
|
# F1 + F2: worktreeconfig failure is named as a git-config condition, falls back to
|
|
# the API, and the API rendering includes the comment BODY.
|
|
: > "$CALLS"
|
|
rc=$(MOCK_TEA_MODE=wtconfig run)
|
|
[ "$rc" = 0 ] || fail "F1: expected rc=0 via API fallback, got $rc: $(cat "$SANDBOX/err")"
|
|
grep -q 'worktreeconfig' "$SANDBOX/err" || fail "F1: stderr does not name the worktreeconfig cause: $(cat "$SANDBOX/err")"
|
|
grep -q 'not a credential problem' "$SANDBOX/err" || fail "F1: stderr does not rule out the credential cause"
|
|
grep -q 'issues/77/comments' "$CALLS" || fail "F2: API fallback never fetched /comments: $(cat "$CALLS")"
|
|
grep -q "$BODY_MARKER" "$SANDBOX/out" || fail "F2: comment body missing from API-path output"
|
|
grep -q "$ISSUE_TITLE" "$SANDBOX/out" || fail "F2: issue title missing from API-path output"
|
|
if grep -q 'REVOKED OR STALE' "$SANDBOX/err"; then fail "F4: stale-token note printed for a git-config failure"; fi
|
|
if grep -q '"comments": 1' "$SANDBOX/out"; then fail "F2: output is still raw JSON (comment count instead of bodies)"; fi
|
|
|
|
# F4 control: a real credential error from tea may still carry the credential note,
|
|
# and tea's own line must be relayed so the reader sees the actual cause.
|
|
: > "$CALLS"
|
|
rc=$(MOCK_TEA_MODE=badtoken run)
|
|
[ "$rc" = 0 ] || fail "F4 control: expected rc=0 via API fallback, got $rc"
|
|
grep -q 'invalid username, password or token' "$SANDBOX/err" || fail "F4: tea's own error line was not relayed"
|
|
if grep -q 'worktreeconfig' "$SANDBOX/err"; then fail "F4: git-config wording printed for a credential failure"; fi
|
|
|
|
# Negative control: an issue with no comments prints no comment section on the API
|
|
# path. Without this, a renderer that always prints a section would pass F2.
|
|
: > "$CALLS"
|
|
rc=$(MOCK_TEA_MODE=wtconfig MOCK_NO_COMMENTS=1 run)
|
|
[ "$rc" = 0 ] || fail "negative control: expected rc=0, got $rc"
|
|
if grep -q -- '--- Comments' "$SANDBOX/out"; then fail "negative control: comment section printed for an issue with no comments"; fi
|
|
if grep -q "$BODY_MARKER" "$SANDBOX/out"; then fail "negative control: a comment body appeared for an issue with no comments"; fi
|
|
|
|
echo "issue-view comments regression harness passed"
|