mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-10-28 10:48:49 -04:00
fixed live run colliding with dry cycle row
This commit is contained in:
1 parent
d5ea35445f
commit
e32cee6f55
2 files changed
+98
-4
No files matched your search
@@ -7672,5 +7672,83 @@ public class SeekerTests : IDisposable
|
||||
historyRows[0].LastOutcome.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecuteAsync_Radarr_LiveRun_ReclaimsDryCycleRowWithSameId()
|
||||
{
|
||||
// Arrange: the migration left an all-dry cycle row under the live CurrentCycleId
|
||||
var config = await _fixture.DataContext.SeekerConfigs.FirstAsync();
|
||||
config.SearchEnabled = true;
|
||||
config.ProactiveSearchEnabled = true;
|
||||
await _fixture.DataContext.SaveChangesAsync();
|
||||
|
||||
var radarrInstance = TestDataContextFactory.AddRadarrInstance(_fixture.DataContext);
|
||||
var currentCycleId = Guid.NewGuid();
|
||||
|
||||
_fixture.DataContext.SeekerInstanceConfigs.Add(new SeekerInstanceConfig
|
||||
{
|
||||
ArrInstanceId = radarrInstance.Id,
|
||||
ArrInstance = radarrInstance,
|
||||
Enabled = true,
|
||||
MonitoredOnly = false,
|
||||
CurrentCycleId = currentCycleId
|
||||
});
|
||||
|
||||
_fixture.EventsContext.SeekerCycles.Add(new SeekerCycle
|
||||
{
|
||||
Id = currentCycleId,
|
||||
ArrInstanceId = radarrInstance.Id,
|
||||
StartedAt = _fixture.TimeProvider.GetUtcNow().AddDays(-10),
|
||||
CompletedAt = _fixture.TimeProvider.GetUtcNow().AddDays(-5),
|
||||
CompletionReason = SeekerCycleCompletionReason.Completed,
|
||||
SearchedUnits = 3,
|
||||
GrabbedUnits = 1,
|
||||
IsDryRun = true
|
||||
});
|
||||
await _fixture.DataContext.SaveChangesAsync();
|
||||
await _fixture.EventsContext.SaveChangesAsync();
|
||||
|
||||
var mockArrClient = Substitute.For<IArrClient>();
|
||||
|
||||
_fixture.ArrQueueReader
|
||||
.ReadAllAsync(mockArrClient, Arg.Any<ArrInstance>())
|
||||
.Returns([]);
|
||||
|
||||
_radarrClient
|
||||
.StreamAllMoviesAsync(radarrInstance, Arg.Any<CancellationToken>())
|
||||
.Returns(ToAsyncEnumerable<SearchableMovie>([
|
||||
new SearchableMovie { Id = 1, Title = "Movie 1", Status = "released", Monitored = true, HasFile = false, Tags = [] }
|
||||
]));
|
||||
StubRadarrWanted(radarrInstance, [
|
||||
new SearchableMovie { Id = 1, Title = "Movie 1", Status = "released", Monitored = true, HasFile = false, Tags = [] }
|
||||
]);
|
||||
|
||||
mockArrClient
|
||||
.SearchItemAsync(radarrInstance, Arg.Any<SearchItem>())
|
||||
.Returns(100L);
|
||||
|
||||
_fixture.ArrClientFactory
|
||||
.GetClient(InstanceType.Radarr, Arg.Any<float>())
|
||||
.Returns(mockArrClient);
|
||||
|
||||
var sut = CreateSut();
|
||||
|
||||
// Act
|
||||
await sut.ExecuteAsync();
|
||||
|
||||
// Assert: no PK collision, the row is reclaimed as a fresh live cycle, and the search dispatched
|
||||
await mockArrClient.Received(1)
|
||||
.SearchItemAsync(radarrInstance, Arg.Any<SearchItem>());
|
||||
|
||||
List<SeekerCycle> cycles = await _fixture.EventsContext.SeekerCycles
|
||||
.Where(c => c.Id == currentCycleId)
|
||||
.ToListAsync();
|
||||
cycles.Count.ShouldBe(1);
|
||||
cycles[0].IsDryRun.ShouldBeFalse();
|
||||
cycles[0].SearchedUnits.ShouldBe(1);
|
||||
cycles[0].GrabbedUnits.ShouldBe(0);
|
||||
cycles[0].CompletedAt.ShouldBeNull();
|
||||
cycles[0].CompletionReason.ShouldBeNull();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1092,8 +1092,7 @@ public sealed class Seeker : IHandler
|
||||
Guid currentCycleId = instanceConfig.CurrentCycleId;
|
||||
List<SeekerCycle> openOrCurrent = await _eventsContext.SeekerCycles
|
||||
.Where(c => c.ArrInstanceId == arrInstanceId
|
||||
&& !c.IsDryRun
|
||||
&& (c.Id == currentCycleId || c.CompletedAt == null))
|
||||
&& (c.Id == currentCycleId || (!c.IsDryRun && c.CompletedAt == null)))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// The migration guesses the open cycle from search events, the config holds the real one.
|
||||
@@ -1110,7 +1109,10 @@ public sealed class Seeker : IHandler
|
||||
SeekerCycle? cycle = openOrCurrent.FirstOrDefault(c => c.Id == currentCycleId);
|
||||
if (cycle is not null)
|
||||
{
|
||||
return cycle;
|
||||
// The all-dry-events migration can hand this id to a dry cycle; a live run reclaims it instead of colliding with it.
|
||||
return cycle.IsDryRun
|
||||
? OpenCycle(arrInstanceId, isDryRun: false, eligibleAtStart: 0, reclaim: cycle)
|
||||
: cycle;
|
||||
}
|
||||
|
||||
List<DateTimeOffset> searchedAt = await _eventsContext.SeekerHistory
|
||||
@@ -1128,8 +1130,22 @@ public sealed class Seeker : IHandler
|
||||
return cycle;
|
||||
}
|
||||
|
||||
private SeekerCycle OpenCycle(Guid arrInstanceId, bool isDryRun, int eligibleAtStart, Guid? id = null)
|
||||
private SeekerCycle OpenCycle(Guid arrInstanceId, bool isDryRun, int eligibleAtStart, Guid? id = null, SeekerCycle? reclaim = null)
|
||||
{
|
||||
if (reclaim is not null)
|
||||
{
|
||||
reclaim.StartedAt = _timeProvider.GetUtcNow();
|
||||
reclaim.EligibleAtStart = eligibleAtStart;
|
||||
reclaim.EligibleNow = eligibleAtStart;
|
||||
reclaim.RemainingUnits = 0;
|
||||
reclaim.SearchedUnits = 0;
|
||||
reclaim.GrabbedUnits = 0;
|
||||
reclaim.CompletedAt = null;
|
||||
reclaim.CompletionReason = null;
|
||||
reclaim.IsDryRun = isDryRun;
|
||||
return reclaim;
|
||||
}
|
||||
|
||||
SeekerCycle cycle = new()
|
||||
{
|
||||
Id = id ?? Guid.NewGuid(),
|
||||
|
||||
Reference in new issue
Block a user