Skip to content

Overnight/test harness 2026 04 20#5

Merged
claudenstein merged 87 commits into
mainfrom
overnight/test-harness-2026-04-20
Apr 27, 2026
Merged

Overnight/test harness 2026 04 20#5
claudenstein merged 87 commits into
mainfrom
overnight/test-harness-2026-04-20

Conversation

@claudenstein

Copy link
Copy Markdown
Owner

No description provided.

Frank Dosk and others added 30 commits April 26, 2026 13:58
Two new tests:
  - TestPauseTorrentIdempotent calls PauseTorrent twice on
    the same torrent; the second call must take the
    `if already { return nil }` fast path.
  - TestRemoveTorrentUnknownInfoHashErrors passes an
    unrecognised infohash so the handleByHex error propagates
    cleanly rather than panicking.

PauseTorrent 93.3% → 100%; RemoveTorrent 92.9% → 100%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pass an unregistered infohash so the inner handleByHex call
returns an error; SetFilePriority must surface that without
attempting to call into anacrolix on a nil handle.
SetFilePriority 92.9% → 100%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mark a peer capable, plug in a scriptedSender that
synthesises a Result with one hit via HandleMessage on the
fake reply path. The HTTP /search response must carry the
hit translated into a SwarmHit (Name, Size, Seeders, etc.).
handleSearch 90.7% → 100%; package 98.4% → 99.5%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two manifest tests in a single file: load a manifest whose JSON
has 'entries' set to null (Unmarshal leaves m.Entries nil → exercises
the rebuild guard) and a Save where the parent dir loses write
permission so os.WriteFile on the tmp path fails.

LoadOrCreateManifest 94.7%→100%. Save remains 92.9% — the residual
gap is the json.MarshalIndent error which is unreachable for the
manifest's struct shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… arms

Two reachable archive.go gaps:

  - claims-by-extension switch: Dispatch normally pre-fills MIME from
    filepath.Ext, so the .zip/.tar/.tgz/.tar.gz arms were dead. Pass
    MIME='application/octet-stream' to bypass mimeFromPath and force
    the extension switch to fire.
  - zipMemberNames: archive/zip lets a writer create an entry with
    name "", which surfaces as f.Name=="" on read and must be
    filtered out before joining names. The existing 'skip directory'
    test claimed to cover empty names but didn't actually plant one.

zipMemberNames 90.9%→100%, init 81.8%→100%, archive.go Extract steady
at 90.5% (residual is io.ReadAll error on a real reader, unreachable).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…F arms

Five reachable branches across the docx and exif extractors:

  - docx empty document.xml (whitespace-only text run): exercises
    the 'TrimSpace(text) == \"\"' early-return.
  - docx malformed XML: drives extractDocumentText to surface a
    decode error rather than returning empty text.
  - exif JPEG with no APP1 segment: hits the
    findExifTIFF !found arm (returns nil, nil).
  - exif APP1 with TIFF whose byte-order marker is neither II nor
    MM: drives parseTIFF's bad-BOM error through Extract.
  - exif APP1 with valid TIFF but zero entries: emits no known
    tags so the assembled text is empty and Extract returns nil.

docx Extract 77.8%→85.2%, exif Extract 78.6%→89.3%, parseTIFF
93.3%→95.5%, parseIFD 95.0%→96.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EPUB Extract whitespace-only-chapters arm: an EPUB whose every
chapter body is empty/whitespace returns (nil, nil) rather than a
chunk holding empty text.

RTF control-word arms: one synthesised body chains \\par, \\line,
\\sect, \\page, \\tab, \\emdash, \\endash, \\lquote, \\rquote,
\\ldblquote, \\rdblquote, \\bullet, \\'a9 hex escape, plus the
literal \\\\ \\{ \\} escapes. Each glyph appearing in the output
proves its switch arm fired.

Plus the application/rtf MIME-claim path (text/rtf is owned by
the plaintext extractor first, so this hits the application/rtf
half of the OR).

EPUB Extract 84.8%→87.0%, RTF Extract 69.8%→92.1%, RTF init
83.3%→100%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rrors

Five reachable arms across chunker.go and subtitle.go:

  - chunkText with targetBytes <= 0 falls back to
    DefaultChunkTargetBytes (compared head-to-head with an
    explicit-default call).
  - chunkText two-paragraph join: with target sized so both
    paragraphs fit, the WriteString("\\n\\n") arm fires.
  - chunkByLine flush-before-oversized-line: a paragraph whose
    later line exceeds maxChunk after content was already
    buffered triggers the cur.Reset() flush arm.
  - subtitle dialog line that is entirely markup
    (e.g. "<i></i>") strips to empty and gets skipped.
  - subtitle scanner.Err() propagation: an io.Reader that errors
    after returning valid bytes surfaces as an Extract error.

chunkText 93.8%→100%, chunkByLine 93.3%→100%, subtitle Extract
92.6%→100%, package coverage 87.4%→89.0%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plant a regular file at the index path before calling
OpenWithLogger; Bleve's create call fails (the path exists as
non-index data) so the wrapped error propagates.

OpenWithLogger 77.3%→81.8%. Residual gaps are the schema-
rebuild's intermediate Close/RemoveAll/re-create failures, which
require corrupting state mid-rebuild and aren't deterministic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two reachable guards:

  - companion.OpenBTree's RootPieceIndex != 0 check: build a real
    signed tree, decode the trailer, mutate RootPieceIndex to 1,
    re-sign with the same key, splice the doctored trailer back
    in. OpenBTree must reject after VerifyTrailerSig passes.
  - swarmsearch.BuildRecordsFrame's len(recs) > MaxRecordsPerMessage
    cap: pass MaxRecordsPerMessage+1 records and expect the
    'exceeds cap' error rather than an oversize wire frame.

OpenBTree 93.8%→100%, BuildRecordsFrame 92.9%→100%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
HandleMessage's routing path catches malformed bencode at
peekHeader, so the inner DecodeQuery err arm in handleQuery is
unreachable from the public entry point. The internal test
calls handleQuery directly with non-bencode bytes; the function
must return cleanly without panicking and without invoking the
reply closure.

handleQuery 90.2%→95.1%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
engine.New treats failures from reputation.LoadOrCreateBloom and
LoadOrCreateTracker as non-fatal — the engine still comes up
without the spam-resistance state. The two err arms log a Warn
and continue; both were uncovered.

Plant a directory at the bloom and reputation paths (each
containing a blocker file so ReadFile fails with a non-NotExist
error). engine.New must succeed.

engine.New 73.3%→74.8%, engine package 85.5%→85.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two more non-fatal warn arms in engine.New:

  - loadSession with corrupt JSON: plant '{not json' at the
    canonical session.json path; engine.New must continue with
    an empty session rather than fail.
  - identity.LoadOrCreate with a directory at the identity path:
    LoadOrCreate's IsDir guard fires; engine.New's warn arm
    swallows the error so the engine still starts (just without
    DHT publisher capability).

engine.New 73.3%→77.0%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Default tests leave HTTPUserAgent empty so the
'if cfg.HTTPUserAgent != \"\" { tc.HTTPUserAgent = ... }' branch
in engine.New stays cold. Pass a custom UA in cfg and verify the
engine boots cleanly. No public accessor for the resulting
torrent.Client UA, so a clean New + Close is enough proof.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Six previously-uncovered FLAC arms now exercised by hand-crafted
fLaC payloads:

  - parseVorbisComment len(b) < 4 short-circuit
  - vendor length overruns the vorbis body
  - per-comment length overruns the body
  - per-comment branches: missing '=', empty value, unknown
    Vorbis field name (vorbisLabel returns ""), known field
    (TITLE) surfaces in output
  - VORBIS_COMMENT block whose declared bodyLen exceeds the
    bytes that actually follow → readFull error
  - non-VORBIS block whose declared bodyLen exceeds the bytes
    that follow → br.Discard error

flac.Extract 78.6%→93.8%, parseVorbisComment 75%→100%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A JPEG containing a comment (COM) segment followed by EOI takes
findExifTIFF's 'i = bodyEnd' advance branch — the segment fits
but isn't EXIF, so the loop must skip past its body and continue
checking. Without this case the only path through findExifTIFF
either matched an EXIF APP1 immediately or bailed out on a
truncated segment.

findExifTIFF 87.5%→91.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
writeTorrentCopy is a best-effort persistence step in
AddTorrentFile — if it fails, the engine logs a warn and the add
still succeeds. Force the failure by chmod 0o500 on the
torrents/ subdirectory after engine.New has created it; the
subsequent AddTorrentFile must still return a non-nil handle.

Skipped on Windows and as root (chmod semantics).

AddTorrentFile 90.0%→93.3%, engine package 85.5%→86.2%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The existing partial-failure test (failNth=4) trips on the
SECOND IndexTorrent call, which exercises the IndexTorrent err
arm but not the IndexContent err arm. failNth=2 trips on the
first IndexContent call instead so the inner content-loop
error path fires.

subscriber.ingest 96.0%→100%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both Save calls in Close are non-fatal — failures are logged
and Close still completes. Strip write permission on the bloom
file's and reputation file's parent directories AFTER engine.New
has wired them up; the in-Close Save then fails on tmp WriteFile,
exercising the previously-uncovered warn arms.

Skipped on Windows + as root.

engine.Close 84.0%→92.0%, engine package 85.7%→86.3%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AllTorrentDocs pages through results 1000 at a time. With <1000
docs in the index the second-iteration logic ('from += batch')
never fires. Index 1001 torrent docs and verify the returned
slice is complete; this exercises both the 1000-doc first batch
and the 1-doc tail.

Skipped under -short because indexing 1001 Bleve docs takes
~20s on the test box.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…Bytes arms

Four reachable Extract arms in zim.go:

  - dir entry mimeIdx == zimRedirectMime/zimDeletedMime: use a
    helper to patch a built ZIM's dir entry mimeIdx to 0xFFFF
    (redirect) and verify the loop's IsRedirect skip fires.
  - dir entry mimeIdx >= len(mimes): same helper writes an
    out-of-range mimeIdx; bounds-check skip arm fires.
  - non-extractable MIME (image/png): the only article's MIME
    fails zimIsExtractableMime → no chunks returned.
  - empty decoded text: a whitespace-only article body decodes
    to "" via zimDecodeArticle and is skipped.
  - emitted >= maxBytes break: small cap below the first
    article's length prevents the second from being emitted.

zim Extract 68.6%→78.4%, readZimDirEntry 66.7%→75.0%, package
coverage 89.6%→89.9%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three more reachable arms in zim.go:

  - dispatch ext fallback: pass MIME='application/octet-stream'
    so the registered claim function reaches the
    'EqualFold(filepath.Ext(c.Path), \".zim\") return true' arm
    rather than matching the MIME-prefix check above.
  - cluster type 4 (XZ/LZMA2): patch a built ZIM's cluster type
    byte; readZimCluster surfaces the 'not supported in v1'
    error which Extract swallows via continue.
  - unknown cluster type (e.g. 6): patch byte to value outside
    {1, 2, 4} so readZimCluster's default-arm error fires.

zim init 83.3%→100%, readZimCluster 48.8%→53.5%, package
89.6%→90.2%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ApplyNeed accepts up to MaxNeedIDsPerMessage (1000) IDs, but
BuildRecordsFrame's record cap is MaxRecordsPerMessage (500).
A sync_need that resolves to 501+ records reaches the overflow
arm in onSyncNeed and the function must return without sending
sync_records.

Build 501 distinct LocalRecords, register a responder session,
and dispatch the sync_need: assert reply is never invoked.

onSyncNeed 84.6%→100%, swarmsearch coverage 98.2%→98.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When cfg.SeedListPath is set, engine.New calls
tr.LoadSeedList and surfaces a log.Info on n>0 plus a warn for
each per-entry parse error. Default tests leave SeedListPath
empty so both arms are cold.

Plant a seed list with one valid 32-byte pubkey and one
'not-hex' bad entry. The valid one imports (n=1 fires the
'engine.seed_list_loaded' info), the bad one routes through
the warn loop ('engine.seed_list_err').

engine.New 77.0%→81.5%, engine package 86.3%→86.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same shape as bloom/reputation/identity load tolerance: plant a
directory at cfg.TrustPath so trust.LoadOrCreate fails on a
non-NotExist read. engine.New must continue with TrustStore=nil.

engine.New 81.5%→82.2%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The arm fires when the FIRST ContentDoc for a file has empty
FilePath but a LATER ContentDoc for the same FileIndex has a
non-empty one. Bleve's ContentDocsForInfoHash returns docs in
non-deterministic order, so a black-box test can't pin the
sequence. Switch to an internal test that calls
collectFileRecords directly with the exact ordered slice.

collectFileRecords 96.4%→100%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The existing pause tests add via magnet (no metadata), so the
pre-metadata branch covers `paused`; the post-metadata switch
arm stays cold. Use AddTorrentMetaInfo so metadata is present
immediately, pause the torrent, and verify the snapshot reports
Status="paused".

snapshotOf 86.2%→89.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Six getZimBlob err arms were uncovered. Internal tests pass
hand-crafted cluster bytes directly so each guard can fire
independently:

  - empty cluster (len < 1) → 'missing type byte'
  - body shorter than offsetSize → 'body shorter than first offset'
  - first offset 0 or non-aligned → 'bad first offset'
  - blobNum >= blobCount → 'blob N out of range'
  - claimed offset entry past cluster end
  - startOff > endOff or endOff > body length → 'offsets invalid'

Plus a happy-path extended-cluster test exercising the 8-byte
offset width.

getZimBlob 67.7%→100%, zim Extract 78.4%→80.4%, package
coverage 90.2%→90.8%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
readZimDirEntry's `case zimDeletedMime: return zimDirEntry{IsDeleted: true}`
arm was untested — the previous redirect test only covered the
zimRedirectMime/zimLinkTargetMime case. Patch a dir entry's
mimeIdx to zimDeletedMime; Extract's IsDeleted skip arm fires
and the deleted entry is filtered out.

readZimDirEntry 75.0%→83.3%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The arm `if t.BytesMissing() <= 0 { continue }` skips fully
verified torrents from the active-downloads count. To exercise
it, the data file must already exist where anacrolix's storage
backend looks for it (DataDir/<torrent-name>) so VerifyData
runs synchronously and BytesMissing drops to 0 quickly.

Place the source file directly under the engine's DataDir,
AddTorrentMetaInfo, poll until BytesMissing == 0, then add a
magnet. The magnet's queueOrActivate triggers
countActiveDownloads — the seeding torrent is skipped per the
arm under test.

countActiveDownloads 93.3%→100%, engine package 86.7%→87.2%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Frank Dosk and others added 28 commits April 26, 2026 19:40
Wire an index, place a .txt file inside DataDir so the
plaintext extractor matches once VerifyData fires file-complete
events, AddTorrentMetaInfo, then poll the index until at least
one content document appears. The pipeline calls the OpenReader
closure inside ingestFileEvents (lines 1669-1681) which reads
the file and feeds chunks into Bleve.

ingestFileEvents 47.1%→88.2%, engine package 87.5%→88.0%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After AddTorrentMetaInfo wires up the file tracker but before
VerifyData fires, flip the per-torrent indexing flag OFF via
SetTorrentIndexing. ingestFileEvents drains the file events
but the `if !h.IsIndexing() { continue }` arm skips the
pipeline submit instead of indexing.

ingestFileEvents 88.2%→94.1%, engine package 88.0%→88.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire identity + DHT enabled (with a dead-end bootstrap addr so
the embedded server doesn't leak to the public mainline) so
engine.New's startPublisher hits the publisher worker, lookup,
and pointer-putter/getter constructors instead of returning at
the 'no identity' / 'no DHT server' guards. Asserts each handle
is non-nil after engine.New.

startPublisher 9.5%→78.6%, engine package 88.4%→90.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DisableDHTPublish=true is the documented 'leech-only DHT' mode
— suppresses the keyword publisher worker but keeps lookup +
pointer putter/getter wired so the node can still subscribe to
other publishers' BEP-46 pointers and BEP-44 keyword indexes.

Adds the second startPublisher test that asserts:
  - Publisher() is nil
  - Lookup(), PointerPutter(), PointerGetter() are non-nil

startPublisher 78.6%→81.0%, engine package 90.7%→91.0%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adding a torrent + sleeping briefly lets the autoIndex goroutine
catch up and call pub.Submit on the active publisher. Covers
the previously-cold `if pub != nil { pub.Submit(...) }` arm in
autoIndex.

autoIndex 84.6%→92.3%, engine package 91.0%→91.1%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AddInfoHash starts an upgradeMagnetSession goroutine that
blocks on GotInfo. Hand the same infohash's metainfo to the
engine via AddTorrentMetaInfo — anacrolix's registerLocked
dedupes by infohash, so metadata flows to the existing handle
and GotInfo fires. The goroutine then writes
DataDir/torrents/<ih>.torrent and updates the session entry.

Polls for the .torrent file's appearance to avoid asserting on
internal session state.

upgradeMagnetSession 39.1%→69.6%, engine package 91.0%→91.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same shape as the happy-path test but chmod 0o500 on the
torrents/ subdirectory after engine.New so writeTorrentCopy's
WriteFile fails when upgradeMagnetSession runs. Logs warn and
returns rather than panicking.

upgradeMagnetSession 69.6%→78.3%, engine package 91.7%→91.8%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hand metainfo into the engine first via AddTorrentMetaInfo so
the AddInfoHash inside FetchCompanionTorrent reuses the same
handle; GotInfo fires immediately, the file is single-file and
already verified on disk so the BytesCompleted poll loop exits
on the first iteration. Returns an absolute path under DataDir.

FetchCompanionTorrent 36.4%→86.4%, engine package 91.9%→92.8%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…m + min-score

The DHT-enabled full-path test now also wires:
  - cfg.Regtest=true → exercises the RegtestPublisherOptions arm
  - cfg.ReputationPath → lookup.SetTracker arm
  - cfg.BloomPath → lookup.SetBloom arm
  - cfg.MinIndexerScore=0.25 → lookup.SetMinIndexerScore arm

startPublisher 81.0%→90.5%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plant a directory at cfg.PublisherManifest so
LoadOrCreateManifest fails on the read of an unreadable path.
startPublisher's `if err != nil { return }` arm fires; engine.New
swallows the error after a warn (publisher_start_err) and the
engine still comes up with Publisher() == nil.

startPublisher 90.5%→92.9%, engine package 92.7%→93.3%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire two engines on loopback, place identical content under
both DataDirs, AddTorrentMetaInfo on each, then connect with
AddTrustedPeerEngine in both directions. Anacrolix dials over
loopback and the LTEP handshake fires the previously-cold
callback bodies in engine.New: PeerConnAdded, ReadExtended-
Handshake, PeerConnReadExtensionMessage, PeerConnClosed.

The test itself is timing-tolerant — even partial connection
setup triggers the callbacks the cover tool needs to mark
covered.

engine.New 82.2%→91.1%, engine package 93.3%→94.3%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The piece-event arm in file_tracker.run only fires when pieces
complete AFTER the tracker subscribes — i.e., during a real
download, not on a torrent that's already verified. Wire two
engines on loopback where the leech (B) starts with NO data
on disk; the seed (A) feeds it pieces, the leech's tracker
processes them via the `case ev, ok := <-sub.Values` arm, and
remaining[fi] decrements until it dispatches the file-complete
event.

file_tracker.run 79.4%→91.2%, engine package 94.3%→95.1%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire two engines with attached indices, seed a torrent doc on
the responder, establish an LTEP handshake via
AddTrustedPeerEngine, then issue a swarmsearch query from one
to the other. The responder's HandleMessage invokes the reply
closure inside engine.New's PeerConnReadExtensionMessage
callback (lines 688-700) which queues the result payload for
WriteExtendedMessage on the connection.

engine.New 91.1%→95.6%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Strip write on DataDir but keep DataDir/torrents writable so
writeTorrentCopy succeeds (writes to torrents/) but the
following session.update fails (saveLocked's WriteFile on
DataDir/session.json.tmp). Logs warn, returns cleanly.

upgradeMagnetSession 78.3%→82.6%, engine package 95.1%→95.3%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds TestFetchCompanionTorrentCtxCancelMidPoll: builds metainfo
from a file outside DataDir so VerifyData reports BytesCompleted=0,
no peers means it never advances, and a 250ms ctx times out inside
the polling loop, exercising the previously-cold ctx.Done() arm.
FetchCompanionTorrent coverage 86.4% → 95.5%.
Adds four loadPrivKey tests in cmd/swartznet:
  - explicit-path happy path with round-trip equality
  - explicit-path failure (planted directory)
  - default-path happy path (HOME isolated to a tmpdir)
  - default-path failure (empty HOME → UserHomeDir err)

loadPrivKey 33.3% → 91.7%; defaultIdentityPath 75.0% → 100%.
Adds TestBloomSaveWriteBloomKTooLarge: hand-builds a BloomFilter
with k = 70_000 so writeBloom's `k > math.MaxUint16` guard fires,
exercising Save's writeBloom-error cleanup path (f.Close + tmp
remove). Internal test so the private fields are reachable.

BloomFilter.Save 73.7% → 84.2%.
Three new branch tests for readZimCluster:
  - cluster-num-out-of-range: patch dir entry's ClusterNum to 99
    so the `num >= ClusterCount` guard fires
  - end-leq-start: patch cluster ptr past ChecksumPos so end ≤ start
  - size-exceeds-cap: bump header ChecksumPos so the implied
    cluster size > 64 MiB cap

readZimCluster 65.1% → 72.1%.
Adds TestOpenWithLoggerSchemaRebuildRemoveAllError: chmod the
parent dir to 0o500 between the stale-sentinel poke and the
re-open so os.RemoveAll(path) fails — exercising the
`return fmt.Errorf("indexer: remove stale index: %w", err)`
arm of the schema-rebuild branch.

OpenWithLogger 81.8% → 86.4%.
Adds TestPromoteQueuedLockedBreaksAtCap: starts with cap=1, adds
three torrents (one active + two queued), then raises cap to 2
so promoteQueuedLocked enumerates the queue, promotes one torrent,
hits active >= cap, and breaks before reaching the third —
exercising the previously-cold cap-limited loop body and break.

promoteQueuedLocked 73.7% → 100%.
Adds four error-path tests:
  - bad flag → flag.Parse err → exitUsage
  - missing positional args → NArg != 2 → exitUsage
  - missing file → os.ReadFile err → exitRuntime
  - non-btree bytes → companion.OpenBTree err → exitRuntime

cmdAggregateFind 66.7% → 87.9%.
Adds four cmd_crawl_probe tests:
  - bad-flag → flag.Parse err → exitUsage
  - bad-addr → ResolveUDPAddr err → exitUsage
  - explicit-hex --target round-trips through the responder
    (previously only the random-target branch was covered)
  - dead loopback peer + short timeout → SampleInfohashes err →
    exitRuntime

cmdCrawlProbe 80.6% → 90.3%.
Adds two PDF Extract branch tests:
  - iotest.ErrReader → io.ReadAll err → wrapped error returned
  - PDF whose Tj draws only whitespace → TrimSpace strips it →
    `return nil, nil` empty-text arm

PDFExtractor.Extract 73.9% → 82.6%.
Adds four readRecords branch tests:
  - happy path with blank lines (covers the empty-line continue)
  - missing file → os.Open err
  - empty kw → "empty kw" err
  - oversized line → scanner.Err propagates bufio.ErrTooLong

readRecords 82.1% → 96.4%.
Adds four iotest.ErrReader-driven branch tests for the
io.ReadAll(io.LimitReader(...)) arm in each office Extractor.

ODP 81.5 → 85.2%; ODT 85.2 → 88.9%; DOCX 85.2 → 88.9%;
PPTX 87.9 → 90.9%.
Adds four hand-built MKV branch tests, each declaring a body
length the bytes can't satisfy:
  - EBML body truncated → Discard err
  - no Segment header after EBML → second readEBMLHeader err
  - Info child body truncated → readFull err
  - unknown-child body truncated → default-case Discard err

MKVExtractor.Extract 81.1% → 91.9%.
Five more iotest.ErrReader-driven branch tests for the
io.ReadAll(io.LimitReader(...)) arm.

Archive 95.2 → 100%; MOBI 93.8 → 95.8%; EXIF 89.3 → 92.9%;
EPUB 87.9 → 90.9%; MP4 84.2 → 89.5%; package total 94.9 → 95.2%.
@claudenstein
claudenstein merged commit 5b49fb5 into main Apr 27, 2026
1 check failed
claudenstein pushed a commit that referenced this pull request Jul 19, 2026
Closes the round-4 adversarial backlog; each fix has a regression test that
fails when the fix is neutralized. All touched packages race-clean.

- #10 companion: put the new BEP-46 pointer BEFORE dropping the old seed, so a
  failed put keeps the still-advertised infohash fetchable.
- #7 dhtindex: TOCTOU between off-lock aggregate Refresh and a concurrent
  Retract — added a gen counter + post-distribute re-check so a retracted tree
  isn't left published for a full refresh interval.
- #9 indexer/httpapi/daemon: a malformed query string is now a typed
  ErrBadQuery → HTTP 400 (client fault), not 500; classified at the daemon
  seam via a LocalBadRequest flag (httpapi imports no indexer types).
- #5 companion: publisher GeneratedAt is now strictly monotonic across content
  changes, so a backward wall-clock step no longer trips the follower's replay
  guard and drops legitimate new content.
- #11 companion: an Unfollow racing an in-flight Sync no longer resurrects the
  dedup maps — per-publisher Forget epoch, re-checked under lock in commitDedup.
- #4 engine: persistAdd runs after Add releases e.mu; a racing RemoveTorrent
  could be resurrected — updateGuarded(abort) + Handle.isRemoved() make the
  create happens-before-correct (RemoveTorrent closes h.removed before its
  lock-guarded remove); persistSeedAdd DataPath refinement → updateExisting.
- #8 swarmsearch: initiator/responder txid collision deferred with a fix sketch
  (needs a wire direction bit + the unwired StartSync path) — see DECISIONS T3-8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant