mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-10-28 10:48:49 -04:00
94 lines
3.2 KiB
C#
94 lines
3.2 KiB
C#
using Cleanuparr.Domain.Enums;
|
|
using Cleanuparr.Infrastructure.Events.Interfaces;
|
|
using Cleanuparr.Infrastructure.Features.Context;
|
|
using Cleanuparr.Infrastructure.Features.ItemStriker;
|
|
using Cleanuparr.Infrastructure.Interceptors;
|
|
using Cleanuparr.Infrastructure.Tests.TestHelpers;
|
|
using Cleanuparr.Persistence;
|
|
using Cleanuparr.Persistence.Models.State;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
using NSubstitute;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace Cleanuparr.Infrastructure.Tests.Services;
|
|
|
|
/// <summary>
|
|
/// The *arr apps report an uppercase download id.
|
|
/// The download clients report a lowercase hash.
|
|
/// Both casings are one torrent: striking through either lands on the same lowercase row.
|
|
/// Struck against a real migrated database, since the invariant is the stored column's.
|
|
/// </summary>
|
|
public sealed class StrikerHashCasingTests : IAsyncLifetime
|
|
{
|
|
private const string UppercaseHash = "ABCDEF0123456789ABCDEF0123456789ABCDEF01";
|
|
|
|
private const string LowercaseHash = "abcdef0123456789abcdef0123456789abcdef01";
|
|
|
|
private readonly SqliteTestDatabase _events = SqliteTestDatabase.Create("striker-hash-casing");
|
|
|
|
private readonly Guid _jobRunId = Guid.CreateVersion7();
|
|
|
|
public async Task InitializeAsync()
|
|
{
|
|
await using EventsContext events = CreateContext();
|
|
await events.Database.MigrateAsync();
|
|
|
|
events.JobRuns.Add(new JobRun { Id = _jobRunId, Type = JobType.QueueCleaner });
|
|
await events.SaveChangesAsync();
|
|
|
|
Striker.RecurringHashes.Clear();
|
|
}
|
|
|
|
public Task DisposeAsync()
|
|
{
|
|
_events.Dispose();
|
|
Striker.RecurringHashes.Clear();
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Same_torrent_struck_through_both_casings_shares_one_download_item()
|
|
{
|
|
// The context is async local, so the strikes must claim the job run from inside the test.
|
|
ContextProvider.SetJobRunId(_jobRunId);
|
|
|
|
await using (EventsContext arrContext = CreateContext())
|
|
{
|
|
await CreateStriker(arrContext).StrikeAndCheckLimit(UppercaseHash, "Test Item", 3, StrikeType.Stalled);
|
|
}
|
|
|
|
await using (EventsContext clientContext = CreateContext())
|
|
{
|
|
await CreateStriker(clientContext).StrikeAndCheckLimit(LowercaseHash, "Test Item", 3, StrikeType.Stalled);
|
|
}
|
|
|
|
await using EventsContext assertions = CreateContext();
|
|
List<DownloadItem> items = await assertions.DownloadItems
|
|
.AsNoTracking()
|
|
.Include(d => d.Strikes)
|
|
.OrderBy(d => d.DownloadId)
|
|
.ToListAsync();
|
|
|
|
items.Count.ShouldBe(1);
|
|
items[0].DownloadId.ShouldBe(LowercaseHash);
|
|
items[0].Strikes.Count.ShouldBe(2);
|
|
}
|
|
|
|
private static Striker CreateStriker(EventsContext context)
|
|
{
|
|
IDryRunInterceptor dryRunInterceptor = Substitute.For<IDryRunInterceptor>();
|
|
dryRunInterceptor.IsDryRunEnabled().Returns(false);
|
|
|
|
return new Striker(
|
|
Substitute.For<ILogger<Striker>>(),
|
|
context,
|
|
Substitute.For<IEventPublisher>(),
|
|
dryRunInterceptor);
|
|
}
|
|
|
|
private EventsContext CreateContext() => _events.CreateContext<EventsContext>();
|
|
}
|