Skip to content

Fix Seeker's filters allowing searching for all types of movies regardless of settings#567

Merged
Flaminel merged 3 commits into
mainfrom
fix_seeker_filter
Apr 16, 2026
Merged

Fix Seeker's filters allowing searching for all types of movies regardless of settings#567
Flaminel merged 3 commits into
mainfrom
fix_seeker_filter

Conversation

@Flaminel

@Flaminel Flaminel commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary by Sourcery

Tighten Seeker’s proactive search filtering so only items matching the configured criteria are searched, and extend coverage with comprehensive tests around Radarr/Sonarr filters, history, round‑robin behavior, replacement searches, dry runs, and notifications.

Bug Fixes:

  • Prevent Radarr and Sonarr proactive searches from including items that already have files when both cutoff and custom format score filters are disabled.

Tests:

  • Add extensive Radarr proactive search tests covering missing-only behavior, cutoff and custom format score combinations, search reasons, status filtering, history, command tracking, stale-history cleanup, total eligible item tracking, dry runs, disabled instances, download limits, round-robin behavior, replacement search edge cases, and SignalR notifications.
  • Add Sonarr proactive search tests ensuring filters respect missing-only mode, cutoff, custom format scores, monitored-only setting, skip tags, fully-downloaded series exclusions, and correct construction of series search items for replacements.

Summary by CodeRabbit

  • Bug Fixes

    • Corrected proactive search eligibility so items and episodes are no longer incorrectly excluded by cutoff and custom-format score logic.
  • Tests

    • Added extensive tests covering search eligibility, missing-only/monitored/tag filters, cutoff/format-score interactions, history persistence and cleanup, dry-run behavior, queue/replacement edge cases, download limits, round-robin handling, and search stats updates.

@Flaminel

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@Flaminel

Copy link
Copy Markdown
Contributor Author

@greptileai review

@Flaminel

Copy link
Copy Markdown
Contributor Author

/review

@qodo-code-review

qodo-code-review Bot commented Apr 16, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0)   📘 Rule violations (2)   📎 Requirement gaps (0)
📘\ ✧ Quality (2)

Grey Divider


Remediation recommended

1. Ineffective OR-logic test🐞
Description
ExecuteAsync_Radarr_UseCutoffAndUseCustomFormatScore_OrLogic only asserts that movie 4 was not
searched, but it never asserts that any search happened nor that movies qualifying via either side
of the OR (movie 2 via CF, movie 3 via cutoff) are actually searchable. This test can pass even if
the OR logic is implemented incorrectly (e.g., as AND) because capturedSearchItems may be empty
and still satisfy ShouldNotContain.
Code

code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs[R2151-2156]

+        // Act — run twice to pick both eligible candidates (selector picks one per run)
+        await sut.ExecuteAsync();
+        _fixture.RecreateDataContext();
+        // Re-check: at least the first search did not pick movie 4
+        capturedSearchItems.ShouldNotContain(item => item.Id == 4);
+    }
Relevance

⭐⭐⭐ High

Test can pass with empty capturedSearchItems; missing assertions for OR-eligibility makes it
ineffective.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The test’s Act section runs ExecuteAsync() once and then only checks that ID 4 is absent, which
would also be true if no search was triggered at all (empty capture list). In production code,
Radarr proactive searching selects at most one movie per run (selector.Select(..., 1)), so
validating OR behavior requires asserting that searches occur and that items qualifying via either
cutoff-not-met or CF-below-cutoff are included over multiple runs.

code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs[2068-2156]
code/backend/Cleanuparr.Infrastructure/Features/Jobs/Seeker.cs[515-523]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The test `ExecuteAsync_Radarr_UseCutoffAndUseCustomFormatScore_OrLogic` does not actually validate the intended OR behavior. It only asserts that movie `Id == 4` is not searched, but it never asserts that any search occurred or that movies qualifying via either condition (cutoff unmet OR CF score below cutoff) are eligible.
### Issue Context
`Seeker.ProcessRadarrAsync` selects **one** candidate per run (`selector.Select(..., 1)`), so a correct OR-logic test should either:
- run `ExecuteAsync()` multiple times (without recreating the DB/context) and assert that both eligible IDs appear across runs, or
- otherwise assert that at least one eligible ID was searched and that the eligible set includes items that only fail cutoff and items that only fail CF.
### Fix Focus Areas
- code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs[2068-2156]
- code/backend/Cleanuparr.Infrastructure/Features/Jobs/Seeker.cs[515-523]
### Suggested change
1. Remove `_fixture.RecreateDataContext()` from the test (it resets the database and can invalidate multi-run assertions).
2. Call `await sut.ExecuteAsync();` twice.
3. Assert:
 - `capturedSearchItems.ShouldNotBeEmpty()` (or assert received calls)
 - `capturedSearchItems.ShouldNotContain(i => i.Id == 4)`
 - `capturedSearchItems.ShouldContain(i => i.Id == 2)` and `...Id == 3` (or equivalently assert the set is `{2,3}` and count is 2).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

2. sut variable abbreviation 📘
Description
New tests use the identifier sut, which is an ambiguous abbreviation and not on the allowed
abbreviation list. This reduces readability and violates the requirement to avoid unclear
abbreviations in identifiers.
Code

code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs[761]

+        var sut = CreateSut();
Relevance

⭐ Low

sut is a standard test convention (“system under test”); teams typically allow it in test code.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 225597 requires avoiding ambiguous abbreviations in identifiers unless clearly
documented/approved; the added test code introduces sut as a variable name.

Rule 225597: Avoid ambiguous abbreviations in identifiers
code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs[761-761]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The added test code uses `sut` as a variable name, which is an ambiguous abbreviation.
## Issue Context
Compliance requires avoiding unclear abbreviations in identifiers unless explicitly documented as approved.
## Fix Focus Areas
- code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs[761-761]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. ci lambda parameter abbreviation 📘
Description
New NSubstitute callbacks use the parameter name ci, which is an ambiguous abbreviation and not on
the allowed abbreviation list. This reduces readability and violates the requirement to avoid
unclear abbreviations in identifiers.
Code

code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs[R751-754]

+            .Returns(ci =>
+            {
+                capturedSearchItem = ci.ArgAt<SearchItem>(1);
+                return 100L;
Relevance

⭐ Low

ci is a common NSubstitute CallInfo shorthand in tests; likely acceptable despite abbreviation
rule.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 225597 requires avoiding ambiguous abbreviations in identifiers unless clearly
documented/approved; the added callback uses ci as the parameter name.

Rule 225597: Avoid ambiguous abbreviations in identifiers
code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs[751-754]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The added NSubstitute callback uses `ci` as a lambda parameter name, which is an ambiguous abbreviation.
## Issue Context
Compliance requires avoiding unclear abbreviations in identifiers unless explicitly documented as approved.
## Fix Focus Areas
- code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs[751-754]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@coderabbitai

coderabbitai Bot commented Apr 16, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 453a76ab-cb42-478a-a84c-3bf9f35410c1

📥 Commits

Reviewing files that changed from the base of the PR and between 12dcf8b and 83e9847.

📒 Files selected for processing (1)
  • code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs
✅ Files skipped from review due to trivial changes (1)
  • code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs

📝 Walkthrough

Walkthrough

Adds extensive Seeker unit tests for Radarr and Sonarr proactive search behaviors and side effects, and removes a redundant exclusion clause from Seeker candidate filtering that depended on both UseCutoff and UseCustomFormatScore being false.

Changes

Cohort / File(s) Summary
Seeker Tests
code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs
Adds ~1460 lines of tests covering Radarr and Sonarr proactive search eligibility (MissingOnly, UseCutoff, UseCustomFormatScore, MonitoredOnly, SkipTags, release-state), history persistence (SeekerHistory, SeekerCommandTracker), stale-history cleanup, dry-run behavior, seeker instance & throttling edge cases, replacement/queue edge cases, and SignalR SearchStatsUpdated broadcast.
Seeker Filtering Logic
code/backend/Cleanuparr.Infrastructure/Features/Jobs/Seeker.cs
Removes a redundant exclusion clause from Radarr and Sonarr candidate/episode filtering: the composite predicate no longer treats items as ineligible only when both UseCutoff and UseCustomFormatScore are false, simplifying the no-file admission logic while keeping cutoff/format-score checks gated by their flags.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through tests in moonlit stacks,
Checked cutoffs, tags, and missing tracks,
Saved histories, nudged queues along,
Tuned a filter — small but strong,
A rabbit cheers: simpler code, happy hops!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main bug fix: correcting Seeker's filtering logic to prevent searching for all movies regardless of settings. It directly aligns with the primary change in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix_seeker_filter

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai

sourcery-ai Bot commented Apr 16, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adjusts Seeker’s Radarr and Sonarr proactive-search filters so that, when cutoff/custom-format filters are disabled, only genuinely missing items are eligible for search (not items that already have files), and adds an extensive test suite covering Radarr/Sonarr filtering, history/cleanup, dry-run behavior, round-robin, replacement queue handling, and SignalR notifications.

File-Level Changes

Change Details Files
Tighten Radarr proactive search candidate selection to exclude movies with existing files unless they fail cutoff or custom-format filters.
  • Remove the condition that allowed movies with files to be eligible when both UseCutoff and UseCustomFormatScore are disabled, ensuring only missing or below-cutoff movies are searched.
  • Keep existing logic that includes movies whose movie file has QualityCutoffNotMet when UseCutoff is enabled.
  • Keep existing logic that includes movies whose custom format score is below cutoff when UseCustomFormatScore is enabled.
code/backend/Cleanuparr.Infrastructure/Features/Jobs/Seeker.cs
Tighten Sonarr proactive search candidate selection to exclude episodes with existing files unless they fail cutoff or custom-format filters.
  • Remove the condition that allowed episodes with files to be eligible when both UseCutoff and UseCustomFormatScore are disabled, ensuring only missing or below-cutoff episodes are searched.
  • Keep existing logic that includes episodes whose episode file has QualityCutoffNotMet when UseCutoff is enabled.
  • Keep existing logic that includes episodes whose custom format score is below cutoff when UseCustomFormatScore is enabled.
code/backend/Cleanuparr.Infrastructure/Features/Jobs/Seeker.cs
Add comprehensive Radarr proactive-search tests to validate missing-only behavior, cutoff/custom-format filtering, OR-combination logic, status-based inclusion, search reasons, history/command-tracker updates, stale-history cleanup, total eligible item tracking, dry-run behavior, disabled instance handling, active-download limit semantics, round-robin disabled behavior, replacement-queue edge cases, and SignalR notifications.
  • Add tests to confirm that when both UseCutoff and UseCustomFormatScore are disabled, only missing Radarr movies are searched and movies with files are excluded.
  • Add tests for UseCustomFormatScore-only and combined UseCutoff+UseCustomFormatScore logic to ensure movies are eligible if they fail either filter and excluded when both pass.
  • Add tests to verify non-released movies are excluded, correct SeekerSearchReason is published, search history and command tracker records are persisted as expected, stale and old-cycle history entries are cleaned up, and TotalEligibleItems is updated.
  • Add tests to verify dry-run proactive searches still trigger search but only mark history as IsDryRun and do not persist command trackers, and that disabled instances or round-robin configuration behave as intended.
  • Add tests to verify replacement queue items are correctly transformed into search items, are dequeued even on search failure, and that SignalR SearchStatsUpdated is sent after proactive searches.
code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs
Add comprehensive Sonarr proactive-search tests to validate missing-only behavior, cutoff/custom-format filtering, monitored-only and skip-tag handling, fully-downloaded-series behavior, and season-level search item construction.
  • Add tests to confirm that when both UseCutoff and UseCustomFormatScore are disabled, only missing Sonarr episodes are searched and episodes with files are excluded, with season-level search items built accordingly.
  • Add tests for UseCutoff-only and UseCustomFormatScore-only configurations to ensure episodes whose files fail cutoff or whose custom format score is below cutoff remain eligible while others are excluded.
  • Add tests to confirm MonitoredOnly and SkipTags are respected at the series level and that fully downloaded series are skipped when only missing items are targeted.
code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The new test coverage is very thorough but makes SeekerTests extremely long and repetitive; consider extracting common arrange/setup patterns into helper methods or test data builders to keep individual tests focused on behavior and easier to maintain.
  • Several tests reconfigure SeekerConfigs and SeekerInstanceConfigs in slightly different ways; introducing factory/helper methods for creating instance configs with specific combinations of UseCutoff, UseCustomFormatScore, and MonitoredOnly would reduce boilerplate and help avoid configuration mistakes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new test coverage is very thorough but makes `SeekerTests` extremely long and repetitive; consider extracting common arrange/setup patterns into helper methods or test data builders to keep individual tests focused on behavior and easier to maintain.
- Several tests reconfigure `SeekerConfigs` and `SeekerInstanceConfigs` in slightly different ways; introducing factory/helper methods for creating instance configs with specific combinations of `UseCutoff`, `UseCustomFormatScore`, and `MonitoredOnly` would reduce boilerplate and help avoid configuration mistakes.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs`:
- Around line 2068-2156: The test
ExecuteAsync_Radarr_UseCutoffAndUseCustomFormatScore_OrLogic currently can pass
without any search happening; update it to actually assert searches occurred by
(1) invoking sut.ExecuteAsync() twice as intended (call it a second time instead
of recreating context immediately) or keep the RecreateDataContext() but ensure
you call sut.ExecuteAsync() again after recreation, (2) assert that
capturedSearchItems has at least one entry after the first run (e.g.,
capturedSearchItems.Count > 0) and/or at least two entries after the second run
before asserting capturedSearchItems.ShouldNotContain(item => item.Id == 4), and
(3) keep using mockArrClient.SearchItemAsync interception to populate
capturedSearchItems so the assertions validate that a search actually executed;
references: test method
ExecuteAsync_Radarr_UseCutoffAndUseCustomFormatScore_OrLogic,
sut.ExecuteAsync(), capturedSearchItems, mockArrClient.SearchItemAsync, and
_fixture.RecreateDataContext().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 37de8100-a090-443a-99cb-11a737b945ae

📥 Commits

Reviewing files that changed from the base of the PR and between ee5e7c0 and b33c2f7.

📒 Files selected for processing (2)
  • code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs
  • code/backend/Cleanuparr.Infrastructure/Features/Jobs/Seeker.cs
💤 Files with no reviewable changes (1)
  • code/backend/Cleanuparr.Infrastructure/Features/Jobs/Seeker.cs

@codecov

codecov Bot commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Apr 16, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a bug where Seeker's proactive movie search was ignoring the UseCutoff and UseCustomFormatScore toggle settings, causing movies that already had an acceptable file to be re-searched regardless of configuration. The fix adds the required instanceConfig.UseCutoff && and instanceConfig.UseCustomFormatScore && guards to the movie candidate filter in ProcessRadarrAsync, and a comprehensive set of new tests covers the corrected behavior across missing-only, cutoff, CF-score, queue-exclusion, grace period, and cycle-management scenarios.

Confidence Score: 5/5

Safe to merge — the production fix is correct and well-tested; only remaining finding is a minor test assertion gap.

The filter fix is targeted and correct, the Sonarr path was already correctly gated, and the new test suite covers the main regression extensively. The single P2 finding is a test quality issue (weak assertion in one test) with no impact on production behavior.

No files require special attention for the production fix; the test assertion gap in SeekerTests.cs is worth addressing but is not blocking.

Important Files Changed

Filename Overview
code/backend/Cleanuparr.Infrastructure/Features/Jobs/Seeker.cs Core fix: ProcessRadarrAsync candidate filter now correctly gates on instanceConfig.UseCutoff and instanceConfig.UseCustomFormatScore before including movies-with-files; Sonarr path already had the correct guards. No regressions introduced.
code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs Comprehensive new tests for missing-only, UseCutoff, UseCustomFormatScore, OR-logic, queue exclusion, grace period, and cycle management; one test (ExecuteAsync_Radarr_UseCutoffAndUseCustomFormatScore_OrLogic) has a weak assertion that passes even when zero searches are triggered, and contains an orphaned RecreateDataContext() call.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[ExecuteAsync] --> B{SearchEnabled?}
    B -- No --> Z[Return]
    B -- Yes --> C{ReplacementItem in queue?}
    C -- Yes --> D[ProcessReplacementItemAsync\nSearch & remove from queue]
    D --> Z
    C -- No --> E{ProactiveSearchEnabled?}
    E -- No --> Z
    E -- Yes --> F[ProcessProactiveSearchAsync\nLoad enabled SeekerInstanceConfigs]
    F --> G{UseRoundRobin?}
    G -- Yes --> H[Order by oldest LastProcessedAt\nProcess first instance that triggers a search]
    G -- No --> I[Process all instances sequentially]
    H --> J[ProcessSingleInstanceAsync]
    I --> J
    J --> K{ActiveDownloadLimit reached?}
    K -- Yes --> Z2[Skip instance]
    K -- No --> L[ProcessInstanceAsync]
    L --> M{InstanceType}
    M -- Radarr --> N[ProcessRadarrAsync\nFetch movies + tags + CF scores]
    M -- Sonarr --> O[ProcessSonarrAsync\nFetch series + episodes]
    N --> P{Filter candidates}
    P --> P1["• Status = released\n• Past grace period\n• MonitoredOnly filter\n• SkipTags filter\n• !HasFile\n  OR UseCutoff AND QualityCutoffNotMet\n  OR UseCustomFormatScore AND CF score below cutoff"]
    P1 --> Q{Candidates empty?}
    Q -- No --> R[Exclude queued movies\nCycle management\nSelect one via IItemSelector\nSearchItemAsync]
    Q -- Yes --> Z2
Loading

Reviews (1): Last reviewed commit: "added more tests" | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs (1)

2068-2158: ⚠️ Potential issue | 🟠 Major

This still doesn’t prove both OR branches work.

This test is stronger now, but one ExecuteAsync() call still only proves that either movie 2 or movie 3 was eligible. It will still pass if one side of the UseCutoff || UseCustomFormatScore logic regresses and only the other branch remains searchable. Run twice and assert both 2 and 3 are searched, while 4 never is.

Suggested tightening
-        // Act
-        await sut.ExecuteAsync();
+        // Act
+        await sut.ExecuteAsync();
+        await sut.ExecuteAsync();

-        // Assert — a search happened, and the excluded candidate (both filters met) was never selected
-        capturedSearchItems.ShouldNotBeEmpty();
-        capturedSearchItems.ShouldAllBe(item => item.Id == 2 || item.Id == 3);
+        // Assert — both OR branches produced an eligible search, and the excluded candidate never did
+        await mockArrClient.Received(2)
+            .SearchItemAsync(radarrInstance, Arg.Any<SearchItem>());
+        capturedSearchItems.ShouldContain(item => item.Id == 2);
+        capturedSearchItems.ShouldContain(item => item.Id == 3);
         capturedSearchItems.ShouldNotContain(item => item.Id == 4);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs`
around lines 2068 - 2158, The test only calls sut.ExecuteAsync() once so it
doesn't guarantee both OR branches (UseCutoff vs UseCustomFormatScore) are
exercised; modify ExecuteAsync_Radarr_UseCutoffAndUseCustomFormatScore_OrLogic
to call await sut.ExecuteAsync() a second time (or run the selection flow twice)
and then assert that capturedSearchItems contains both Id 2 and Id 3 (and still
never contains Id 4), using the existing capturedSearchItems list populated by
the mockArrClient.SearchItemAsync to verify both branches ran; keep all other
setup (radarrInstance, CustomFormatScoreEntry entries, mockArrClient,
ArrClientFactory wiring) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs`:
- Around line 2277-2509: The three Sonarr tests only assert the single captured
SeriesSearchItem and can miss a bug where filtered seasons still get searched
later; update each test
(ExecuteAsync_Sonarr_MissingOnly_ExcludesEpisodesWithFiles,
ExecuteAsync_Sonarr_UseCutoff_ExcludesEpisodesMeetingCutoff,
ExecuteAsync_Sonarr_UseCustomFormatScore_ExcludesEpisodesAboveCutoff) to capture
every SearchItem passed to mockArrClient.SearchItemAsync (e.g., append
ci.ArgAt<SearchItem>(1) cast to SeriesSearchItem into a local list) across the
sut.ExecuteAsync() run(s) and then assert that the list contains the expected
allowed season(s) and does NOT contain the excluded season (e.g., no
SeriesSearchItem with Id == 2); keep using the existing mockArrClient,
SeriesSearchItem type, SearchItemAsync method, CreateSut and ExecuteAsync to
locate where to change the test capture and final assertions.
- Around line 1988-2066: The test currently only asserts that some searches
occurred, which can pass due to movie 1 being missing; update
ExecuteAsync_Radarr_UseCustomFormatScore_ExcludesMoviesAboveCutoff to explicitly
assert the custom-format branch by checking capturedSearchItems contains a
SearchItem for movie with Id == 2 (the below-cutoff CF entry) and still does not
contain Id == 3 (above-cutoff), e.g. assert capturedSearchItems.Any(item =>
item.Id == 2) and capturedSearchItems.All(item => item.Id != 3); use the
existing capturedSearchItems variable to locate and assert the expected entries
so the test verifies CF filtering logic in the SUT's ExecuteAsync path.

---

Duplicate comments:
In `@code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs`:
- Around line 2068-2158: The test only calls sut.ExecuteAsync() once so it
doesn't guarantee both OR branches (UseCutoff vs UseCustomFormatScore) are
exercised; modify ExecuteAsync_Radarr_UseCutoffAndUseCustomFormatScore_OrLogic
to call await sut.ExecuteAsync() a second time (or run the selection flow twice)
and then assert that capturedSearchItems contains both Id 2 and Id 3 (and still
never contains Id 4), using the existing capturedSearchItems list populated by
the mockArrClient.SearchItemAsync to verify both branches ran; keep all other
setup (radarrInstance, CustomFormatScoreEntry entries, mockArrClient,
ArrClientFactory wiring) unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d4cbab3-0b90-4e2a-a63c-cdf8452f8bf6

📥 Commits

Reviewing files that changed from the base of the PR and between b33c2f7 and 12dcf8b.

📒 Files selected for processing (1)
  • code/backend/Cleanuparr.Infrastructure.Tests/Features/Jobs/SeekerTests.cs

@Flaminel Flaminel force-pushed the fix_seeker_filter branch from 12dcf8b to 83e9847 Compare April 16, 2026 14:54
@Flaminel Flaminel merged commit 89a0d12 into main Apr 16, 2026
13 of 14 checks passed
@Flaminel Flaminel deleted the fix_seeker_filter branch April 16, 2026 15:21
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