Overnight/test harness 2026 04 20#7
Merged
Conversation
Same pattern as the AllTorrentDocs and Stats pagination covers: 1001 content docs under one infohash forces ContentDocsForInfoHash's second loop iteration with from=1000, exercising the `from += batch` increment at indexer.go:485. Skipped under -short (indexing 1001 docs takes ~10s). Coverage: - indexer.go ContentDocsForInfoHash 90.9% → 95.5% - internal/indexer total 96.9% → 97.1% Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
newRightClickCapture and CreateRenderer move 0% → 100%. TappedSecondary covers nil-menuBuilder, nil-menu, empty-items, and nil-canvas early-returns at 88.9% (the ShowPopUpMenuAtPosition success path requires a real canvas attachment which fyne/test doesn't drive). Coverage: - context_menu.go newRightClickCapture 0% → 100% - context_menu.go CreateRenderer 0% → 100% - context_menu.go TappedSecondary 0% → 88.9% - internal/gui total 9.9% → 10.8% Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both methods are testable without a daemon — SelectTab uses only the tabs container, and Cleanup calls a.cancel(). Construct App directly with the relevant fields and exercise: - SelectTab dispatches by all 10 accepted names (5 tabs × 2 spellings) and silently ignores unknown names. - SelectTab nil-tabs short-circuit at app.go:393-395. - Cleanup invokes the stored cancel func, observable via the paired ctx. Coverage: - app.go SelectTab 0% → 100% - app.go Cleanup 0% → 100% - internal/gui 10.8% → 11.5% Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two tab-construction helpers that don't need a daemon: labelRow returns an HBox of (bold-name, value-label) and makeLabelGroup allocates N "-" placeholder labels. Construct under test.NewApp and verify shape + count. Coverage: - status.go labelRow 0% → 100% - status.go makeLabelGroup 0% → 100% - internal/gui total 11.5% → 12.0% Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sortFilesLocked dispatches on fd.sortBy across path/size/progress/ priority/index plus the prioRank table inside the priority case. Construct a filesDialog with synthetic FileSnapshots — no daemon needed — and assert the post-sort ordering for each sort key (plus an "unknown" key that falls through to the index default). Coverage: - files_dialog.go sortFilesLocked 0% → 100% - internal/gui total 12.0% → 13.7% Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two-case cover: - negative sortCol short-circuits at downloads.go:434. - sortCol >= 0 invokes snapLess + sortSnapsSlice (already 100%) and produces the documented ascending order. Coverage: - downloads.go sortSnapsLocked 0% → 100% - internal/gui total 13.7% → 14.1% Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
toggleSort cycles asc → desc → cleared on the active column, switches to ascending on a different column, and short-circuits on out-of-range column indices. Construct a downloadsTab with a real (but stub-callback) widget.Table so Refresh works inside test.NewApp, and walk every transition. Coverage: - downloads.go toggleSort 0% → 100% - internal/gui total 14.1% → 15.0% Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four one-line wrappers — companionTab.win(), downloadsTab.win(), searchTab.win(), settingsTab.win() — each returning windowForObject(t.content). With no canvas attachment they fall back to the first known window. Construct each tab with a label content under test.NewApp + an anchor window and assert non-nil return. Coverage: - companion.go win 0% → 100% - downloads.go win 0% → 100% - search.go win 0% → 100% - settings.go win 0% → 100% - internal/gui 15.0% → 15.3% Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
unfollowAt's `if ct.d.CompSub == nil || idx >= len(ct.follows)`
short-circuit at companion.go:255 covers two pre-decode guards.
Construct a companionTab with a bare daemon.Daemon{} (nil CompSub)
and exercise both nil-CompSub + out-of-range halves. The
DecodeString err arm requires a real CompSub and is left
uncovered for now.
Coverage:
- companion.go unfollowAt 0% → 22.2%
- internal/gui total 15.3% → 15.4%
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two more daemon-free helpers — refreshPublisher's
`if ct.d.CompPub == nil { return }` arm and doFollow's first
arm `if ct.d.CompSub == nil → ShowError + return`. Both build
on a bare daemon.Daemon{} and (for doFollow) a fyne/test app +
anchor window so dialog.ShowError has a parent. The second/third
doFollow arms (len-check + bad-hex) need a real
SubscriberWorker; left for a future testlab integration test.
Coverage:
- companion.go refreshPublisher 0% → 28.6%
- companion.go doFollow 0% → 23.1%
- internal/gui total 15.4% → 15.8%
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
selectedInfoHash returns dl.snaps[dl.selected].InfoHash with two out-of-range guards. Five sub-cases cover -1 sentinel, past-end index, two valid indices, and an empty snaps slice. Daemon-free. Coverage: - downloads.go selectedInfoHash 0% → 100% Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
showAddMagnetError builds an information dialog, wires a re-open callback to OnClosed, and shows it. Daemon-free — only needs a fyne/test app + anchor window. The OnClosed callback path (which calls showAddMagnetDialogPrefilled) requires closing the dialog, which test.NewApp doesn't drive interactively, so it remains uncovered. Coverage: - downloads.go showAddMagnetError 0% → 75% - internal/gui total 15.8% → 16.1% Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds tests for the daemon-free arms of several gui methods: selectedInfoHash early-returns in pause/resume/remove/toggle/show files; showSignatureDialog unsigned arm; runSearch empty-query guard; showAddMagnetDialog/Prefilled dialog construction; showAddFileDialog construction; removeSelected confirm-dialog construction (named + unnamed labels). gui pkg coverage 16.1% → 19.7%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds tests for daemon-free arms of three more gui paths: - buildContextMenu: nil-return on no selection, plus four snap variants (paused/unpaused, indexing/not, queued, signed trusted/untrusted) so every label/section branch fires; daemon calls live inside menu-item callbacks that don't run - applyQueueSettings/applyRateLimits: invalid-input ShowError arms before any daemon call - createTorrentDialog: full dialog construction (rootEntry, pieceSelect, trackers/webseeds, sign/seed checks, output path picker); daemon only used inside OnConfirm so passing nil is safe gui pkg coverage 19.7% → 26.3%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds tests for daemon-free arms across four more gui paths: - installShortcuts: full keyboard-shortcut wiring (Ctrl+N/F/Q, Delete) with closures captured but not invoked - setupSystemTray: non-desktop type-assertion early-return arm (Fyne's test app is not desktop.App) - makeLocalHitCard / makeSwarmHitCard / makeDHTHitCard: card construction across each subtitle-extension arm (empty/non- empty Name, content vs torrent DocType, signed-by, BloomHit, Sources/Size present) - buildResults: all six branches — local/swarm/dht resp arm, local/swarm/dht err arm, plus the no-layers fallback parts append. Card callbacks reference daemon methods but never fire during construction. gui pkg coverage 26.3% → 32.2%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds tests for daemon-free arms in gui pollLoop / refresh paths: - companionTab.refresh: nil CompPub + nil CompSub arms execute the gather-skip paths and the fyne.Do label-reset block; refresh 0% → 43.2% - companionTab.pollLoop / downloadsTab.pollLoop / filesDialog.pollLoop: canceled-context fast-return arm fires before any daemon dereference gui pkg coverage 32.2% → 34.7%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds tests for daemon-free arms across more gui paths:
- titleLoop / notificationLoop: canceled-ctx fast-return arm
(no daemon dereference before select)
- newDownloadsTab: full constructor (table, headers, toolbar
buttons, empty-state); canceled context makes the
background pollLoop return on first iteration
- newSearchTab: pure constructor — daemon used only in
callbacks, no goroutine
- newCompanionTab: constructor + nil-safe pollLoop refresh
(CompPub / CompSub both nil) — empty daemon.Daemon{}
suffices
gui pkg coverage 34.7% → 41.9%.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a test covering setAllPriorities at files_dialog.go:219 when fd.files is empty: the synchronous body snapshots an empty index list, the goroutine's for-loop body skips, and fd.d.Eng.SetFilePriority is never called. setAllPriorities 0% → 61.5%; gui total 41.9% → 42.5%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds daemon-backed tests that lift several previously-0% gui methods. Introduces a newTestDaemon helper that spins up a minimal daemon (no DHT, no upload, no identity / reputation / bloom / trust paths) — enough wiring for engine getters and indexer Stats/Search to run without segfaulting. - newStatusTab + statusTab.refresh (0% / 0% → 76.7% / 69.1%): full card construction and one refresh under a real engine - newSettingsTab + load/apply paths: loadCurrent, loadRateLimits, loadQueueSettings + the success arms of applyRateLimits, applyQueueSettings, save - confirmHit / flagHit: bad-hex, wrong-length-decoded, and happy paths against a non-nil bloom + tracker - runSearch goroutine: happy-path local query and the all-layers- off fallback "No search layers enabled" status gui pkg coverage 42.5% → 56.7%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds daemon-backed tests for several previously-0% gui paths: - showFilesDialog: err arm against an unknown infohash (engine returns "torrent not found") - filesDialog.build: full dialog + pollLoop construction with hand-injected files; dlg.Hide() exits the goroutine cleanly - App.showAbout: dialog renders with empty pubKey (no identity), non-zero LocalPort, and apiAddr "disabled" arms - runCreateTorrent: missing-Root err arm AND happy-path success (creates a real .torrent and re-adds it via AddTorrentMetaInfo for the "Seeding started" message) gui pkg coverage 56.7% → 62.5%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an addTestTorrent helper that creates a real torrent file via Engine.CreateTorrentFile + AddTorrentMetaInfo, polling TorrentSnapshots until the new infohash appears. Two daemon- backed tests use it: - pauseSelected / resumeSelected / toggleIndexSelected / showFilesForSelected: each runs against a real engine + real torrent so the daemon-touching arms execute (previously only the nil-selection early-returns) - showSignatureDialog: trusted + untrusted variants — both fire the TrustStore.Label() lookup and render the trust labels; previously only the unsigned-early-return arm was covered gui pkg coverage 62.5% → 65.7%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds daemon-backed tests that let the long-running goroutines fire at least one tick before cancellation: - downloadsTab.pollLoop: 2.2s wait covers the tick.C body — TorrentSnapshots fetch, sort under lock, table.Refresh, and empty-state toggle - titleLoop + notificationLoop: combined into a single 3.2s wait so total wall time stays under ~3.5s; both fetch snapshots and update window title / fire notifications gui pkg coverage 65.7% → 67.1%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Setting BloomPath + ReputationPath under TempDir lets the engine construct a non-nil KnownGoodBloom and ReputationTracker, which in turn lets confirmHit / flagHit reach their bloom.Add and RecordConfirmed/RecordFlagged arms. Two new tests pre-seed the SourceTracker so the `len(pks) > 0` happy path also fires. confirmHit 21.4% → 92.9%, flagHit 18.8% → 87.5%; gui pkg total 67.1% → 69.0%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an IdentityPath under TempDir so the engine constructs a real Identity (helps showAbout pubKey arm) and a new test that pre-records reputation entries (one short pubkey, one >16-char to exercise the truncation arm) before status.refresh runs. status.refresh 72.2% → 79.4%; gui pkg 69.0% → 69.6%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a newDHTTestDaemon helper that builds a daemon with DHT enabled (DisableDHTPublish=true) and CompanionDir set so the engine wires up CompSub + CompPub. Three previously-uncovered companion-tab arms now fire end-to-end: - doFollow happy path: 64-char hex pubkey decodes, CompSub.Follow runs (23.1% → 69.2%) - unfollowAt happy path: valid follow row, CompSub.Unfollow runs (22.2% → 88.9%) - refreshPublisher: CompPub.RefreshNow goroutine spawns and returns cleanly with no indexed content (28.6% → 71.4%) gui pkg coverage 69.6% → 70.7%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new daemon-backed tests using newDHTTestDaemon exercise the non-nil-Lookup / non-nil-Bootstrap / non-nil-Publisher arms in status.refresh, and the non-nil-CompPub / non-nil-CompSub arms in companion.refresh — including the follow-loop body after CompSub.Follow seeds a row. companion.refresh 43.2% → 94.6%, status.refresh 79.4% → 84.5%; gui pkg total 70.7% → 72.6%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two more gui tests: - setAllPriorities failed arm: with files populated and an unknown infohash, the inner SetFilePriority loop populates the failed slice and fyne.Do fires ShowError - newCompanionTab with CompSub wired: pre-seed a follow so the follow-list UpdateCell callback runs against a real row during the initial refresh gui pkg coverage 72.6% → 73.0%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mounting dl.content in a test window and resizing to 1600x800 forces Fyne to lay out the downloads table and call the UpdateCell callback for every visible row, exercising every column's cell-formatting branch (Name with empty + non-empty, Status, Progress, Size, Peers, rates, Indexed yes/no, Signed none/trusted/untrusted). newDownloadsTab 34.9% → 77.9%; gui pkg total 73.0% → 75.8%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mount ct.content in a 1200x800 test window plus a second seeded follow with a long pubkey label so the >16-char truncation arm fires. Companion follow-list UpdateCell callback now executes its per-row body for both seeded entries. newCompanionTab 60.0% → ~80%; gui pkg total 75.8% → 76.7%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A daemon-backed test combines DHT enabled (so Publisher and Lookup are wired) with a real torrent in flight (so the snaps loop body and per-status switch fire). status.refresh 84.5% → 89.7%; gui pkg total 76.7% → 77.1%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The test canvas's embedded ShortcutHandler lets us call TypedShortcut for each Ctrl+N/F/Q binding so the handler closures execute (Add Magnet dialog, focus Search, quit). The OnTypedKey handler is fetched via Canvas.OnTypedKey() and invoked with KeyDelete to fire the row-delete arm. installShortcuts 47.4% → 84.2%; gui pkg total 82.5% → 83.1%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A small helper recursively walks a CanvasObject tree to find a button by label. The companion tab's "Refresh Now" and "Follow" buttons are deep inside cards/containers, so direct handles aren't available — the walker locates them and we invoke each OnTapped closure. newCompanionTab 87.5% → 92.5%; gui pkg total 83.1% → 83.4%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
findButtonByLabel locates each toolbar button (Add Magnet, Add .torrent, Create Torrent, Pause, Resume, Remove, Toggle Index, Files...) and we invoke its OnTapped closure. With selected=-1 the per-row actions early-return but the closure prefix and dialog-construction paths execute. newDownloadsTab 86.0% → 95.3%; gui pkg total 83.4% → 84.0%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After fd.build() the dialog content is added as an overlay on the parent window's canvas. test.LaidOutObjects walks the laid-out child tree including PopUp/Card wrappers, so we locate the Select All / Deselect All buttons by Text and invoke their OnTapped closures (which delegate to fd.setAllPriorities). filesDialog.build 86.0% → 89.5%; gui pkg total 84.0% → 84.2%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After createTorrentDialog opens, find the Choose File/Folder/ Save As buttons via test.LaidOutObjects on the canvas overlays and invoke each OnTapped — the closure body that constructs and shows a file/folder dialog runs (the file-pick callback itself only fires on user pick). createTorrentDialog 44.9% → 53.8%; gui pkg total 84.2% → 84.7%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Locate rootEntry by its placeholder text via test.LaidOutObjects on the canvas overlays, then SetText with three variants: slash-only (autofillName early-return on base ".") , a real path (initial fill), and a second real path (re-fill since nameEntry.Text == lastAutofill). createTorrentDialog 53.8% → 62.8%; gui pkg total 84.7% → 85.3%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the dialog is shown the URI Entry and Add button live in the canvas overlay stack. We locate them via test.LaidOutObjects and tap Add with three URIs to drive each submit-callback arm: empty (showAddMagnetError), validateMagnetURI rejection (showAddMagnetError), and a valid magnet (engine AddMagnetURI goroutine fires). showAddMagnetDialogPrefilled 40.0% → 83.3%; gui pkg total 85.3% → 86.3%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Walking the canvas overlays after removeSelected shows the confirm dialog, locate the default "Yes" button (fyne uses Yes/No labels for ShowConfirm) and tap OnTapped. The confirmation goroutine then invokes Eng.RemoveTorrent and flips selected back to -1. removeSelected 63.6% → 95.5%; gui pkg total 86.3% → 86.8%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Walk the canvas overlays after showAddMagnetError, locate the information dialog's "OK" button (fyne's default dismiss), and invoke OnTapped. d.Hide() then fires the SetOnClosed callback which re-opens showAddMagnetDialogPrefilled. showAddMagnetError 75.0% → 100%; gui pkg total 86.8% → 86.9%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three opens of the dialog, each tapping its "Create" button via overlay-walker: - empty rootEntry → ShowError "root path required" - root set, output empty → ShowError "output path required" - valid root + output → runCreateTorrent goroutine fires (the hashing fails because the path doesn't exist, but the closure body up through that point executes) createTorrentDialog 62.8% → 78.2%; gui pkg total 86.9% → 87.8%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- flagHit fallback: pre-record a tracker entry but flag an infohash with no recorded sources — the function falls through to iterate tracker.Snapshot, populating pks from every known indexer. flagHit 87.5% → 93.8% - status + companion pollLoop tick.C arms: combined into one ~4.2s wait so both 4s tickers fire once before cancellation. Both 85.7% → 100% gui pkg total 87.8% → 88.0%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two pollLoop tick tests: - TestDownloadsPollLoopTickEmpty: no torrents added → snaps stays empty → emptyState.Show() arm fires - TestDownloadsPollLoopTick (updated): adds a torrent first → snaps non-empty → emptyState.Hide() arm fires downloads.pollLoop 93.3% → 100%; gui pkg total 88.0% → 88.1%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Splits each newXxxTab function into a buildXxxTab (struct + widget construction, no goroutine) plus a thin newXxxTab (buildXxxTab + go pollLoop). Tests use the build* variant to drive refresh/status logic without the pollLoop goroutine racing test-thread widget reads on shared Fyne caches. - newStatusTab: now a 3-line wrapper over buildStatusTab; tests in status_with_daemon, refresh_with_dht, status_dht_with_torrent, status_full_dht, status_with_reputation switch over - newCompanionTab: same split; companion_buttons, new_companion_tab[_with_dht], refresh_with_dht switch over - newDownloadsTab: same split (no test usage change yet but the helper is available) - build_context_menu_actions: skips goroutine-spawning menu items (Pause/Resume/Toggle Index/Queue moves) since their leaked Eng.* goroutines race other tests' Fyne caches under -race - companion_buttons: stops tapping Refresh Now (its goroutine bleeds); just verifies the button is wired up - removes status_companion_pollloop_tick_test — its purpose was to exercise the 4s tick.C arms, but the pollLoop goroutines it spawns leak across the whole test sweep and race other tests Behavior unchanged in production (newXxxTab still spawns pollLoop). gui pkg coverage 88.1% → 86.2%; the trade-off is worth it for cleaner -race behaviour. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The valid-magnet arm tapped the dialog's Add button with a real magnet URI, which spawned a goroutine that ran Eng.AddMagnetURI + Eng.SetTorrentIndexing. That goroutine bleeds across the test boundary and races other gui tests' Fyne caches under -race. Empty-URI and bad-scheme arms (the validation paths) still cover the closure body without spawning engine goroutines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Several tests added in this branch tap goroutine-spawning UI paths (runCreateTorrent, RemoveTorrent confirm, runSearch multi-layer goroutines, refreshPublisher's goroutine). Their spawned goroutines outlive the test and race other tests' Fyne work under -race. The goroutine bodies are mostly covered by direct unit tests of confirmHit/flagHit/buildResults; the bleeding wrapper tests are net-negative for -race compliance. Removed: run_create_torrent_test.go, create_dialog_confirm_test.go, remove_selected_confirm_test.go, run_search_dht_test.go, TestRunSearchHappyPath / TestRunSearchAllLayersOff (in search_with_daemon_test.go), TestCompanionRefreshPublisherWithDaemon (in companion_with_compsub_test.go). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three more bleeders went away:
- TestSetAllPrioritiesFailedArm: setAllPriorities with non-empty
files spawns a goroutine that errs and pushes ShowError via
fyne.Do; the queued Fyne work bleeds into next tests
- TestFilesDialogBuildWithPrefilledFiles: fd.build spawns the
2 s-tick pollLoop on its own context; goroutine outlives the
test (its OnClosed cancel only fires on dialog interaction)
- TestDownloadsSelectedActionsHappyPath: showFilesForSelected
goes through the same fd.build path
- TestPersistFnsNoSessReturnEarly (engine): writes eng.sess = nil
after AddMagnet which has spawned upgradeMagnetSession reading
eng.sess — true engine race, not just goroutine bleed. Removed
rather than serialise; the nil-sess defensive arms are
reachable via the existing engine.Close happy-path tests.
Full CI-equivalent race sweep
(go test -race -count=1 $pkgs, where pkgs excludes
internal/testlab) is now CLEAN across cmd/swartznet,
cmd/swartznet-gui, internal/{companion, config, daemon,
dhtindex, engine, gui, httpapi, httpapi/web, identity,
indexer, indexer/extractors, reputation, signing,
swarmsearch, trust}.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous version called eng.AddMagnet which spawned an
upgradeMagnetSession goroutine that read e.sess concurrently
with the test's e.sess = nil write. The new version skips
AddMagnet entirely: each persist*/RestoreSession/upgradeMagnetSession
function checks `if e.sess == nil { return }` BEFORE touching its
*Handle argument, so we can pass nil and still hit the
nil-sess defensive arm.
persistAdd / persistState / RestoreSession back to 100%
(upgradeMagnetSession 87% same as before because that fn's
non-nil-sess path already had an existing test).
Full -race sweep across all CI-tested packages remains clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TestBuildResultsArms and TestSetAllPrioritiesEmptyFiles racing each other ~3/8 of the time on a clean -race sweep. Both touch Fyne widgets which share unsynchronised global font caches upstream. With t.Parallel removed they run sequentially and the flake disappears (10/10 clean across consecutive runs). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Calling pollLoop directly with a canceled context exercises the initial-refresh + ctx.Done early-return arm without spawning the goroutine that bleeds across test boundaries. status/companion pollLoop both 0% → ~85%; gui pkg 74.0% → 74.5%. -race sweep stable across 5 consecutive runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
downloads.pollLoop has no pre-loop refresh (unlike status/ companion pollLoops), so a canceled-ctx newDownloadsTab call spawns the goroutine which exits on the first select without any fyne.Do bleed. Wrapper goes 0% → 100%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both wrappers spawn pollLoop goroutines that fire an initial refresh; with a canceled ctx the goroutine exits after the refresh. A 500 ms drain at end of test lets the fyne.Do callbacks complete before the test returns so they don't bleed into other tests' Fyne work. newStatusTab / newCompanionTab 0% → 100%; gui pkg 74.5% → 74.9%. -race stable across 5 consecutive runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Calls runCreateTorrent with a non-existent Root path: the goroutine errs quickly inside CreateTorrentFile, then fyne.Do hits the ShowError + return arm. A 500 ms drain at end of test lets the goroutine fully complete so its work doesn't bleed. runCreateTorrent 0% → 60%; gui pkg 74.9% → 75.6%. -race stable across 5 consecutive runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fd.build spawns a pollLoop goroutine on its own internal context, cancelled via SetOnClosed. We hand-populate files, call build, immediately Hide() the dialog (fires OnClosed → cancel), then sleep 100 ms to drain the goroutine before the test returns. build 0% → 86%; gui pkg 75.6% → 79.4%. -race stable across 5 consecutive runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CompPub.RefreshNow goroutine completes quickly on a fresh daemon (no indexed content → no work → returns nil); the fyne.Do(ShowError) arm doesn't fire so the goroutine doesn't bleed Fyne work. A 200 ms drain keeps it bounded. refreshPublisher 28.6% → 71.4%; gui pkg 79.4% → 79.6%. -race stable across 5 consecutive runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restored TestSetAllPrioritiesWithFiles (with 500 ms drain after calling setAllPriorities on non-empty files + unknown infohash). The goroutine's SetFilePriority loop fails per-file, populates failed slice, fyne.Do(ShowError) fires — all bounded within the drain window so the goroutine doesn't bleed. setAllPriorities 61.5% → 100%; gui pkg 79.6% → 80.0%. -race stable across 5 consecutive runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
With all layer checkboxes off, runSearch's orchestrator goroutine spawns no per-layer goroutines, waits on an empty WaitGroup, then fyne.Do's buildResults to write the "No search layers enabled" status message. A 500 ms drain at end of test lets the goroutine fully complete; the test does NOT read statusLbl.Text from the test goroutine since that would race with buildResults' SetText. runSearch 6.4% → 61.7%; gui pkg 80.0% → 82.0%. -race stable across 5 consecutive runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restored TestRemoveSelectedConfirmAction. After tapping the confirm dialog's Yes button the goroutine fires Eng.RemoveTorrent + flips selected via fyne.Do; a 500 ms drain bounds it. removeSelected 63.6% → 95.5%; gui pkg 82.0% → 82.6%. -race stable across 5 consecutive runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restored TestCreateTorrentDialogConfirmArms with three input variants (empty root, empty output, valid both). The valid arm spawns runCreateTorrent's goroutine; a 500 ms drain bounds it. createTorrentDialog 62.8% → 78.2%; gui pkg 82.6% → 83.5%. -race stable across 5 consecutive runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.