Some checks failed
CI / test (pull_request) Has been cancelled
CI / release-exercise (pull_request) Has been cancelled
CI / self-guards (pull_request) Has been cancelled
CI / action-exercise (pull_request) Has been cancelled
CI / docs-sync-exercise (pull_request) Has been cancelled
labels / labels (pull_request) Has been cancelled
Term 3 of #188. Forgejo has no GraphQL API, so these two gathers could not be translated — there is no endpoint to translate them to. A real forgejo-runner job says so from the other side: GITHUB_GRAPHQL_URL arrives set to the empty string (probe task 278). MERGED_REF_PR_RECORDS was already a body parse; GraphQL was buying pagination, nothing semantic. OPEN_PR_ISSUES used GitHub's own parse of the closing keywords, so it becomes lib/closes_references.sh — a sibling of refs_references, sharing its LOCAL/CROSS classifier so rig#112 can still never be read as local #112 (#61). Both gathers now read /pulls, which /api/v3 and /api/v1 return in the same shape (measured on both). merged_at replaces GraphQL's states: MERGED. Bodies travel base64: jq's @tsv escapes a newline to a literal backslash-n, which a line parser reads as one line and loses every declaration after the first. The accepted delta, written down rather than rediscovered: GitHub also records closing links attached through the PR development sidebar, which live in no body. This family declares links in the body, so the delta is zero here. Refs #188
87 lines
3.8 KiB
Bash
87 lines
3.8 KiB
Bash
#!/usr/bin/env bash
|
|
# Contract tests for lib/closes_references.sh (issue #188, term 3).
|
|
# set -u, not -e: failing commands are behavior for the harness to inspect.
|
|
set -u
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
# shellcheck source=test/harness.sh
|
|
. "$ROOT/test/harness.sh"
|
|
# issue_references (the LOCAL/CROSS classifier) lives here; closes_references
|
|
# calls it, exactly as refs_references does.
|
|
# shellcheck source=actions/issueflow-reconcile/issueflow-reconcile.sh
|
|
. "$ROOT/actions/issueflow-reconcile/issueflow-reconcile.sh"
|
|
# shellcheck source=lib/closes_references.sh
|
|
. "$ROOT/lib/closes_references.sh"
|
|
|
|
# closes <want-newline-separated> <body> — the parse of <body> is exactly
|
|
# <want>. Exact, not substring: "12" is contained in "123".
|
|
closes() {
|
|
local want="$1" body="$2" got
|
|
got="$(printf '%s' "$body" | closes_references)"
|
|
[ "$got" = "$want" ]
|
|
}
|
|
|
|
# --- the three verbs, the three tenses ----------------------------------
|
|
# GitHub's documented keyword set. All of them, because a body that says
|
|
# "Fixed #4" and goes unclosed is a silent failure of the post-merge
|
|
# transition, not a loud one.
|
|
|
|
check "closes" 0 "" closes 1 'Closes #1'
|
|
check "close" 0 "" closes 1 'Close #1'
|
|
check "closed" 0 "" closes 1 'Closed #1'
|
|
check "fixes" 0 "" closes 2 'Fixes #2'
|
|
check "fix" 0 "" closes 2 'Fix #2'
|
|
check "fixed" 0 "" closes 2 'Fixed #2'
|
|
check "resolves" 0 "" closes 3 'Resolves #3'
|
|
check "resolve" 0 "" closes 3 'Resolve #3'
|
|
check "resolved" 0 "" closes 3 'Resolved #3'
|
|
check "case-insensitive" 0 "" closes 4 'CLOSES #4'
|
|
check "lowercase" 0 "" closes 4 'closes #4'
|
|
check "colon form" 0 "" closes 5 'Closes: #5'
|
|
|
|
# --- Refs is NOT a closing link -----------------------------------------
|
|
# The relation this file must not swallow. refs_references owns Refs, and
|
|
# conflating them makes every referenced issue look closeable — the
|
|
# post-merge transition #151 was reopened by hand over exactly that
|
|
# distinction.
|
|
|
|
check "Refs is not a close" 0 "" closes '' 'Refs #7'
|
|
check "Refs and Closes in one body keeps only the close" 0 "" \
|
|
closes 8 $'Refs #7\nCloses #8'
|
|
|
|
# --- cross-repo references stay out (#61) -------------------------------
|
|
# rig#112 must never be read as local #112. The classifier is shared with
|
|
# refs_references precisely so this rule has one implementation.
|
|
|
|
check "qualified reference is not local" 0 "" closes '' 'Closes rig#112'
|
|
check "owner-qualified reference is not local" 0 "" \
|
|
closes '' 'Closes heavy-duty/rig#112'
|
|
check "a local and a cross reference keep only the local" 0 "" \
|
|
closes 9 $'Closes rig#112\nCloses #9'
|
|
|
|
# --- every occurrence contributes ---------------------------------------
|
|
# Binding to the first occurrence is the defect #184 fixed in
|
|
# blocked_reference_records; this parser must not reintroduce it.
|
|
|
|
check "two closes on one line" 0 "" closes $'1\n2' 'Closes #1. Closes #2.'
|
|
check "two closes on two lines" 0 "" closes $'1\n2' $'Closes #1\nCloses #2'
|
|
check "sorted and deduplicated" 0 "" closes $'2\n10' $'Closes #10\nCloses #2\nCloses #10'
|
|
|
|
# --- prose must not be swallowed ----------------------------------------
|
|
|
|
check "trailing prose is not part of the reference" 0 "" \
|
|
closes 12 'Closes #12, and adds the guard'
|
|
check "a sentence terminator ends the reference" 0 "" closes 13 'Closes #13.'
|
|
check "no reference means no output" 0 "" closes '' 'Closes the door behind it'
|
|
check "a bare issue mention is not a close" 0 "" closes '' 'See #14 for context'
|
|
# "unclosed" contains "close" — a naive word match would fire on it.
|
|
check "a word merely containing a verb does not fire" 0 "" \
|
|
closes '' 'This left #15 unclosed'
|
|
|
|
# --- the shapes a real PR body carries ----------------------------------
|
|
|
|
check "the template's leading declaration" 0 "" \
|
|
closes 188 $'Closes #188\n\n## Acceptance criteria\n\n- [ ] a thing'
|
|
check "an empty body yields nothing" 0 "" closes '' ''
|
|
|
|
summary
|