Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
ad3ea97
httpapi: cover GET / root-handler closure body
Apr 25, 2026
f57de60
dhtindex: cover EncodeValue nil-Hits default arm
Apr 25, 2026
459ba0f
daemon: cover httpGetClient Do-error arm
Apr 25, 2026
647e073
daemon: cover FallbackToHTTPS nil-client default arm
Apr 25, 2026
22ccbfe
companion: cover SignAndMineRecord PoW-error propagation
Apr 25, 2026
5e1ba8b
companion: cover VerifyFingerprint count-mismatch arm
Apr 25, 2026
8f8fe66
companion: cover Find PoW-skip arm
Apr 25, 2026
03a714e
companion: cover Find bad-signature skip arm
Apr 25, 2026
5006b51
engine: cover CreateTorrentFile CreateTorrent-error propagation
Apr 25, 2026
636a73d
engine: cover CreateTorrent WalkDir-error arm
Apr 25, 2026
7f7caf0
dhtindex: cover GetInfohashPointer DHT-traversal-fails arm
Apr 26, 2026
035bb7b
dhtindex: cover Put*/Get* DHT-traversal-fails arms
Apr 26, 2026
ff8c781
swarmsearch: cover handleQuery nil-reply decode-only arm
Apr 26, 2026
5ec1672
swarmsearch: cover handleQuery reply-error arm
Apr 26, 2026
0336a29
swarmsearch: fix TestOnSyncRecordsApplyError to actually trigger appl…
Apr 26, 2026
1b14499
companion: cover Find piece-fetch error arms
Apr 26, 2026
7a58883
companion: cover Find leaf re-fetch decode-error arm
Apr 26, 2026
ee9fa16
companion: cover Find root-decode + kind-not-root arms
Apr 26, 2026
b240466
indexer: cover Pipeline.handle empty-chunks skip arm
Apr 26, 2026
e55352b
companion: cover walkToLeaves unexpected-kind arm
Apr 26, 2026
8f0e5b6
indexer: cover Pipeline.handle all-chunks-failed counter arm
Apr 26, 2026
d4a9f4a
companion: cover WriteCompanionFiles torrent-write fail arm
Apr 26, 2026
e913636
daemon: cover IngestEndorsement bloom-policy fallback arm
Apr 26, 2026
f88bc87
dhtindex: cover legacyQuery merge name-fill arm
Apr 26, 2026
01a5cdc
dhtindex: cover legacyQuery Size/Files merge fill arms
Apr 26, 2026
8547d00
dhtindex: cover legacyQuery bad-infohash-length skip arm
Apr 26, 2026
f508fd7
reputation: cover Tracker.Save no-path no-op arm
Apr 26, 2026
304bc07
reputation: cover writeBloom write-error arms
Apr 26, 2026
bc7da9e
reputation: cover readBloom corruption + truncation arms
Apr 26, 2026
7ece988
reputation: cover EstimatedItems saturated-filter arm
Apr 26, 2026
de9508b
companion: cover leadingZeroBitsOfByteSlice all-zero arm
Apr 26, 2026
92cd86d
companion: cover packLeaves single-record-too-big arm
Apr 26, 2026
1e41198
companion: cover EncodeRecord oversize-bencoded guard
Apr 26, 2026
5a43aee
companion: cover VerifyFingerprint fetch/decode/hash arms
Apr 26, 2026
1404cb7
swarmsearch: cover ProduceSymbols phase + count-clamp arms
Apr 26, 2026
4d2db17
swarmsearch: cover ApplyNeed txid + too-many-ids guards
Apr 26, 2026
1856c14
swarmsearch: cover DecodeSyncBegin wrong-msg-type arm
Apr 26, 2026
04ae09a
swarmsearch: cover DecodeSyncRecords too-many-records cap
Apr 26, 2026
ecfa3e8
swarmsearch: cover EncodeSyncNeed nil-IDs default arm
Apr 26, 2026
0a72248
swarmsearch: cover NeedFrame too-many-IDs cap
Apr 26, 2026
24319ba
swarmsearch: cover Query all-sends-fail zero-asked arm
Apr 26, 2026
1f23e8b
swarmsearch: cover DecodePeerAnnounce truncation arm
Apr 26, 2026
512d8de
gitignore: exclude local .claude/ workspace state
Apr 26, 2026
26ec681
swarmsearch: cover DecodeSyncSymbols + EncodeSyncRecords arms
Apr 26, 2026
8e52851
swarmsearch: cover handleSyncFrame Records + End happy-path dispatch
Apr 26, 2026
eb62af5
httpapi: cover handleSearch DHT-error arm
Apr 26, 2026
f37b77e
httpapi: cover handleSearch DHT hit-iteration arm
Apr 26, 2026
1dba6e1
engine: cover restoreEntry QueueOrder counter-bump arm
Apr 26, 2026
ad03060
swarmsearch: cover ApplySymbols budget-exceeded arm
Apr 26, 2026
278f123
swarmsearch: cover ingestSyncRecords bad-length defence-in-depth
Apr 26, 2026
488c04b
swarmsearch: cover OnRemoteHandshake publisher-announce arms
Apr 26, 2026
199a2a8
companion: cover EncodeLeaf EncodeRecord-error propagation
Apr 26, 2026
941dc2f
engine: cover sampleRate no-metadata early-return arm
Apr 26, 2026
3cee590
engine: cover restoreEntry closed-engine early-return arm
Apr 26, 2026
3e0822a
engine: cover loadSession mkdir + read error arms
Apr 26, 2026
9537c30
engine: cover Close bloom + tracker persistence arms
Apr 26, 2026
d729c63
companion: cover DecodeTrailer kind + payload-length arms
Apr 26, 2026
014f42a
companion: cover BuildBTree packLeaves-error propagation
Apr 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ Thumbs.db
/tests/torrent-test/peer*-data/
/tests/torrent-test/peer*-index/
/tests/torrent-test/results/
.claude/
32 changes: 32 additions & 0 deletions internal/companion/btree_encode_guards_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ func TestEncodeLeafEmptyRecords(t *testing.T) {
}
}

// TestEncodeLeafPropagatesEncodeRecordError covers EncodeLeaf's
// `if err != nil { return nil, err }` arm inside the per-
// record loop. Pass a record with an empty keyword so
// EncodeRecord errors immediately, and verify EncodeLeaf
// surfaces the error rather than producing a leaf with a
// half-written payload.
func TestEncodeLeafPropagatesEncodeRecordError(t *testing.T) {
t.Parallel()
bad := Record{Kw: ""} // EncodeRecord rejects empty keyword
if _, err := EncodeLeaf(0, []Record{bad}, MinPieceSize); err == nil {
t.Error("EncodeLeaf should propagate EncodeRecord errors")
}
}

// TestEncodeRecordOversizedKeyword — EncodeRecord rejects a
// keyword longer than MaxKeywordBytes before attempting to
// marshal. Same guard packLeaves uses, but exercised at the
Expand All @@ -61,3 +75,21 @@ func TestEncodeRecordOversizedKeyword(t *testing.T) {
t.Error("EncodeRecord should reject oversize keyword")
}
}

// TestEncodeRecordExceedsByteCap covers EncodeRecord's
// `if len(out) > MaxRecordBytes { return error }` guard —
// the post-marshal size check for records whose encoded form
// pushes past the 256-byte cap. With Kw at the keyword limit
// plus max-width Pow + T values, the bencoded form of a
// recordWire crosses the boundary.
func TestEncodeRecordExceedsByteCap(t *testing.T) {
t.Parallel()
r := Record{
Kw: strings.Repeat("k", MaxKeywordBytes),
T: 1<<63 - 1,
Pow: 1<<64 - 1,
}
if _, err := EncodeRecord(r); err == nil {
t.Error("EncodeRecord should reject record whose encoded form exceeds MaxRecordBytes")
}
}
21 changes: 21 additions & 0 deletions internal/companion/build_btree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,27 @@ func TestBuildBTreeMultiLeafWalk(t *testing.T) {
}
}

// TestBuildBTreePropagatesPackLeavesError covers BuildBTree's
// `if err != nil { return out, err }` arm after packLeaves.
// Pass a record with empty keyword so packLeaves rejects;
// BuildBTree must surface that error.
func TestBuildBTreePropagatesPackLeavesError(t *testing.T) {
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
var pk [32]byte
copy(pk[:], pub)
bad := Record{Kw: ""} // packLeaves rejects empty keyword
if _, err := BuildBTree(BuildBTreeInput{
Records: []Record{bad},
PubKey: pk,
PrivKey: priv,
Seq: 1,
PieceSize: MinPieceSize,
CreatedTs: 1,
}); err == nil {
t.Error("BuildBTree should propagate packLeaves errors")
}
}

func TestBuildBTreeRejectsEmpty(t *testing.T) {
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
var pk [32]byte
Expand Down
32 changes: 32 additions & 0 deletions internal/companion/decode_leaf_errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,38 @@ func TestEncodeTrailerBadVersion(t *testing.T) {
}
}

// TestDecodeTrailerBadKind — DecodeTrailer must reject a
// page whose header decodes successfully but whose Kind is
// not PageKindTrailer.
func TestDecodeTrailerBadKind(t *testing.T) {
t.Parallel()
body := make([]byte, TrailerPayloadSize)
page := makeInteriorPage(t, PageKindLeaf, body, MinPieceSize)
if _, err := DecodeTrailer(page); err == nil {
t.Error("DecodeTrailer should reject leaf-kind page")
}
}

// TestDecodeTrailerWrongPayloadLength — a trailer whose
// header.PayloadLength differs from TrailerPayloadSize must be
// rejected before any body bytes are read.
func TestDecodeTrailerWrongPayloadLength(t *testing.T) {
t.Parallel()
// Hand-build a page with PageKindTrailer but wrong payload
// length. encodeHeader clamps to uint16; pass a length
// smaller than TrailerPayloadSize.
page := make([]byte, MinPieceSize)
hdr := encodeHeader(PageHeader{
Version: BTreeVersion,
Kind: PageKindTrailer,
PayloadLength: 32, // not TrailerPayloadSize
})
copy(page, hdr)
if _, err := DecodeTrailer(page); err == nil {
t.Error("DecodeTrailer should reject mismatched PayloadLength")
}
}

// TestDecodeTrailerBadVersion — DecodeTrailer must reject a
// trailer whose first payload byte (TrailerVersion) is not
// 0x01. Hand-build the page since EncodeTrailer rejects bad
Expand Down
15 changes: 15 additions & 0 deletions internal/companion/pack_leaves_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,18 @@ func TestPackLeavesRejectsOversizeKeyword(t *testing.T) {
t.Error("packLeaves should reject oversize keyword")
}
}

// TestPackLeavesSingleRecordTooLargeForPage covers the
// `if len(cur) == 0 { return error }` arm of packLeaves.
// Pass a tiny pieceSize so even a single normally-sized
// record can't fit; packLeaves must surface the
// "record too large" error rather than silently truncating.
func TestPackLeavesSingleRecordTooLargeForPage(t *testing.T) {
t.Parallel()
r := Record{Kw: "ubuntu"}
// 100 bytes is well below the ~270 bytes a record encodes
// to, plus the 16-byte page header.
if _, err := packLeaves([]Record{r}, 100); err == nil {
t.Error("packLeaves should reject single oversized record")
}
}
37 changes: 37 additions & 0 deletions internal/companion/pow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,23 @@ func TestSignAndMineRecordRoundTrip(t *testing.T) {
}
}

// TestLeadingZeroBitsOfByteSliceEdgeCases covers the two
// edge-case return arms of the pow.go-local copy of the
// leading-zero-counter: the all-zero input (outer loop exits
// without ever entering the inner mask loop) and the empty
// input (outer loop never iterates).
func TestLeadingZeroBitsOfByteSliceEdgeCases(t *testing.T) {
if got := leadingZeroBitsOfByteSlice(nil); got != 0 {
t.Errorf("leadingZeroBitsOfByteSlice(nil) = %d, want 0", got)
}
if got := leadingZeroBitsOfByteSlice([]byte{0x00, 0x00, 0x00}); got != 24 {
t.Errorf("leadingZeroBitsOfByteSlice(3 zeros) = %d, want 24", got)
}
if got := leadingZeroBitsOfByteSlice([]byte{0x00, 0x00, 0x40}); got != 17 {
t.Errorf("leadingZeroBitsOfByteSlice(2 zeros + 0x40) = %d, want 17", got)
}
}

func TestSignAndMineRecordRejectsBadPubLen(t *testing.T) {
_, priv, _ := ed25519.GenerateKey(rand.Reader)
var ih [20]byte
Expand All @@ -100,6 +117,26 @@ func TestSignAndMineRecordRejectsBadPubLen(t *testing.T) {
}
}

// TestSignAndMineRecordPropagatesPoWError covers the
// `if err != nil` arm after MineRecordPoW. Pass bits=41 so
// MineRecordPoW's "cost prohibitive" guard fires; the wrapped
// error must surface from SignAndMineRecord without producing
// a signed record.
func TestSignAndMineRecordPropagatesPoWError(t *testing.T) {
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
var ih [20]byte
r, err := SignAndMineRecord(priv, pub, "x", ih, 0, 41)
if err == nil {
t.Fatal("expected error for prohibitive bits=41")
}
// The returned record is the partial mined value; its Sig
// must remain zero since signing was skipped.
var zeroSig [64]byte
if r.Sig != zeroSig {
t.Error("Sig should be zero on PoW failure (signing skipped)")
}
}

// Sanity: recordPreimage and RecordSigMessage MUST produce
// identical bytes — otherwise a miner would solve for one preimage
// and a verifier would check a different one.
Expand Down
Loading
Loading