diff --git a/src/Recyclarr.Cli/Cache/CacheAutofacModule.cs b/src/Recyclarr.Cli/Cache/CacheAutofacModule.cs index 5b90af31..7c6ffc0c 100644 --- a/src/Recyclarr.Cli/Cache/CacheAutofacModule.cs +++ b/src/Recyclarr.Cli/Cache/CacheAutofacModule.cs @@ -6,7 +6,7 @@ public class CacheAutofacModule : Module { protected override void Load(ContainerBuilder builder) { - // Clients must register their own implementation of ICacheStoragePath + builder.RegisterType().As(); builder.RegisterType().As(); } } diff --git a/src/Recyclarr.Cli/Cache/CachePersister.cs b/src/Recyclarr.Cli/Cache/CachePersister.cs deleted file mode 100644 index 255d848a..00000000 --- a/src/Recyclarr.Cli/Cache/CachePersister.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Text.Json; -using Recyclarr.Config.Models; - -namespace Recyclarr.Cli.Cache; - -public class CachePersister(ILogger log, IServiceCache serviceCache) : ICachePersister -{ - public CustomFormatCache Load(IServiceConfiguration config) - { - var cache = serviceCache.Load(config); - if (cache == null) - { - log.Debug("Custom format cache does not exist; proceeding without it"); - return new CustomFormatCache(); - } - - // If the version is higher OR lower, we invalidate the cache. It means there's an - // incompatibility that we do not support. - if (cache.Version != CustomFormatCache.LatestVersion) - { - log.Information("Cache version mismatch ({OldVersion} vs {LatestVersion}); ignoring cache data", - cache.Version, CustomFormatCache.LatestVersion); - throw new CacheException("Version mismatch"); - } - - return cache; - } - - public void Save(IServiceConfiguration config, CustomFormatCache cache) - { - log.Debug("Saving Cache with {Mappings}", JsonSerializer.Serialize(cache.TrashIdMappings)); - - serviceCache.Save(cache, config); - } -} diff --git a/src/Recyclarr.Cli/Console/Helpers/CacheStoragePath.cs b/src/Recyclarr.Cli/Cache/CacheStoragePath.cs similarity index 96% rename from src/Recyclarr.Cli/Console/Helpers/CacheStoragePath.cs rename to src/Recyclarr.Cli/Cache/CacheStoragePath.cs index ec26d489..218f9d5f 100644 --- a/src/Recyclarr.Cli/Console/Helpers/CacheStoragePath.cs +++ b/src/Recyclarr.Cli/Cache/CacheStoragePath.cs @@ -5,7 +5,7 @@ using System.Text; using Recyclarr.Config.Models; using Recyclarr.Platform; -namespace Recyclarr.Cli.Console.Helpers; +namespace Recyclarr.Cli.Cache; public class CacheStoragePath(IAppPaths paths) : ICacheStoragePath { diff --git a/src/Recyclarr.Cli/Cache/CustomFormatCache.cs b/src/Recyclarr.Cli/Cache/CustomFormatCache.cs deleted file mode 100644 index 73732522..00000000 --- a/src/Recyclarr.Cli/Cache/CustomFormatCache.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Recyclarr.Cli.Pipelines.CustomFormat; -using Recyclarr.TrashGuide.CustomFormat; - -namespace Recyclarr.Cli.Cache; - -[CacheObjectName("custom-format-cache")] -public record CustomFormatCache -{ - public const int LatestVersion = 1; - - public int Version { get; init; } = LatestVersion; - - [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")] - public string? InstanceName { get; init; } - - public IReadOnlyList TrashIdMappings { get; init; } = new List(); - - public CustomFormatCache Update(CustomFormatTransactionData transactions) - { - // Assume that RemoveStale() is called before this method, and that TrashIdMappings contains existing CFs - // in the remote service that we want to keep and update. - - var existingCfs = transactions.UpdatedCustomFormats - .Concat(transactions.UnchangedCustomFormats) - .Concat(transactions.NewCustomFormats); - - return this with - { - TrashIdMappings = TrashIdMappings - .DistinctBy(x => x.CustomFormatId) - .Where(x => transactions.DeletedCustomFormats.All(y => y.CustomFormatId != x.CustomFormatId)) - .FullOuterJoin(existingCfs, JoinType.Hash, - l => l.CustomFormatId, - r => r.Id, - // Keep existing service CFs, even if they aren't in user config - l => l, - // Add a new mapping for CFs in user's config - r => new TrashIdMapping(r.TrashId, r.Name, r.Id), - // Update existing mappings for CFs in user's config - (l, r) => l with {TrashId = r.TrashId, CustomFormatName = r.Name}) - .Where(x => x.CustomFormatId != 0) - .OrderBy(x => x.CustomFormatId) - .ToList() - }; - } - - public CustomFormatCache RemoveStale(IEnumerable serviceCfs) - { - return this with - { - TrashIdMappings = TrashIdMappings - .Where(x => x.CustomFormatId != 0 && serviceCfs.Any(y => y.Id == x.CustomFormatId)) - .ToList() - }; - } - - public int? FindId(CustomFormatData cf) - { - return TrashIdMappings.FirstOrDefault(c => c.TrashId == cf.TrashId)?.CustomFormatId; - } -} - -public record TrashIdMapping(string TrashId, string CustomFormatName, int CustomFormatId); diff --git a/src/Recyclarr.Cli/Console/Helpers/ICacheStoragePath.cs b/src/Recyclarr.Cli/Cache/ICacheStoragePath.cs similarity index 81% rename from src/Recyclarr.Cli/Console/Helpers/ICacheStoragePath.cs rename to src/Recyclarr.Cli/Cache/ICacheStoragePath.cs index 87ad3a62..f783028d 100644 --- a/src/Recyclarr.Cli/Console/Helpers/ICacheStoragePath.cs +++ b/src/Recyclarr.Cli/Cache/ICacheStoragePath.cs @@ -1,7 +1,7 @@ using System.IO.Abstractions; using Recyclarr.Config.Models; -namespace Recyclarr.Cli.Console.Helpers; +namespace Recyclarr.Cli.Cache; public interface ICacheStoragePath { diff --git a/src/Recyclarr.Cli/Cache/ServiceCache.cs b/src/Recyclarr.Cli/Cache/ServiceCache.cs index 381b054e..9cf387fe 100644 --- a/src/Recyclarr.Cli/Cache/ServiceCache.cs +++ b/src/Recyclarr.Cli/Cache/ServiceCache.cs @@ -2,7 +2,6 @@ using System.IO.Abstractions; using System.Reflection; using System.Text.Json; using System.Text.RegularExpressions; -using Recyclarr.Cli.Console.Helpers; using Recyclarr.Common.Extensions; using Recyclarr.Config.Models; using Recyclarr.Json; @@ -23,12 +22,10 @@ public partial class ServiceCache(ICacheStoragePath storagePath, ILogger log) : return null; } - using var stream = path.OpenText(); - var json = stream.ReadToEnd(); - try { - return JsonSerializer.Deserialize(json, _jsonSettings); + using var stream = path.OpenRead(); + return JsonSerializer.Deserialize(stream, _jsonSettings); } catch (JsonException e) { diff --git a/src/Recyclarr.Cli/CompositionRoot.cs b/src/Recyclarr.Cli/CompositionRoot.cs index c19113aa..d6824443 100644 --- a/src/Recyclarr.Cli/CompositionRoot.cs +++ b/src/Recyclarr.Cli/CompositionRoot.cs @@ -4,7 +4,6 @@ using Autofac; using Autofac.Extras.Ordering; using AutoMapper.Contrib.Autofac.DependencyInjection; using Recyclarr.Cli.Cache; -using Recyclarr.Cli.Console.Helpers; using Recyclarr.Cli.Console.Setup; using Recyclarr.Cli.Logging; using Recyclarr.Cli.Migration; @@ -58,7 +57,6 @@ public static class CompositionRoot builder.RegisterModule(); builder.RegisterModule(); - builder.RegisterType().As(); builder.RegisterType().As(); builder.Register(_ => new ResourceDataReader(thisAssembly)).As(); diff --git a/src/Recyclarr.Cli/Pipelines/CustomFormat/Cache/CustomFormatCache.cs b/src/Recyclarr.Cli/Pipelines/CustomFormat/Cache/CustomFormatCache.cs new file mode 100644 index 00000000..0ae35a08 --- /dev/null +++ b/src/Recyclarr.Cli/Pipelines/CustomFormat/Cache/CustomFormatCache.cs @@ -0,0 +1,46 @@ +using Recyclarr.TrashGuide.CustomFormat; + +namespace Recyclarr.Cli.Pipelines.CustomFormat.Cache; + +public class CustomFormatCache(IEnumerable mappings) +{ + private List _mappings = mappings.ToList(); // Deep clone with ToList() + + public IReadOnlyList Mappings => _mappings; + + public void Update(CustomFormatTransactionData transactions) + { + // Assume that RemoveStale() is called before this method, and that TrashIdMappings contains existing CFs + // in the remote service that we want to keep and update. + + var existingCfs = transactions.UpdatedCustomFormats + .Concat(transactions.UnchangedCustomFormats) + .Concat(transactions.NewCustomFormats); + + _mappings = _mappings + .DistinctBy(x => x.CustomFormatId) + .Where(x => transactions.DeletedCustomFormats.All(y => y.CustomFormatId != x.CustomFormatId)) + .FullOuterJoin(existingCfs, JoinType.Hash, + l => l.CustomFormatId, + r => r.Id, + // Keep existing service CFs, even if they aren't in user config + l => l, + // Add a new mapping for CFs in user's config + r => new TrashIdMapping(r.TrashId, r.Name, r.Id), + // Update existing mappings for CFs in user's config + (l, r) => l with {TrashId = r.TrashId, CustomFormatName = r.Name}) + .Where(x => x.CustomFormatId != 0) + .OrderBy(x => x.CustomFormatId) + .ToList(); + } + + public void RemoveStale(IEnumerable serviceCfs) + { + _mappings.RemoveAll(x => x.CustomFormatId == 0 || serviceCfs.All(y => y.Id != x.CustomFormatId)); + } + + public int? FindId(CustomFormatData cf) + { + return _mappings.Find(c => c.TrashId == cf.TrashId)?.CustomFormatId; + } +} diff --git a/src/Recyclarr.Cli/Pipelines/CustomFormat/Cache/CustomFormatCacheData.cs b/src/Recyclarr.Cli/Pipelines/CustomFormat/Cache/CustomFormatCacheData.cs new file mode 100644 index 00000000..1e568445 --- /dev/null +++ b/src/Recyclarr.Cli/Pipelines/CustomFormat/Cache/CustomFormatCacheData.cs @@ -0,0 +1,11 @@ +using Recyclarr.Cli.Cache; + +namespace Recyclarr.Cli.Pipelines.CustomFormat.Cache; + +public record TrashIdMapping(string TrashId, string CustomFormatName, int CustomFormatId); + +[CacheObjectName("custom-format-cache")] +public record CustomFormatCacheData( + int Version, + string InstanceName, + IReadOnlyCollection TrashIdMappings); diff --git a/src/Recyclarr.Cli/Pipelines/CustomFormat/Cache/CustomFormatCachePersister.cs b/src/Recyclarr.Cli/Pipelines/CustomFormat/Cache/CustomFormatCachePersister.cs new file mode 100644 index 00000000..9c17adc4 --- /dev/null +++ b/src/Recyclarr.Cli/Pipelines/CustomFormat/Cache/CustomFormatCachePersister.cs @@ -0,0 +1,38 @@ +using System.Text.Json; +using Recyclarr.Cli.Cache; +using Recyclarr.Config.Models; + +namespace Recyclarr.Cli.Pipelines.CustomFormat.Cache; + +public class CustomFormatCachePersister(ILogger log, IServiceCache serviceCache) : ICustomFormatCachePersister +{ + public const int LatestVersion = 1; + + public CustomFormatCache Load(IServiceConfiguration config) + { + var cacheData = serviceCache.Load(config); + if (cacheData == null) + { + log.Debug("Custom format cache does not exist; proceeding without it"); + cacheData = new CustomFormatCacheData(LatestVersion, config.InstanceName, []); + } + + // If the version is higher OR lower, we invalidate the cache. It means there's an + // incompatibility that we do not support. + if (cacheData.Version != LatestVersion) + { + log.Information("Cache version mismatch ({OldVersion} vs {LatestVersion}); ignoring cache data", + cacheData.Version, LatestVersion); + throw new CacheException("Version mismatch"); + } + + return new CustomFormatCache(cacheData.TrashIdMappings); + } + + public void Save(IServiceConfiguration config, CustomFormatCache cache) + { + var data = new CustomFormatCacheData(LatestVersion, config.InstanceName, cache.Mappings); + log.Debug("Saving Custom Format Cache with {Mappings}", JsonSerializer.Serialize(data.TrashIdMappings)); + serviceCache.Save(data, config); + } +} diff --git a/src/Recyclarr.Cli/Cache/ICachePersister.cs b/src/Recyclarr.Cli/Pipelines/CustomFormat/Cache/ICustomFormatCachePersister.cs similarity index 62% rename from src/Recyclarr.Cli/Cache/ICachePersister.cs rename to src/Recyclarr.Cli/Pipelines/CustomFormat/Cache/ICustomFormatCachePersister.cs index 512f815d..64080208 100644 --- a/src/Recyclarr.Cli/Cache/ICachePersister.cs +++ b/src/Recyclarr.Cli/Pipelines/CustomFormat/Cache/ICustomFormatCachePersister.cs @@ -1,8 +1,8 @@ using Recyclarr.Config.Models; -namespace Recyclarr.Cli.Cache; +namespace Recyclarr.Cli.Pipelines.CustomFormat.Cache; -public interface ICachePersister +public interface ICustomFormatCachePersister { CustomFormatCache Load(IServiceConfiguration config); void Save(IServiceConfiguration config, CustomFormatCache cache); diff --git a/src/Recyclarr.Cli/Pipelines/CustomFormat/CustomFormatAutofacModule.cs b/src/Recyclarr.Cli/Pipelines/CustomFormat/CustomFormatAutofacModule.cs index c34675d2..1535c28e 100644 --- a/src/Recyclarr.Cli/Pipelines/CustomFormat/CustomFormatAutofacModule.cs +++ b/src/Recyclarr.Cli/Pipelines/CustomFormat/CustomFormatAutofacModule.cs @@ -1,6 +1,6 @@ using Autofac; using Autofac.Extras.AggregateService; -using Recyclarr.Cli.Cache; +using Recyclarr.Cli.Pipelines.CustomFormat.Cache; using Recyclarr.Cli.Pipelines.CustomFormat.Models; using Recyclarr.Cli.Pipelines.CustomFormat.PipelinePhases; @@ -12,7 +12,7 @@ public class CustomFormatAutofacModule : Module { builder.RegisterType().As().AsSelf().InstancePerLifetimeScope(); - builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType(); builder.RegisterAggregateService(); diff --git a/src/Recyclarr.Cli/Pipelines/CustomFormat/CustomFormatSyncPipeline.cs b/src/Recyclarr.Cli/Pipelines/CustomFormat/CustomFormatSyncPipeline.cs index 8050823a..cf35b737 100644 --- a/src/Recyclarr.Cli/Pipelines/CustomFormat/CustomFormatSyncPipeline.cs +++ b/src/Recyclarr.Cli/Pipelines/CustomFormat/CustomFormatSyncPipeline.cs @@ -1,6 +1,6 @@ using System.Collections.ObjectModel; -using Recyclarr.Cli.Cache; using Recyclarr.Cli.Console.Settings; +using Recyclarr.Cli.Pipelines.CustomFormat.Cache; using Recyclarr.Cli.Pipelines.CustomFormat.Models; using Recyclarr.Cli.Pipelines.CustomFormat.PipelinePhases; using Recyclarr.Common.Extensions; @@ -29,7 +29,7 @@ public record CustomFormatTransactionData public class CustomFormatSyncPipeline( ILogger log, - ICachePersister cachePersister, + ICustomFormatCachePersister cachePersister, ICustomFormatPipelinePhases phases) : ISyncPipeline { @@ -46,7 +46,7 @@ public class CustomFormatSyncPipeline( var serviceData = await phases.ApiFetchPhase.Execute(config); - cache = cache.RemoveStale(serviceData); + cache.RemoveStale(serviceData); var transactions = phases.TransactionPhase.Execute(config, guideCfs, serviceData, cache); @@ -59,9 +59,7 @@ public class CustomFormatSyncPipeline( await phases.ApiPersistencePhase.Execute(config, transactions); - cachePersister.Save(config, cache.Update(transactions) with - { - InstanceName = config.InstanceName - }); + cache.Update(transactions); + cachePersister.Save(config, cache); } } diff --git a/src/Recyclarr.Cli/Pipelines/CustomFormat/PipelinePhases/CustomFormatTransactionPhase.cs b/src/Recyclarr.Cli/Pipelines/CustomFormat/PipelinePhases/CustomFormatTransactionPhase.cs index 774933a6..a785de91 100644 --- a/src/Recyclarr.Cli/Pipelines/CustomFormat/PipelinePhases/CustomFormatTransactionPhase.cs +++ b/src/Recyclarr.Cli/Pipelines/CustomFormat/PipelinePhases/CustomFormatTransactionPhase.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Recyclarr.Cli.Cache; +using Recyclarr.Cli.Pipelines.CustomFormat.Cache; using Recyclarr.Cli.Pipelines.CustomFormat.Models; using Recyclarr.Common.Extensions; using Recyclarr.Config.Models; @@ -47,7 +47,7 @@ public class CustomFormatTransactionPhase(ILogger log) if (config.DeleteOldCustomFormats) { - transactions.DeletedCustomFormats.AddRange(cache.TrashIdMappings + transactions.DeletedCustomFormats.AddRange(cache.Mappings // Custom format must be in the cache but NOT in the user's config .Where(map => guideCfs.All(cf => cf.TrashId != map.TrashId)) // Also, that cache-only CF must exist in the service (otherwise there is nothing to delete) diff --git a/tests/Recyclarr.Cli.IntegrationTests/CustomFormatTransactionPhaseTest.cs b/tests/Recyclarr.Cli.IntegrationTests/CustomFormatTransactionPhaseTest.cs index 36812d22..0c2151f1 100644 --- a/tests/Recyclarr.Cli.IntegrationTests/CustomFormatTransactionPhaseTest.cs +++ b/tests/Recyclarr.Cli.IntegrationTests/CustomFormatTransactionPhaseTest.cs @@ -1,5 +1,5 @@ -using Recyclarr.Cli.Cache; using Recyclarr.Cli.Pipelines.CustomFormat; +using Recyclarr.Cli.Pipelines.CustomFormat.Cache; using Recyclarr.Cli.Pipelines.CustomFormat.Models; using Recyclarr.Cli.Pipelines.CustomFormat.PipelinePhases; using Recyclarr.Tests.TestLibrary; @@ -22,7 +22,7 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture var serviceData = Array.Empty(); - var cache = new CustomFormatCache(); + var cache = new CustomFormatCache([]); var config = NewConfig.Radarr(); @@ -58,7 +58,7 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture new CustomFormatData {Name = "one"} }; - var cache = new CustomFormatCache(); + var cache = new CustomFormatCache([]); var config = NewConfig.Radarr(); @@ -97,13 +97,9 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture new CustomFormatData {Name = "different2", Id = 2} }; - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("cf1", "", 2) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("cf1", "", 2) + ]); var config = NewConfig.Radarr(); @@ -142,7 +138,7 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture new CustomFormatData {Name = "different1", Id = 2} }; - var cache = new CustomFormatCache(); + var cache = new CustomFormatCache([]); var config = NewConfig.Radarr() with { @@ -179,7 +175,7 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture new CustomFormatData {Name = "two", Id = 1} }; - var cache = new CustomFormatCache(); + var cache = new CustomFormatCache([]); var config = NewConfig.Radarr() with { @@ -213,13 +209,9 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture new CustomFormatData {Name = "two", Id = 1} }; - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("cf1", "one", 1) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("cf1", "one", 1) + ]); var config = NewConfig.Radarr() with { @@ -259,13 +251,9 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture new CustomFormatData {Name = "one", Id = 1} }; - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("cf1", "one", 1) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("cf1", "one", 1) + ]); var config = NewConfig.Radarr() with { @@ -298,7 +286,7 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture new CustomFormatData {Name = "one", Id = 1} }; - var cache = new CustomFormatCache(); + var cache = new CustomFormatCache([]); var config = NewConfig.Radarr() with { @@ -328,13 +316,9 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture new CustomFormatData {Name = "one", Id = 1} }; - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("cf1", "one", 1) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("cf1", "one", 1) + ]); var config = NewConfig.Radarr() with { @@ -361,13 +345,9 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture new CustomFormatData {Name = "two", Id = 2} }; - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("cf2", "two", 2) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("cf2", "two", 2) + ]); var config = NewConfig.Radarr() with { @@ -397,13 +377,9 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture new CustomFormatData {Name = "two", Id = 2} }; - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("cf2", "two", 2) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("cf2", "two", 2) + ]); var config = NewConfig.Radarr() with { @@ -430,13 +406,9 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture new CustomFormatData {Name = "two", Id = 2} }; - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("cf2", "two", 2) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("cf2", "two", 2) + ]); var config = NewConfig.Radarr(); @@ -457,13 +429,9 @@ internal class CustomFormatTransactionPhaseTest : CliIntegrationFixture var serviceData = Array.Empty(); - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("cf2", "two", 200) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("cf2", "two", 200) + ]); var config = NewConfig.Radarr(); diff --git a/tests/Recyclarr.Cli.Tests/Cache/CachePersisterTest.cs b/tests/Recyclarr.Cli.Tests/Cache/CachePersisterTest.cs deleted file mode 100644 index 9b895a1a..00000000 --- a/tests/Recyclarr.Cli.Tests/Cache/CachePersisterTest.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Collections.ObjectModel; -using Recyclarr.Cli.Cache; -using Recyclarr.Config.Models; - -namespace Recyclarr.Cli.Tests.Cache; - -[TestFixture] -public class CachePersisterTest -{ - private sealed class Context - { - public Context() - { - var log = Substitute.For(); - ServiceCache = Substitute.For(); - Persister = new CachePersister(log, ServiceCache); - } - - public CachePersister Persister { get; } - public IServiceCache ServiceCache { get; } - } - - [TestCase(CustomFormatCache.LatestVersion - 1)] - [TestCase(CustomFormatCache.LatestVersion + 1)] - public void Throw_when_versions_mismatch(int versionToTest) - { - var ctx = new Context(); - var config = Substitute.For(); - - var testCfObj = new CustomFormatCache - { - Version = versionToTest, - TrashIdMappings = new Collection {new("", "", 5)} - }; - - ctx.ServiceCache.Load(config).Returns(testCfObj); - - var act = () => ctx.Persister.Load(config); - - act.Should().Throw(); - } - - [Test] - public void Accept_loaded_cache_when_versions_match() - { - var ctx = new Context(); - var config = Substitute.For(); - - var testCfObj = new CustomFormatCache - { - Version = CustomFormatCache.LatestVersion, - TrashIdMappings = new Collection {new("", "", 5)} - }; - ctx.ServiceCache.Load(config).Returns(testCfObj); - var result = ctx.Persister.Load(config); - result.Should().NotBeNull(); - } - - [Test] - public void Cache_is_valid_after_successful_load() - { - var ctx = new Context(); - var testCfObj = new CustomFormatCache(); - var config = Substitute.For(); - - ctx.ServiceCache.Load(config).Returns(testCfObj); - var result = ctx.Persister.Load(config); - result.Should().BeSameAs(testCfObj); - } - - [Test] - public void Save_works_with_valid_cf_cache() - { - var ctx = new Context(); - var testCfObj = new CustomFormatCache(); - var config = Substitute.For(); - - ctx.ServiceCache.Load(config).Returns(testCfObj); - - var result = ctx.Persister.Load(config); - ctx.Persister.Save(config, result); - - ctx.ServiceCache.Received().Save(testCfObj, config); - } -} diff --git a/tests/Recyclarr.Cli.Tests/Console/Helpers/CacheStoragePathTest.cs b/tests/Recyclarr.Cli.Tests/Cache/CacheStoragePathTest.cs similarity index 85% rename from tests/Recyclarr.Cli.Tests/Console/Helpers/CacheStoragePathTest.cs rename to tests/Recyclarr.Cli.Tests/Cache/CacheStoragePathTest.cs index 303e2944..97438709 100644 --- a/tests/Recyclarr.Cli.Tests/Console/Helpers/CacheStoragePathTest.cs +++ b/tests/Recyclarr.Cli.Tests/Cache/CacheStoragePathTest.cs @@ -1,7 +1,7 @@ -using Recyclarr.Cli.Console.Helpers; +using Recyclarr.Cli.Cache; using Recyclarr.Config.Models; -namespace Recyclarr.Cli.Tests.Console.Helpers; +namespace Recyclarr.Cli.Tests.Cache; [TestFixture] public class CacheStoragePathTest diff --git a/tests/Recyclarr.Cli.Tests/Cache/CustomFormatCacheTest.cs b/tests/Recyclarr.Cli.Tests/Cache/CustomFormatCacheTest.cs index f2c3aaa0..272d577c 100644 --- a/tests/Recyclarr.Cli.Tests/Cache/CustomFormatCacheTest.cs +++ b/tests/Recyclarr.Cli.Tests/Cache/CustomFormatCacheTest.cs @@ -1,5 +1,5 @@ -using Recyclarr.Cli.Cache; using Recyclarr.Cli.Pipelines.CustomFormat; +using Recyclarr.Cli.Pipelines.CustomFormat.Cache; using Recyclarr.Tests.TestLibrary; namespace Recyclarr.Cli.Tests.Cache; @@ -27,10 +27,10 @@ public class CustomFormatCacheTest } }; - var cache = new CustomFormatCache(); - var result = cache.Update(transactions); + var cache = new CustomFormatCache([]); + cache.Update(transactions); - result.TrashIdMappings.Should().BeEquivalentTo(new[] + cache.Mappings.Should().BeEquivalentTo(new[] { new TrashIdMapping("1", "one", 1), new TrashIdMapping("2", "two", 2), @@ -55,18 +55,14 @@ public class CustomFormatCacheTest } }; - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("3", "three", 3), - new TrashIdMapping("4", "four", 4) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("3", "three", 3), + new TrashIdMapping("4", "four", 4) + ]); - var result = cache.Update(transactions); + cache.Update(transactions); - result.TrashIdMappings.Should().BeEquivalentTo(new[] + cache.Mappings.Should().BeEquivalentTo(new[] { new TrashIdMapping("1", "one", 1), new TrashIdMapping("2", "two", 2), @@ -83,20 +79,16 @@ public class CustomFormatCacheTest NewCf.Data("two", "2", 2) }; - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("1", "one", 1), - new TrashIdMapping("2", "two", 2), - new TrashIdMapping("3", "three", 3), - new TrashIdMapping("4", "four", 4) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("1", "one", 1), + new TrashIdMapping("2", "two", 2), + new TrashIdMapping("3", "three", 3), + new TrashIdMapping("4", "four", 4) + ]); - var result = cache.RemoveStale(serviceCfs); + cache.RemoveStale(serviceCfs); - result.TrashIdMappings.Should().BeEquivalentTo(new[] + cache.Mappings.Should().BeEquivalentTo(new[] { new TrashIdMapping("1", "one", 1), new TrashIdMapping("2", "two", 2) @@ -119,11 +111,11 @@ public class CustomFormatCacheTest } }; - var cache = new CustomFormatCache(); + var cache = new CustomFormatCache([]); - var result = cache.Update(transactions); + cache.Update(transactions); - result.TrashIdMappings.Should().BeEquivalentTo(new[] + cache.Mappings.Should().BeEquivalentTo(new[] { new TrashIdMapping("1", "one", 1), new TrashIdMapping("2", "two", 2) @@ -150,20 +142,16 @@ public class CustomFormatCacheTest } }; - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("1", "one", 1), - new TrashIdMapping("2", "two", 2), - new TrashIdMapping("3", "three", 3), - new TrashIdMapping("4", "four", 4) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("1", "one", 1), + new TrashIdMapping("2", "two", 2), + new TrashIdMapping("3", "three", 3), + new TrashIdMapping("4", "four", 4) + ]); - var result = cache.Update(transactions); + cache.Update(transactions); - result.TrashIdMappings.Should().BeEquivalentTo(new[] + cache.Mappings.Should().BeEquivalentTo(new[] { new TrashIdMapping("1", "one_new", 1), new TrashIdMapping("2", "two_new", 2), @@ -177,21 +165,17 @@ public class CustomFormatCacheTest { var transactions = new CustomFormatTransactionData(); - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("1", "one", 1), - new TrashIdMapping("12", "one2", 1), - new TrashIdMapping("2", "two", 2), - new TrashIdMapping("3", "three", 3), - new TrashIdMapping("4", "four", 4) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("1", "one", 1), + new TrashIdMapping("12", "one2", 1), + new TrashIdMapping("2", "two", 2), + new TrashIdMapping("3", "three", 3), + new TrashIdMapping("4", "four", 4) + ]); - var result = cache.Update(transactions); + cache.Update(transactions); - result.TrashIdMappings.Should().BeEquivalentTo(new[] + cache.Mappings.Should().BeEquivalentTo(new[] { new TrashIdMapping("1", "one", 1), new TrashIdMapping("2", "two", 2), @@ -205,20 +189,16 @@ public class CustomFormatCacheTest { var transactions = new CustomFormatTransactionData(); - var cache = new CustomFormatCache - { - TrashIdMappings = new[] - { - new TrashIdMapping("1", "one", 1), - new TrashIdMapping("3", "three", 3), - new TrashIdMapping("4", "four", 4), - new TrashIdMapping("2", "two", 2) - } - }; + var cache = new CustomFormatCache([ + new TrashIdMapping("1", "one", 1), + new TrashIdMapping("3", "three", 3), + new TrashIdMapping("4", "four", 4), + new TrashIdMapping("2", "two", 2) + ]); - var result = cache.Update(transactions); + cache.Update(transactions); - result.TrashIdMappings.Should().BeEquivalentTo(new[] + cache.Mappings.Should().BeEquivalentTo(new[] { new TrashIdMapping("1", "one", 1), new TrashIdMapping("2", "two", 2), diff --git a/tests/Recyclarr.Cli.Tests/Cache/ServiceCacheTest.cs b/tests/Recyclarr.Cli.Tests/Cache/ServiceCacheTest.cs index e4bbccfe..6b67cced 100644 --- a/tests/Recyclarr.Cli.Tests/Cache/ServiceCacheTest.cs +++ b/tests/Recyclarr.Cli.Tests/Cache/ServiceCacheTest.cs @@ -1,7 +1,7 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using Recyclarr.Cli.Cache; -using Recyclarr.Cli.Console.Helpers; +using Recyclarr.Cli.Pipelines.CustomFormat.Cache; using Recyclarr.Config.Models; namespace Recyclarr.Cli.Tests.Cache; @@ -205,7 +205,7 @@ public class ServiceCacheTest fs.AddFile("cacheFile.json", new MockFileData(cacheJson)); storage.CalculatePath(default!, default!).ReturnsForAnyArgs(fs.FileInfo.New("cacheFile.json")); - var result = sut.Load(config); + var result = sut.Load(config); result.Should().BeEquivalentTo(new { diff --git a/tests/Recyclarr.Cli.Tests/Pipelines/CustomFormat/Cache/CustomFormatCachePersisterTest.cs b/tests/Recyclarr.Cli.Tests/Pipelines/CustomFormat/Cache/CustomFormatCachePersisterTest.cs new file mode 100644 index 00000000..1d9c7f8a --- /dev/null +++ b/tests/Recyclarr.Cli.Tests/Pipelines/CustomFormat/Cache/CustomFormatCachePersisterTest.cs @@ -0,0 +1,70 @@ +using AutoFixture; +using Recyclarr.Cli.Cache; +using Recyclarr.Cli.Pipelines.CustomFormat.Cache; +using Recyclarr.Config.Models; + +namespace Recyclarr.Cli.Tests.Pipelines.CustomFormat.Cache; + +[TestFixture] +public class CustomFormatCachePersisterTest +{ + [TestCase(CustomFormatCachePersister.LatestVersion - 1)] + [TestCase(CustomFormatCachePersister.LatestVersion + 1)] + public void Throw_when_versions_mismatch(int versionToTest) + { + var fixture = NSubstituteFixture.Create(); + var serviceCache = fixture.Freeze(); + var sut = fixture.Create(); + + var config = Substitute.For(); + + var testCfObj = new CustomFormatCacheData(versionToTest, "", + [ + new TrashIdMapping("", "", 5) + ]); + + serviceCache.Load(config).Returns(testCfObj); + + var act = () => sut.Load(config); + + act.Should().Throw(); + } + + [Test] + public void Accept_loaded_cache_when_versions_match() + { + var fixture = NSubstituteFixture.Create(); + var serviceCache = fixture.Freeze(); + var sut = fixture.Create(); + + var config = Substitute.For(); + + var testCfObj = new CustomFormatCacheData(CustomFormatCachePersister.LatestVersion, "", + [ + new TrashIdMapping("", "", 5) + ]); + + serviceCache.Load(config).Returns(testCfObj); + + var result = sut.Load(config); + + result.Should().NotBeNull(); + } + + [Test] + public void Cache_is_valid_after_successful_load() + { + var fixture = NSubstituteFixture.Create(); + var serviceCache = fixture.Freeze(); + var sut = fixture.Create(); + + TrashIdMapping[] mappings = [new TrashIdMapping("abc", "name", 123)]; + var config = Substitute.For(); + + serviceCache.Load(config).Returns(new CustomFormatCacheData(1, "", mappings)); + + var result = sut.Load(config); + + result.Mappings.Should().BeEquivalentTo(mappings); + } +}