test(send): cover the last gate before a message reaches Discord #275

Merged
coilysiren merged 1 commit from test/send-bounds into main 2026-08-13 06:07:31 +00:00
Member

Tests only. Found by a coverage sweep during a quiet period, not from a report.

Why these two

go test -cover on internal/community reports 76.1%. Most of the uncovered set needs a live discordgo session — Run, onReady, onMessage, React — which is defensible. Two are pure functions on the send path with zero coverage:

  • truncateRunes — called from six sites across the reply, command, and job paths. It is the last thing that touches a message before it leaves the process.
  • displayName — names the transcript author on every Discord turn.

What lands

Boundary on both sides: under, exactly at, one over, far over, empty. One over is the case that matters — it proves the ellipsis replaces a rune rather than joining it, so a truncated value lands exactly on the limit rather than one past it.

Rune counting asserted with accented and emoji input, the same property the turn contract already holds for input.

Tiny limits, because they take a different branch: at 1 and 0 there is no room for both a rune and the ellipsis.

displayName precedence — nickname, then global name, then username, then the "member" fallback — including the blank-string cases, which are the ones the TrimSpace guards exist for.

Verified by mutation

Slicing to limit instead of limit-1:

--- FAIL: TestTruncateRunesLandsExactlyOnTheLimit
    one over: 11 runes, want 10
    far over: 11 runes, want 10

One characterization, deliberately not filed

A negative limit panics on the slice — slice bounds out of range. I did not file it: no caller passes one, all six sites pass a constant, and a latent panic with no reachable path is noise on the tracker.

It is worth writing down, though, because the failure mode is bad if it ever becomes reachable — a computed limit would find it at runtime, in the path that sends the member their reply. The test says so, and flips if it is ever made total.

One thing I could not settle

truncateRunes bounds at 1990 runes, and the constant's comment says it "sits under Discord's own 2000". Measured: 10 emoji runes are 19 UTF-16 code units. If Discord's 2000 limit counts UTF-16 rather than code points, a heavily-emoji reply could pass this gate and be rejected by the API.

I am not claiming that is a defect — I do not know how Discord counts, and I cannot test it without sending a live message. Recording it because #203 and #221 both push toward more emoji, which is what would make the gap reachable. One 1990-emoji message to a test channel settles it.

Coverage goes 76.1% → 76.4%. go vet, gofmt, full go test ./..., pre-commit run --files clean.


Quail (QA)

Tests only. Found by a coverage sweep during a quiet period, not from a report. ## Why these two `go test -cover` on `internal/community` reports **76.1%**. Most of the uncovered set needs a live `discordgo` session — `Run`, `onReady`, `onMessage`, `React` — which is defensible. Two are pure functions on the send path with **zero coverage**: - **`truncateRunes`** — called from six sites across the reply, command, and job paths. It is the last thing that touches a message before it leaves the process. - **`displayName`** — names the transcript author on every Discord turn. ## What lands **Boundary on both sides:** under, exactly at, one over, far over, empty. *One over* is the case that matters — it proves the ellipsis **replaces** a rune rather than joining it, so a truncated value lands exactly on the limit rather than one past it. **Rune counting** asserted with accented and emoji input, the same property the turn contract already holds for input. **Tiny limits**, because they take a different branch: at 1 and 0 there is no room for both a rune and the ellipsis. **`displayName` precedence** — nickname, then global name, then username, then the `"member"` fallback — including the blank-string cases, which are the ones the `TrimSpace` guards exist for. ## Verified by mutation Slicing to `limit` instead of `limit-1`: ``` --- FAIL: TestTruncateRunesLandsExactlyOnTheLimit one over: 11 runes, want 10 far over: 11 runes, want 10 ``` ## One characterization, deliberately not filed A **negative limit panics** on the slice — `slice bounds out of range`. I did not file it: no caller passes one, all six sites pass a constant, and a latent panic with no reachable path is noise on the tracker. It is worth *writing down*, though, because the failure mode is bad if it ever becomes reachable — a computed limit would find it at runtime, in the path that sends the member their reply. The test says so, and flips if it is ever made total. ## One thing I could not settle `truncateRunes` bounds at 1990 **runes**, and the constant's comment says it "sits under Discord's own 2000". Measured: 10 emoji runes are **19 UTF-16 code units**. If Discord's 2000 limit counts UTF-16 rather than code points, a heavily-emoji reply could pass this gate and be rejected by the API. **I am not claiming that is a defect** — I do not know how Discord counts, and I cannot test it without sending a live message. Recording it because https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/203 and https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/221 both push toward more emoji, which is what would make the gap reachable. One 1990-emoji message to a test channel settles it. Coverage goes 76.1% → 76.4%. `go vet`, `gofmt`, full `go test ./...`, `pre-commit run --files` clean. --- Quail (QA)
test(send): cover the last gate before a message reaches Discord
All checks were successful
ci / test (pull_request) Successful in 28s
ci / publish-echo-image (pull_request) Has been skipped
ci / image-build (pull_request) Successful in 25s
00381af56f
Found by a coverage sweep rather than a report. truncateRunes is called
from the reply, command, and job paths — six sites, all bounding what
actually leaves the process — and had no test. displayName names the
transcript author on every Discord turn and had none either.

Boundary held on both sides: under, exactly at, one over, far over, and
empty. One over is the case that proves the ellipsis replaces a rune
rather than joining it, so a truncated value lands exactly on the limit
instead of one past it.

Rune counting asserted with accented and emoji input, the same property
the turn contract already holds for input.

Tiny limits covered because they take a different branch: at one and zero
there is no room for both a rune and the ellipsis.

A negative limit panics on the slice. Recorded as characterization rather
than filed — no caller passes one, every site passes a constant, and the
value of writing it down is that a computed limit would otherwise find it
at runtime in the send path.

Verified by mutation. Slicing to limit rather than limit-1 fails with
"one over: 11 runes, want 10".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/sirens-echo!275
No description provided.