Unit test coverage: wands/staves (game/sticks.go, 534 lines, one test) #6

Closed
opened 2026-08-09 03:41:09 +02:00 by clawbot · 1 comment
Collaborator

Problem

game/sticks.go is the largest under-tested file in the repo: 534 lines,
23 functions, and exactly one test
(TestZapSlowMonster). Every other zap
effect, the bolt-bouncing geometry, and the level-drain logic are untested.

Second of three units splitting the TODO.md Next Step into commit-sized
pieces.

Untested surface

Per-effect handlers, all unexercised except speed/slow:
zapLight, zapDrainLife, zapInvisibility, zapPolymorph,
zapCancellation, zapTeleport, zapMagicMissile, zapBolt.

Supporting logic, all unexercised:

  • fireBolt — bolt propagation, the highest-risk code here.
  • boltDirChar / boltBounces — the /, \, |, - glyph selection and
    the wall-bounce reflection rules. Pure geometry over a direction vector;
    cheap to test exhaustively and easy to get subtly wrong.
  • boltStrikesMonster / boltStrikesHero — including the case where a
    bounced bolt hits the player who fired it.
  • drain / drainReaches — which monsters are in range for drain-life,
    including the in-passage and cross-room cases.
  • fixStick, chargeStr — charge accounting and display.

Definition of done

  1. A new game/sticks_test.go covering, at minimum:
    • boltDirChar for all eight direction vectors.
    • boltBounces for the reflection cases in both wall orientations,
      including a corner.
    • At least one end-to-end fireBolt through a constructed level asserting
      the path taken and the final resting position.
    • A bounced bolt striking the hero who fired it.
    • drainReaches for: same room, different room, in-passage.
    • Each remaining zap handler asserting its characteristic state change
      (e.g. zapPolymorph replaces the monster type; zapCancellation clears
      the monster's special flags; zapTeleport moves the target).
    • fixStick charge accounting and chargeStr output for a known and an
      unknown stick.
  2. Deterministic: fixed seeds for anything touching the RNG, following the
    existing tests' pattern.
  3. make check fully green.
  4. TODO.md updated in the same commit (narrow the Next Step; do not mark it
    complete — the wizard-command third remains).
  5. Commit title ends with (closes #N).

Implementation requirements

  • Cross-check expected behavior against the C reference
    (git show origin/c-master:sticks.c). Do NOT check out or modify
    origin/c-master. Do not simply assert current Go behavior — the value of
    these tests is catching divergence from C, and a test written by reading the
    Go code proves nothing.
  • If you find a real divergence from C, stop and report it. Do not fix
    gameplay in a test-coverage commit and do not encode the bug as expected.
  • Beware the process-exit constraint: a bolt can kill the hero, and death
    calls os.Exit, which would kill the test binary. Use the existing
    fortify()-style HP-pinning pattern from the playtest tests where needed.
  • Tests must call t.Parallel(); keep the lint run at 0 issues.
  • White-box tests need the
    //nolint:testpackage // white-box tests reach unexported state (approved 2026-07-07)
    header.
  • make targets only. Do NOT modify .golangci.yml. No game behavior changes.
  • If the file grows unwieldy, splitting bolt-geometry tests into their own
    file is acceptable — but keep it to this one commit.
  • Never mention Claude or Anthropic anywhere.
## Problem `game/sticks.go` is the largest under-tested file in the repo: **534 lines, 23 functions, and exactly one test** (`TestZapSlowMonster`). Every other zap effect, the bolt-bouncing geometry, and the level-drain logic are untested. Second of three units splitting the `TODO.md` Next Step into commit-sized pieces. ## Untested surface Per-effect handlers, all unexercised except speed/slow: `zapLight`, `zapDrainLife`, `zapInvisibility`, `zapPolymorph`, `zapCancellation`, `zapTeleport`, `zapMagicMissile`, `zapBolt`. Supporting logic, all unexercised: - `fireBolt` — bolt propagation, the highest-risk code here. - `boltDirChar` / `boltBounces` — the `/`, `\`, `|`, `-` glyph selection and the wall-bounce reflection rules. Pure geometry over a direction vector; cheap to test exhaustively and easy to get subtly wrong. - `boltStrikesMonster` / `boltStrikesHero` — including the case where a bounced bolt hits the player who fired it. - `drain` / `drainReaches` — which monsters are in range for drain-life, including the in-passage and cross-room cases. - `fixStick`, `chargeStr` — charge accounting and display. ## Definition of done 1. A new `game/sticks_test.go` covering, at minimum: - `boltDirChar` for **all eight** direction vectors. - `boltBounces` for the reflection cases in both wall orientations, including a corner. - At least one end-to-end `fireBolt` through a constructed level asserting the path taken and the final resting position. - A bounced bolt striking the hero who fired it. - `drainReaches` for: same room, different room, in-passage. - Each remaining zap handler asserting its characteristic state change (e.g. `zapPolymorph` replaces the monster type; `zapCancellation` clears the monster's special flags; `zapTeleport` moves the target). - `fixStick` charge accounting and `chargeStr` output for a known and an unknown stick. 2. Deterministic: fixed seeds for anything touching the RNG, following the existing tests' pattern. 3. `make check` fully green. 4. `TODO.md` updated in the same commit (narrow the Next Step; do not mark it complete — the wizard-command third remains). 5. Commit title ends with ` (closes #N)`. ## Implementation requirements - **Cross-check expected behavior against the C reference** (`git show origin/c-master:sticks.c`). Do NOT check out or modify `origin/c-master`. Do not simply assert current Go behavior — the value of these tests is catching divergence from C, and a test written by reading the Go code proves nothing. - If you find a real divergence from C, **stop and report it**. Do not fix gameplay in a test-coverage commit and do not encode the bug as expected. - Beware the process-exit constraint: a bolt can kill the hero, and death calls `os.Exit`, which would kill the test binary. Use the existing `fortify()`-style HP-pinning pattern from the playtest tests where needed. - Tests must call `t.Parallel()`; keep the lint run at 0 issues. - White-box tests need the `//nolint:testpackage // white-box tests reach unexported state (approved 2026-07-07)` header. - `make` targets only. Do NOT modify `.golangci.yml`. No game behavior changes. - If the file grows unwieldy, splitting bolt-geometry tests into their own file is acceptable — but keep it to this one commit. - Never mention Claude or Anthropic anywhere.
Author
Collaborator

Implemented in PR #35 (branch test/sticks-coverage, commit dda90bf).

game/sticks_test.go and game/bolt_test.go cover every item in the definition of done: boltDirChar for all eight directions, boltBounces in both wall orientations plus a corner (and diagonally off a wall, which is what separates C's negate-both-components rule from a surface reflection), an end-to-end fireBolt asserting the path and resting square, a bounced bolt striking the hero who fired it, drainReaches for same-room / other-room / in-passage / door-under-hero, every remaining zap handler with its characteristic state change, and fixStick / chargeStr. TODO.md has the Completed Steps entry; the Next Step was not rotated, since the wizard-command third (#7) remains.

Expectations were read from git show origin/c-master:sticks.c, not from the Go code. No divergence from C was found, so no game code changed. Two pieces of C archaeology are recorded in the test comments: fire_bolt's ch != 'M' guard on the miss message is a tautology (winat is t_disguise when a monster stands there, rogue.h 57), and zapCancellation's t_disguise = t_type is an identity for every monster a zap ray can stop on, because a xeroc's item disguise is step_ok and the ray walks past it.

Verification: make check green (fmt-check, golangci-lint 0 issues with a private lint cache, go test -timeout 30s -race -cover), and 27 mutations of game/sticks.go were each shown to make the targeted test fail — including the door-under-hero guard, whose loss makes the test hang rather than fail, which is the only signal it can have. The full breakdown is in the PR comment.

Implemented in PR #35 (branch `test/sticks-coverage`, commit `dda90bf`). `game/sticks_test.go` and `game/bolt_test.go` cover every item in the definition of done: `boltDirChar` for all eight directions, `boltBounces` in both wall orientations plus a corner (and diagonally off a wall, which is what separates C's negate-both-components rule from a surface reflection), an end-to-end `fireBolt` asserting the path and resting square, a bounced bolt striking the hero who fired it, `drainReaches` for same-room / other-room / in-passage / door-under-hero, every remaining zap handler with its characteristic state change, and `fixStick` / `chargeStr`. `TODO.md` has the Completed Steps entry; the `Next Step` was **not** rotated, since the wizard-command third (#7) remains. Expectations were read from `git show origin/c-master:sticks.c`, not from the Go code. **No divergence from C was found**, so no game code changed. Two pieces of C archaeology are recorded in the test comments: `fire_bolt`'s `ch != 'M'` guard on the miss message is a tautology (`winat` _is_ `t_disguise` when a monster stands there, `rogue.h` 57), and `zapCancellation`'s `t_disguise = t_type` is an identity for every monster a zap ray can stop on, because a xeroc's item disguise is `step_ok` and the ray walks past it. Verification: `make check` green (fmt-check, golangci-lint 0 issues with a private lint cache, `go test -timeout 30s -race -cover`), and 27 mutations of `game/sticks.go` were each shown to make the targeted test fail — including the door-under-hero guard, whose loss makes the test hang rather than fail, which is the only signal it can have. The full breakdown is in the PR comment.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/rgoue#6