forked from heavy-duty/ceremony
test: expose truncated Forgejo timelines
This commit is contained in:
parent
ca7ce6e919
commit
8c0f5d53d7
1 changed files with 75 additions and 2 deletions
|
|
@ -746,6 +746,7 @@ check "no api base refuses" 1 "cannot reach the forge" \
|
|||
# .user.login -> .actor.login. Mutation-verified: collapsing add/remove or
|
||||
# emitting .user instead of .actor each reds its own case (#4853).
|
||||
timeline_stub() {
|
||||
: >"$timeline_calls"
|
||||
# shellcheck disable=SC2317 # invoked indirectly, by forge_api
|
||||
curl() {
|
||||
local hdr="" out="" url=""
|
||||
|
|
@ -753,14 +754,44 @@ timeline_stub() {
|
|||
case "$1" in -D) hdr="$2"; shift ;; -o) out="$2"; shift ;; -H) shift ;; *) url="$1" ;; esac
|
||||
shift
|
||||
done
|
||||
printf 'HTTP/1.1 200 OK\r\nX-Total-Count: %s\r\n\r\n' "${FAKE_TL_N:-2}" >"$hdr"
|
||||
printf '%s\n' "$url" >>"$timeline_calls"
|
||||
local page=1 page_body="$FAKE_TIMELINE" page_total="${FAKE_TL_N:-2}"
|
||||
case "$url" in *page=*) page="${url##*page=}"; page="${page%%&*}" ;; esac
|
||||
if [ "${#FAKE_TL_PAGES[@]}" -gt 0 ]; then
|
||||
if [ "$page" -le "${#FAKE_TL_PAGES[@]}" ]; then
|
||||
page_body="${FAKE_TL_PAGES[$((page - 1))]}"
|
||||
else
|
||||
page_body='[]'
|
||||
fi
|
||||
page_total="$(jq 'length' <<<"$page_body")"
|
||||
fi
|
||||
{
|
||||
printf 'HTTP/1.1 200 OK\r\n'
|
||||
[ "${FAKE_TL_HEADERS:-yes}" = no ] || printf 'X-Total-Count: %s\r\n' "$page_total"
|
||||
printf '\r\n'
|
||||
} >"$hdr"
|
||||
case "$url" in
|
||||
*timeline*) printf '%s' "$FAKE_TIMELINE" >"$out" ;;
|
||||
*timeline*) printf '%s' "$page_body" >"$out" ;;
|
||||
*) printf '[]' >"$out" ;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
}
|
||||
timeline_page() {
|
||||
jq -nc --argjson first "$1" --argjson count "$2" '
|
||||
[range($first; $first + $count)
|
||||
| {
|
||||
type: "label",
|
||||
body: "1",
|
||||
user: {login: "setter"},
|
||||
label: {name: "needs-ruling"},
|
||||
created_at: ("event-" + tostring)
|
||||
}]
|
||||
'
|
||||
}
|
||||
timeline_calls="$TMP/timeline_calls"
|
||||
FAKE_TL_PAGES=()
|
||||
FAKE_TL_HEADERS=yes
|
||||
FAKE_TIMELINE='[
|
||||
{"type":"label","body":"1","user":{"login":"setter"},"label":{"name":"needs-ruling"},"created_at":"2026-08-02T14:58:13Z"},
|
||||
{"type":"label","body":"","user":{"login":"setter"},"label":{"name":"needs-ruling"},"created_at":"2026-08-02T15:22:22Z"},
|
||||
|
|
@ -777,6 +808,48 @@ check "forge_timeline drops non-label events" 0 "" \
|
|||
test "$(jq '[.[] | select(.event == null or .event == "")] | length' <<<"$tl")" = 0
|
||||
check "forge_timeline uses .actor.login, not a bare .user" 0 "" \
|
||||
jq -e 'all(.[]; has("actor") and (.user|not))' <<<"$tl" >/dev/null
|
||||
|
||||
# Forgejo's timeline endpoint lies consistently: x-total-count echoes the
|
||||
# current page size. With 151 events its pages declare 50, 50, 50 and 1, so
|
||||
# strict pagination stops successfully after page 1 and drops the newest 101
|
||||
# events. Exhaustion is safe only here because timelines are append-only.
|
||||
FAKE_TL_PAGES=(
|
||||
"$(timeline_page 1 50)"
|
||||
"$(timeline_page 51 50)"
|
||||
"$(timeline_page 101 50)"
|
||||
"$(timeline_page 151 1)"
|
||||
)
|
||||
timeline_stub
|
||||
tl="$(REPO=o/r forge_timeline 188)"
|
||||
check "forge_timeline exhausts all pages despite per-page total headers" 0 "" \
|
||||
test "$(jq 'length' <<<"$tl")" = 151
|
||||
check "forge_timeline retains the newest event beyond page one" 0 "" \
|
||||
jq -e 'any(.[]; .created_at == "event-151")' <<<"$tl" >/dev/null
|
||||
|
||||
# A collection exactly divisible by the page size needs one final empty read;
|
||||
# stopping after the second full page cannot prove exhaustion.
|
||||
FAKE_TL_PAGES=("$(timeline_page 1 50)" "$(timeline_page 51 50)")
|
||||
timeline_stub
|
||||
exhaustive_count="$(REPO=o/r forge_api --paginate-exhaustive 'repos/o/r/issues/188/timeline' --jq 'length')"
|
||||
check "exhaustive pagination terminates after an empty page" 0 "" \
|
||||
test "$exhaustive_count" = 100
|
||||
check "an exactly-full exhaustive gather reads the empty third page" 0 "" \
|
||||
test "$(wc -l <"$timeline_calls")" = 3
|
||||
|
||||
# The exhaustive path's completeness proof is the short page itself; it must
|
||||
# never consult the endpoint's missing or dishonest total header.
|
||||
FAKE_TL_PAGES=("$(timeline_page 1 50)" "$(timeline_page 51 1)")
|
||||
FAKE_TL_HEADERS=no
|
||||
timeline_stub
|
||||
check "exhaustive pagination needs no x-total-count header" 0 "" \
|
||||
eq 51 forge_api --paginate-exhaustive 'repos/o/r/issues/188/timeline' --jq 'length'
|
||||
FAKE_TL_HEADERS=yes
|
||||
|
||||
check "strict and exhaustive pagination are mutually exclusive" 1 "mutually exclusive" \
|
||||
forge_api --paginate --paginate-exhaustive 'repos/o/r/issues/188/timeline'
|
||||
check "exhaustive pagination refuses a non-GET method" 1 "GET" \
|
||||
forge_api --paginate-exhaustive --method POST 'repos/o/r/issues/188/timeline'
|
||||
|
||||
# Unreadable: curl fails. Status must surface through forge_timeline itself
|
||||
# (not a later jq), or the ruling ladder invents a verdict on a half-read.
|
||||
# shellcheck disable=SC2317
|
||||
|
|
|
|||
Loading…
Reference in a new issue