fix(multi-qbit): scope IsReadyForImportAsync client lookup by instance#303
Conversation
PR #281 scoped Imported DB checks by qBit instance but IsReadyForImportAsync still read torrent completion/seeding state from the first client returning a matching hash. When the same hash exists on multiple instances with different progress (e.g. still downloading on qBit but complete on seedbox), import readiness for the seedbox copy could be evaluated against the wrong client. Align the client iteration filter with ImportTorrentAsync and add a regression test. Co-authored-by: Feramance <Feramance@users.noreply.github.com>
…ion test Co-authored-by: Feramance <Feramance@users.noreply.github.com>
5b9a351 to
76ff154
Compare
There was a problem hiding this comment.
Bug Scan Summary
Date: 2026-06-22
Commits inspected: a88f5d2..76ff154 (PR #303: d08c70a, 76ff154)
Result: No new critical bugs found.
PR #303 validation
Traced IsReadyForImportAsync from Branch 16 in ProcessSingleTorrentAsync (always passes torrent.QBitInstanceName). The added client-iteration guard matches the existing ImportTorrentAsync filter and correctly prevents evaluating seedbox completion/seeding state against the first matching hash on another instance.
Regression test IsReadyForImportAsync_ScopesQbitClientLookupByInstance exercises the reported failure mode (incomplete qBit copy vs complete seedbox copy). 777/777 non-live .NET tests pass.
Deduplication check
- Open PRs: searched
IsReadyForImportAsync,multi-qbit instance— no competing fixes - Recently merged: PR #281 (
8ef62f7) scoped DB Imported checks; this PR completes the client-lookup gap - Open issues: searched
IsReadyForImportAsync,multi-qbit— no duplicate trackers
Below confidence bar (not surfaced as PRs)
MeetsSeedingRequirementsAsync/GetSeedingStatsAsyncre-fetch by hash only insideUpdateSingleTorrentSeedingTagAsync; could mis-setAllowedSeedingwhen the same hash exists on multiple instances. Deletion/HnR paths use the per-instanceTorrentInfodirectly, so impact appears limited to tag sync.ImportPathTracker.IsHashAlreadyScannedis hash-global; a second instance with the same infohash may skip import after the first instance scans. May be intentional dedup — unclear whether a second Arr import is required when paths differ.
Sent by Cursor Automation: Torrentarr - Find critical bugs
There was a problem hiding this comment.
PR Validation (Cursor Automation)
Recommendation: Merge
Primary reason: PR #303 is already merged into master; validating the clean post-merge tree found passing build/tests and no open duplicate overlap.
Gates
| Gate | Status | Notes |
|---|---|---|
| Merge conflicts | Pass | git merge origin/master completed cleanly; conflicts: none. Current PR state is MERGED, and the local post-merge tree has no remaining diff vs origin/master. |
dotnet build |
Pass | dotnet build -c Release succeeded with 18 warnings and 0 errors. |
dotnet test (non-live) |
Pass | Exact command passed on rerun: Core 173, Host 203, Infrastructure 401, total 777 passed. Initial run hit transient ConfigReloaderTests env-var interference; Infrastructure-only and serialized reruns also passed. |
vitest |
Pass | npm ci && npx vitest run: 16 files passed, 154 tests passed. |
npm run build (webui) |
N/A | PR does not modify webui/; frontend production build not required. |
Validation
| Axis | Score | Notes |
|---|---|---|
| Purpose | Pass | Fixes a real multi-qBit bug where IsReadyForImportAsync could inspect the first qBit client containing a hash instead of the requested instance. |
| Correctness | Pass | Minimal fix filters client selection by requested qBit instance, aligns with ImportTorrentAsync/QBitInstanceName patterns, and does not introduce a default-client concept. |
| Tests | Pass | Adds a targeted regression test for same-hash, different-instance import readiness; post-merge test totals are above the stated baseline. |
| Hygiene | Pass | Narrow 3-file change, no generated artifacts, no temp files, no unrelated scope. |
| Overlap | Pass | No open PRs remain on the same theme; related #229 and #281 are merged prerequisites/foundational fixes, not competing open winners. |
Why
This PR closes a remaining multi-qBit scoping gap after earlier tagless/imported fixes: when the same torrent hash exists on multiple qBit instances, import readiness must be evaluated against the torrent copy from the instance currently being processed. The implementation keeps the existing all-client iteration pattern but skips non-matching instances when qbitInstance is supplied, then uses the resolved instance for the database lookup.
The validation branch merged current master cleanly. Since PR #303 was merged during/just before validation, the post-merge validation tree now matches origin/master for this change.
Overlap
None. Related PRs #229 and #281 are already merged and this PR is a focused follow-up, not a duplicate of a better open PR.
Commands run
git fetch origin mastergit fetch origin cursor/bug-finding-automation-cbb6git checkout -B pr-validate origin/cursor/bug-finding-automation-cbb6git merge origin/masterdotnet restoredotnet build -c Releasedotnet test -c Release --no-build --filter "Category!=Live"dotnet test tests/Torrentarr.Infrastructure.Tests/Torrentarr.Infrastructure.Tests.csproj -c Release --no-build --filter "Category!=Live"dotnet test -c Release --no-build --filter "Category!=Live" -m:1cd webui && npm ci && npx vitest rungh pr view 303,gh pr list --state open, related PR status checks
Sent by Cursor Automation: Torrentarr PR validation triage


Summary
Follow-up to #281: the merged fix scoped
ImportedDB lookups by qBit instance, butIsReadyForImportAsyncstill read torrent completion/seeding state from the first qBit client that returned a matching hash.Bug
When the same torrent hash exists on multiple qBit instances with different progress (e.g. still downloading on
qBitbut complete onqBit-seedbox),IsReadyForImportAsync(hash, "qBit-seedbox")could evaluate readiness using the primary instance's incomplete state and returnfalse, silently skipping import for a completed seedbox copy.ImportTorrentAsyncalready filtered by instance; this alignsIsReadyForImportAsyncwith the same logic.Changes
IsReadyForImportAsyncQBittorrentClient.GetTorrentsAsyncasvirtualto enable test stubsIsReadyForImportAsync_ScopesQbitClientLookupByInstanceTests
dotnet test --filter "Category!=Live"— 767 passed