test(chapters): the five gaps a coverage pass found, and two vacuous tests

A pass counting tests per file rather than per feature found five pieces of
this work with none. One of them mattered.

MAX_PROPOSAL_BYTES was enforced and never tested, on either side. The inbound
check is the half that counts -- the proposal is the one place a remote party
decides how much work everybody else does -- and it had nothing at all. Four
tests now, propose and accept, bytes and count, in SignedGroupKeyStateTest
where the adversarial inbound tests belong. The count cap's accept side was
untested too, although the propose-side test's own comment already claimed
otherwise.

Two of those tests passed vacuously as first written, which is the part worth
keeping. acceptProposal rebuilds every event from its own fields under the
room's path and drops the whole proposal if an id does not match, so a
hand-built event with an invented id is refused whatever its size -- and an
assertNull then stays green with the guard deleted. authoredProposal fixes it
by taking its events from real proposals, so their ids are the ones the
manager computes, and a control asserts that a well-formed proposal of the
same shape does open a session. That control failed on its first run, which is
how the vacuity was found; commenting the guard out now fails exactly the test
named after it.

UploadChaptersScreen had no UI test, against a project with fifteen
runDesktopComposeUiTest files that tests other screens this way. Ten now: the
cost of an upload stated before the picker opens, the index and name per row,
every per-row reason rendering, and the FAB counting only what it would
actually propose rather than how many files were picked.

canPropose, kibibytes and the chapter summary had none either, each being read
only by a composable. Eleven, five and five.

Still open, and not closable here: the three FilePicker actuals need
instrumentation this project does not have, and on iOS a host that can compile
them.

1030 tests, no failures; m3Audit's budgets all met.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kgothatso Ngako
2026-10-02 20:20:12 +02:00
parent a0bad7c8da
commit 306b8bb150
6 changed files with 765 additions and 1 deletions

View File

@@ -987,7 +987,38 @@ and stays retryable, and a second pass after the key arrives proposes all three
numbered from zero. The failed pass spent no indices, because it created
nothing.
Total: 995 tests, and the counts per phase are in each commit.
Total: 1030 tests, and the counts per phase are in each commit.
### The five gaps a coverage pass found afterwards
Counting tests per file rather than per feature turned up five things with none,
and one of them mattered:
- **`MAX_PROPOSAL_BYTES` was enforced and never tested, on either side.** The
inbound check is the half that counts — the proposal is where a remote party
decides how much work everybody else does — and it had nothing. Now four
tests, propose and accept, bytes and count, in `SignedGroupKeyStateTest` where
the adversarial inbound tests live. The count cap's accept side was missing
too, although the propose-side test's own comment already claimed it.
- **Two of those tests passed vacuously when first written**, which is the part
worth remembering. `acceptProposal` rebuilds every event under the room's path
and drops the proposal if an id does not match its fields, so a hand-built
event with an invented id is refused whatever its size — and an `assertNull`
then passes with the cap deleted. The fix is `authoredProposal`, which takes
its events from real proposals so the ids are the ones the manager computes,
plus a control asserting that a well-formed proposal of the same shape *does*
open a session. The control failed on the first run, which is how this was
found; commenting the guard out now fails exactly the test named after it.
- **`UploadChaptersScreen` had no UI test**, against a project that has fifteen
`runDesktopComposeUiTest` files and tests other screens this way. Ten now: the
cost stated before the picker, the index and name per row, every per-row
reason rendering, and the FAB counting only what it would actually propose.
- **`canPropose`, `kibibytes` and the chapter summary** had none either, being
read only by composables. Eleven, five and five.
The gap that stays open is the three `FilePicker` actuals, which need
instrumentation this project does not have — and on iOS a host that can compile
them.
Rollout needs no flag. The chapter and chunk kinds, their tags and their apply
branches are unchanged, so an older build receives an uploaded chapter as the