#!/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" <> "$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" <> "$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"