ErrFormat refusal path has no test at all
A database file carries a layout version, const Format uint16 = 1
(internal/pager/pager.go:63), separate from the eight-byte Magic. When a
build meets a file whose Format it does not read, it must refuse politely and say what to
do. That refusal is written and shipped. It has never been run.
The refusal exists, in both readers, carrying both numbers:
// internal/pager/led.go:239 (the encrypted reader)
return Meta{}, fmt.Errorf("%w: file says %d, this build reads %d", ErrFormat, format, Format)
// internal/pager/pager.go:573 (the plain reader)
return Meta{}, fmt.Errorf("%w: file says %d, this build reads %d", ErrFormat, format, Format)
The measurement does not:
grep -rn --include='*_test.go' -F "ErrFormat" . returns 0.ErrNotSapedb — the sibling refusal, raised three lines away — returns
4. There are 76 _test.go files in the tree and four of them are in
internal/pager (pager_test.go, led_test.go,
crypt_test.go, freelist_test.go).pager.go:119 (the declaration),
pager.go:306 (Open deciding a retry is pointless),
pager.go:573,576, led.go:239,242. Six sites, no exercise.pages are %d bytes, this build uses %d —
is untested for the same reason.Does it lose data, break a promise on a public surface, or stop somebody installing and running? Yes — the first one, on the path that matters most.
This is the refusal that stands between a future build and a file it cannot read. If it does not fire,
a build reads a file laid out under different rules as if it were its own: not "not found", not an
error, just a database answering questions wrongly. That is the data-loss shape, and it is the exact
shape checkOldExtension exists to prevent for the other half of the same problem —
and that one has tests.
It is also load-bearing for SAPE-6, which now lists this issue as its blocker. The upgrade rehearsal is "one build writes, the next reads it all back", and the negative half of that rehearsal — the next build declining a file it should decline — is exactly this path. SAPE-6 cannot be called done on top of an untested refusal, so this is not extra scope; it is SAPE-6's floor.
And the work is small, which is why it is in rather than deferred: the refusal is built, both numbers
are already in the sentence, and what is missing is a case that writes a header with a
Format of 2 and asserts errors.Is(err, ErrFormat) plus the two numbers in
the message — for the plain reader and the encrypted one, since they are two copies of the same
sentence.
Format is bumped, this refusal is the
only thing between those files and a build that misreads them.