From 532b9544563f3237f8ca1f3fa12135818e914119 Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Fri, 13 Oct 2023 15:02:58 -0500 Subject: [PATCH] refactor: Generic pipelines support for media naming --- src/Recyclarr.Cli/CompositionRoot.cs | 4 +- .../Generic/GenericPipelinePhases.cs | 11 ++ .../Pipelines/Generic/GenericSyncPipeline.cs | 31 ++++ .../Generic/IApiFetchPipelinePhase.cs | 8 + .../Generic/IApiPersistencePipelinePhase.cs | 8 + .../Pipelines/Generic/IConfigPipelinePhase.cs | 8 + .../Pipelines/Generic/ILogPipelinePhase.cs | 7 + .../Generic/IPreviewPipelinePhase.cs | 6 + .../Generic/ITransactionPipelinePhase.cs | 6 + .../MediaNaming/MediaNamingAutofacModule.cs | 16 +- .../MediaNaming/MediaNamingPipelineContext.cs | 11 ++ .../MediaNaming/MediaNamingSyncPipeline.cs | 40 ----- .../Config/SonarrMediaNamingConfigPhase.cs | 12 +- .../MediaNamingApiFetchPhase.cs | 7 +- .../MediaNamingApiPersistencePhase.cs | 6 +- .../PipelinePhases/MediaNamingConfigPhase.cs | 6 +- ...gPhaseLogger.cs => MediaNamingLogPhase.cs} | 15 +- .../PipelinePhases/MediaNamingPreviewPhase.cs | 7 +- .../MediaNamingTransactionPhase.cs | 13 +- .../MediaNamingConfigPhaseIntegrationTest.cs | 3 +- .../MediaNamingTransactionPhaseRadarrTest.cs | 116 ++++++++------ .../MediaNamingTransactionPhaseSonarrTest.cs | 151 ++++++++++-------- 22 files changed, 288 insertions(+), 204 deletions(-) create mode 100644 src/Recyclarr.Cli/Pipelines/Generic/GenericPipelinePhases.cs create mode 100644 src/Recyclarr.Cli/Pipelines/Generic/GenericSyncPipeline.cs create mode 100644 src/Recyclarr.Cli/Pipelines/Generic/IApiFetchPipelinePhase.cs create mode 100644 src/Recyclarr.Cli/Pipelines/Generic/IApiPersistencePipelinePhase.cs create mode 100644 src/Recyclarr.Cli/Pipelines/Generic/IConfigPipelinePhase.cs create mode 100644 src/Recyclarr.Cli/Pipelines/Generic/ILogPipelinePhase.cs create mode 100644 src/Recyclarr.Cli/Pipelines/Generic/IPreviewPipelinePhase.cs create mode 100644 src/Recyclarr.Cli/Pipelines/Generic/ITransactionPipelinePhase.cs create mode 100644 src/Recyclarr.Cli/Pipelines/MediaNaming/MediaNamingPipelineContext.cs delete mode 100644 src/Recyclarr.Cli/Pipelines/MediaNaming/MediaNamingSyncPipeline.cs rename src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/{MediaNamingPhaseLogger.cs => MediaNamingLogPhase.cs} (71%) diff --git a/src/Recyclarr.Cli/CompositionRoot.cs b/src/Recyclarr.Cli/CompositionRoot.cs index d6824443..79adb367 100644 --- a/src/Recyclarr.Cli/CompositionRoot.cs +++ b/src/Recyclarr.Cli/CompositionRoot.cs @@ -9,6 +9,7 @@ using Recyclarr.Cli.Logging; using Recyclarr.Cli.Migration; using Recyclarr.Cli.Pipelines; using Recyclarr.Cli.Pipelines.CustomFormat; +using Recyclarr.Cli.Pipelines.Generic; using Recyclarr.Cli.Pipelines.MediaNaming; using Recyclarr.Cli.Pipelines.QualityProfile; using Recyclarr.Cli.Pipelines.QualitySize; @@ -75,13 +76,14 @@ public static class CompositionRoot builder.RegisterModule(); builder.RegisterModule(); + builder.RegisterGeneric(typeof(GenericPipelinePhases<>)); builder.RegisterTypes( typeof(TagSyncPipeline), typeof(CustomFormatSyncPipeline), typeof(QualityProfileSyncPipeline), typeof(QualitySizeSyncPipeline), typeof(ReleaseProfileSyncPipeline), - typeof(MediaNamingSyncPipeline)) + typeof(GenericSyncPipeline)) .As() .OrderByRegistration(); } diff --git a/src/Recyclarr.Cli/Pipelines/Generic/GenericPipelinePhases.cs b/src/Recyclarr.Cli/Pipelines/Generic/GenericPipelinePhases.cs new file mode 100644 index 00000000..4cbaa5f0 --- /dev/null +++ b/src/Recyclarr.Cli/Pipelines/Generic/GenericPipelinePhases.cs @@ -0,0 +1,11 @@ +namespace Recyclarr.Cli.Pipelines.Generic; + +public class GenericPipelinePhases +{ + public required IConfigPipelinePhase ConfigPhase { get; init; } + public required ILogPipelinePhase LogPhase { get; init; } + public required IApiFetchPipelinePhase ApiFetchPhase { get; init; } + public required ITransactionPipelinePhase TransactionPhase { get; init; } + public required IPreviewPipelinePhase PreviewPhase { get; init; } + public required IApiPersistencePipelinePhase ApiPersistencePhase { get; init; } +} diff --git a/src/Recyclarr.Cli/Pipelines/Generic/GenericSyncPipeline.cs b/src/Recyclarr.Cli/Pipelines/Generic/GenericSyncPipeline.cs new file mode 100644 index 00000000..148ef49b --- /dev/null +++ b/src/Recyclarr.Cli/Pipelines/Generic/GenericSyncPipeline.cs @@ -0,0 +1,31 @@ +using Recyclarr.Cli.Console.Settings; +using Recyclarr.Config.Models; + +namespace Recyclarr.Cli.Pipelines.Generic; + +public class GenericSyncPipeline(GenericPipelinePhases phases) : ISyncPipeline + where TContext : new() +{ + public async Task Execute(ISyncSettings settings, IServiceConfiguration config) + { + var context = new TContext(); + + await phases.ConfigPhase.Execute(context, config); + if (phases.LogPhase.LogConfigPhaseAndExitIfNeeded(context)) + { + return; + } + + await phases.ApiFetchPhase.Execute(context, config); + phases.TransactionPhase.Execute(context); + + if (settings.Preview) + { + phases.PreviewPhase.Execute(context); + return; + } + + await phases.ApiPersistencePhase.Execute(context, config); + phases.LogPhase.LogPersistenceResults(context); + } +} diff --git a/src/Recyclarr.Cli/Pipelines/Generic/IApiFetchPipelinePhase.cs b/src/Recyclarr.Cli/Pipelines/Generic/IApiFetchPipelinePhase.cs new file mode 100644 index 00000000..f26492ba --- /dev/null +++ b/src/Recyclarr.Cli/Pipelines/Generic/IApiFetchPipelinePhase.cs @@ -0,0 +1,8 @@ +using Recyclarr.Config.Models; + +namespace Recyclarr.Cli.Pipelines.Generic; + +public interface IApiFetchPipelinePhase +{ + Task Execute(TContext context, IServiceConfiguration config); +} \ No newline at end of file diff --git a/src/Recyclarr.Cli/Pipelines/Generic/IApiPersistencePipelinePhase.cs b/src/Recyclarr.Cli/Pipelines/Generic/IApiPersistencePipelinePhase.cs new file mode 100644 index 00000000..f1daec73 --- /dev/null +++ b/src/Recyclarr.Cli/Pipelines/Generic/IApiPersistencePipelinePhase.cs @@ -0,0 +1,8 @@ +using Recyclarr.Config.Models; + +namespace Recyclarr.Cli.Pipelines.Generic; + +public interface IApiPersistencePipelinePhase +{ + Task Execute(TContext context, IServiceConfiguration config); +} \ No newline at end of file diff --git a/src/Recyclarr.Cli/Pipelines/Generic/IConfigPipelinePhase.cs b/src/Recyclarr.Cli/Pipelines/Generic/IConfigPipelinePhase.cs new file mode 100644 index 00000000..5a53b1a4 --- /dev/null +++ b/src/Recyclarr.Cli/Pipelines/Generic/IConfigPipelinePhase.cs @@ -0,0 +1,8 @@ +using Recyclarr.Config.Models; + +namespace Recyclarr.Cli.Pipelines.Generic; + +public interface IConfigPipelinePhase +{ + Task Execute(TContext context, IServiceConfiguration config); +} \ No newline at end of file diff --git a/src/Recyclarr.Cli/Pipelines/Generic/ILogPipelinePhase.cs b/src/Recyclarr.Cli/Pipelines/Generic/ILogPipelinePhase.cs new file mode 100644 index 00000000..c753ce3b --- /dev/null +++ b/src/Recyclarr.Cli/Pipelines/Generic/ILogPipelinePhase.cs @@ -0,0 +1,7 @@ +namespace Recyclarr.Cli.Pipelines.Generic; + +public interface ILogPipelinePhase +{ + bool LogConfigPhaseAndExitIfNeeded(TContext context); + void LogPersistenceResults(TContext context); +} \ No newline at end of file diff --git a/src/Recyclarr.Cli/Pipelines/Generic/IPreviewPipelinePhase.cs b/src/Recyclarr.Cli/Pipelines/Generic/IPreviewPipelinePhase.cs new file mode 100644 index 00000000..58eb005f --- /dev/null +++ b/src/Recyclarr.Cli/Pipelines/Generic/IPreviewPipelinePhase.cs @@ -0,0 +1,6 @@ +namespace Recyclarr.Cli.Pipelines.Generic; + +public interface IPreviewPipelinePhase +{ + void Execute(TContext context); +} \ No newline at end of file diff --git a/src/Recyclarr.Cli/Pipelines/Generic/ITransactionPipelinePhase.cs b/src/Recyclarr.Cli/Pipelines/Generic/ITransactionPipelinePhase.cs new file mode 100644 index 00000000..6257a7c9 --- /dev/null +++ b/src/Recyclarr.Cli/Pipelines/Generic/ITransactionPipelinePhase.cs @@ -0,0 +1,6 @@ +namespace Recyclarr.Cli.Pipelines.Generic; + +public interface ITransactionPipelinePhase +{ + void Execute(TContext context); +} \ No newline at end of file diff --git a/src/Recyclarr.Cli/Pipelines/MediaNaming/MediaNamingAutofacModule.cs b/src/Recyclarr.Cli/Pipelines/MediaNaming/MediaNamingAutofacModule.cs index 2de0ae4c..914e7c82 100644 --- a/src/Recyclarr.Cli/Pipelines/MediaNaming/MediaNamingAutofacModule.cs +++ b/src/Recyclarr.Cli/Pipelines/MediaNaming/MediaNamingAutofacModule.cs @@ -1,5 +1,4 @@ using Autofac; -using Autofac.Extras.AggregateService; using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases; using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases.Config; using Recyclarr.Common; @@ -18,12 +17,13 @@ public class MediaNamingAutofacModule : Module builder.RegisterType() .Keyed(SupportedServices.Sonarr); - builder.RegisterAggregateService(); - builder.RegisterType(); - builder.RegisterType(); - builder.RegisterType(); - builder.RegisterType(); - builder.RegisterType(); - builder.RegisterType(); + builder.RegisterTypes( + typeof(MediaNamingConfigPhase), + typeof(MediaNamingApiFetchPhase), + typeof(MediaNamingTransactionPhase), + typeof(MediaNamingPreviewPhase), + typeof(MediaNamingApiPersistencePhase), + typeof(MediaNamingLogPhase)) + .AsImplementedInterfaces(); } } diff --git a/src/Recyclarr.Cli/Pipelines/MediaNaming/MediaNamingPipelineContext.cs b/src/Recyclarr.Cli/Pipelines/MediaNaming/MediaNamingPipelineContext.cs new file mode 100644 index 00000000..7eda0ea5 --- /dev/null +++ b/src/Recyclarr.Cli/Pipelines/MediaNaming/MediaNamingPipelineContext.cs @@ -0,0 +1,11 @@ +using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases; +using Recyclarr.ServarrApi.MediaNaming; + +namespace Recyclarr.Cli.Pipelines.MediaNaming; + +public class MediaNamingPipelineContext +{ + public ProcessedNamingConfig ConfigOutput { get; set; } = default!; + public MediaNamingDto ApiFetchOutput { get; set; } = default!; + public MediaNamingDto TransactionOutput { get; set; } = default!; +} diff --git a/src/Recyclarr.Cli/Pipelines/MediaNaming/MediaNamingSyncPipeline.cs b/src/Recyclarr.Cli/Pipelines/MediaNaming/MediaNamingSyncPipeline.cs deleted file mode 100644 index 6bfaccf1..00000000 --- a/src/Recyclarr.Cli/Pipelines/MediaNaming/MediaNamingSyncPipeline.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Recyclarr.Cli.Console.Settings; -using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases; -using Recyclarr.Config.Models; - -namespace Recyclarr.Cli.Pipelines.MediaNaming; - -public interface IMediaNamingPipelinePhases -{ - MediaNamingConfigPhase ConfigPhase { get; } - MediaNamingPhaseLogger Logger { get; } - MediaNamingApiFetchPhase ApiFetchPhase { get; } - MediaNamingTransactionPhase TransactionPhase { get; } - MediaNamingPreviewPhase PreviewPhase { get; } - MediaNamingApiPersistencePhase ApiPersistencePhase { get; } -} - -public class MediaNamingSyncPipeline(IMediaNamingPipelinePhases phases) : ISyncPipeline -{ - public async Task Execute(ISyncSettings settings, IServiceConfiguration config) - { - var processedNaming = await phases.ConfigPhase.Execute(config); - if (phases.Logger.LogConfigPhaseAndExitIfNeeded(processedNaming)) - { - return; - } - - var serviceData = await phases.ApiFetchPhase.Execute(config); - - var transactions = phases.TransactionPhase.Execute(serviceData, processedNaming); - - if (settings.Preview) - { - phases.PreviewPhase.Execute(transactions); - return; - } - - await phases.ApiPersistencePhase.Execute(config, transactions); - phases.Logger.LogPersistenceResults(serviceData, transactions); - } -} diff --git a/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/Config/SonarrMediaNamingConfigPhase.cs b/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/Config/SonarrMediaNamingConfigPhase.cs index 14a80b85..189b8419 100644 --- a/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/Config/SonarrMediaNamingConfigPhase.cs +++ b/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/Config/SonarrMediaNamingConfigPhase.cs @@ -5,15 +5,9 @@ using Recyclarr.TrashGuide.MediaNaming; namespace Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases.Config; -public class SonarrMediaNamingConfigPhase : ServiceBasedMediaNamingConfigPhase +public class SonarrMediaNamingConfigPhase(ISonarrCapabilityFetcher sonarrCapabilities) + : ServiceBasedMediaNamingConfigPhase { - private readonly ISonarrCapabilityFetcher _sonarrCapabilities; - - public SonarrMediaNamingConfigPhase(ISonarrCapabilityFetcher sonarrCapabilities) - { - _sonarrCapabilities = sonarrCapabilities; - } - protected override async Task ProcessNaming( SonarrConfiguration config, IMediaNamingGuideService guide, @@ -21,7 +15,7 @@ public class SonarrMediaNamingConfigPhase : ServiceBasedMediaNamingConfigPhase { - public async Task Execute(IServiceConfiguration config) + public async Task Execute(MediaNamingPipelineContext context, IServiceConfiguration config) { - return await api.GetNaming(config); + context.ApiFetchOutput = await api.GetNaming(config); } } diff --git a/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingApiPersistencePhase.cs b/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingApiPersistencePhase.cs index be15c2b0..8156d709 100644 --- a/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingApiPersistencePhase.cs +++ b/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingApiPersistencePhase.cs @@ -1,12 +1,14 @@ +using Recyclarr.Cli.Pipelines.Generic; using Recyclarr.Config.Models; using Recyclarr.ServarrApi.MediaNaming; namespace Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases; public class MediaNamingApiPersistencePhase(IMediaNamingApiService api) + : IApiPersistencePipelinePhase { - public async Task Execute(IServiceConfiguration config, MediaNamingDto serviceDto) + public async Task Execute(MediaNamingPipelineContext context, IServiceConfiguration config) { - await api.UpdateNaming(config, serviceDto); + await api.UpdateNaming(config, context.TransactionOutput); } } diff --git a/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingConfigPhase.cs b/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingConfigPhase.cs index 59712e26..913aab83 100644 --- a/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingConfigPhase.cs +++ b/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingConfigPhase.cs @@ -1,4 +1,5 @@ using Autofac.Features.Indexed; +using Recyclarr.Cli.Pipelines.Generic; using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases.Config; using Recyclarr.Common; using Recyclarr.Config.Models; @@ -18,13 +19,14 @@ public record ProcessedNamingConfig public class MediaNamingConfigPhase( IMediaNamingGuideService guide, IIndex configPhaseStrategyFactory) + : IConfigPipelinePhase { - public async Task Execute(IServiceConfiguration config) + public async Task Execute(MediaNamingPipelineContext context, IServiceConfiguration config) { var lookup = new NamingFormatLookup(); var strategy = configPhaseStrategyFactory[config.ServiceType]; var dto = await strategy.ProcessNaming(config, guide, lookup); - return new ProcessedNamingConfig {Dto = dto, InvalidNaming = lookup.Errors}; + context.ConfigOutput = new ProcessedNamingConfig {Dto = dto, InvalidNaming = lookup.Errors}; } } diff --git a/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingPhaseLogger.cs b/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingLogPhase.cs similarity index 71% rename from src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingPhaseLogger.cs rename to src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingLogPhase.cs index 69967be8..624c8929 100644 --- a/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingPhaseLogger.cs +++ b/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingLogPhase.cs @@ -1,12 +1,15 @@ +using Recyclarr.Cli.Pipelines.Generic; using Recyclarr.ServarrApi.MediaNaming; namespace Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases; -public class MediaNamingPhaseLogger(ILogger log) +public class MediaNamingLogPhase(ILogger log) : ILogPipelinePhase { // Returning 'true' means to exit. 'false' means to proceed. - public bool LogConfigPhaseAndExitIfNeeded(ProcessedNamingConfig config) + public bool LogConfigPhaseAndExitIfNeeded(MediaNamingPipelineContext context) { + var config = context.ConfigOutput; + if (config.InvalidNaming.Count != 0) { foreach (var (topic, invalidValue) in config.InvalidNaming) @@ -33,12 +36,12 @@ public class MediaNamingPhaseLogger(ILogger log) return false; } - public void LogPersistenceResults(MediaNamingDto oldDto, MediaNamingDto newDto) + public void LogPersistenceResults(MediaNamingPipelineContext context) { - var differences = oldDto switch + var differences = context.ApiFetchOutput switch { - RadarrMediaNamingDto x => x.GetDifferences(newDto), - SonarrMediaNamingDto x => x.GetDifferences(newDto), + RadarrMediaNamingDto x => x.GetDifferences(context.TransactionOutput), + SonarrMediaNamingDto x => x.GetDifferences(context.TransactionOutput), _ => throw new ArgumentException("Unsupported configuration type in LogPersistenceResults method") }; diff --git a/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingPreviewPhase.cs b/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingPreviewPhase.cs index 8379c57e..ac2253e8 100644 --- a/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingPreviewPhase.cs +++ b/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingPreviewPhase.cs @@ -1,19 +1,20 @@ +using Recyclarr.Cli.Pipelines.Generic; using Recyclarr.ServarrApi.MediaNaming; using Spectre.Console; namespace Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases; -public class MediaNamingPreviewPhase(IAnsiConsole console) +public class MediaNamingPreviewPhase(IAnsiConsole console) : IPreviewPipelinePhase { private Table? _table; - public void Execute(MediaNamingDto serviceDto) + public void Execute(MediaNamingPipelineContext context) { _table = new Table() .Title("Media Naming [red](Preview)[/]") .AddColumns("[b]Field[/]", "[b]Value[/]"); - switch (serviceDto) + switch (context.TransactionOutput) { case RadarrMediaNamingDto dto: PreviewRadarr(dto); diff --git a/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingTransactionPhase.cs b/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingTransactionPhase.cs index 5f2ac3ed..4dcc6d0c 100644 --- a/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingTransactionPhase.cs +++ b/src/Recyclarr.Cli/Pipelines/MediaNaming/PipelinePhases/MediaNamingTransactionPhase.cs @@ -1,17 +1,16 @@ -using System.Diagnostics.CodeAnalysis; +using Recyclarr.Cli.Pipelines.Generic; using Recyclarr.ServarrApi.MediaNaming; namespace Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases; -public class MediaNamingTransactionPhase +public class MediaNamingTransactionPhase : ITransactionPipelinePhase { - [SuppressMessage("Performance", "CA1822:Mark members as static")] - public MediaNamingDto Execute(MediaNamingDto serviceData, ProcessedNamingConfig config) + public void Execute(MediaNamingPipelineContext context) { - return serviceData switch + context.TransactionOutput = context.ApiFetchOutput switch { - RadarrMediaNamingDto dto => UpdateRadarrDto(dto, config), - SonarrMediaNamingDto dto => UpdateSonarrDto(dto, config), + RadarrMediaNamingDto dto => UpdateRadarrDto(dto, context.ConfigOutput), + SonarrMediaNamingDto dto => UpdateSonarrDto(dto, context.ConfigOutput), _ => throw new ArgumentException("Config type not supported in media naming transation phase") }; } diff --git a/src/tests/Recyclarr.Cli.IntegrationTests/Pipelines/MediaNamingConfigPhaseIntegrationTest.cs b/src/tests/Recyclarr.Cli.IntegrationTests/Pipelines/MediaNamingConfigPhaseIntegrationTest.cs index 794fe100..d3391511 100644 --- a/src/tests/Recyclarr.Cli.IntegrationTests/Pipelines/MediaNamingConfigPhaseIntegrationTest.cs +++ b/src/tests/Recyclarr.Cli.IntegrationTests/Pipelines/MediaNamingConfigPhaseIntegrationTest.cs @@ -1,4 +1,5 @@ using Autofac.Core.Registration; +using Recyclarr.Cli.Pipelines.MediaNaming; using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases; using Recyclarr.Common; using Recyclarr.Config.Models; @@ -18,7 +19,7 @@ internal class MediaNamingConfigPhaseIntegrationTest : CliIntegrationFixture public async Task Throw_on_unknown_config_type() { var sut = Resolve(); - var act = () => sut.Execute(new UnsupportedConfigType {InstanceName = ""}); + var act = () => sut.Execute(new MediaNamingPipelineContext(), new UnsupportedConfigType {InstanceName = ""}); await act.Should().ThrowAsync(); } } diff --git a/tests/Recyclarr.Cli.Tests/Pipelines/MediaNaming/MediaNamingTransactionPhaseRadarrTest.cs b/tests/Recyclarr.Cli.Tests/Pipelines/MediaNaming/MediaNamingTransactionPhaseRadarrTest.cs index 4fcc6a0c..c11ab601 100644 --- a/tests/Recyclarr.Cli.Tests/Pipelines/MediaNaming/MediaNamingTransactionPhaseRadarrTest.cs +++ b/tests/Recyclarr.Cli.Tests/Pipelines/MediaNaming/MediaNamingTransactionPhaseRadarrTest.cs @@ -1,103 +1,115 @@ +using System.Diagnostics.CodeAnalysis; +using Recyclarr.Cli.Pipelines.MediaNaming; using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases; using Recyclarr.ServarrApi.MediaNaming; namespace Recyclarr.Cli.Tests.Pipelines.MediaNaming; [TestFixture] +[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = + "Do not care about disposal in a testing context")] public class MediaNamingTransactionPhaseRadarrTest { - [Test] [AutoMockData] + [Test, AutoMockData] public void Radarr_left_null( MediaNamingTransactionPhase sut) { - var left = new RadarrMediaNamingDto(); - - var right = new ProcessedNamingConfig + var context = new MediaNamingPipelineContext { - Dto = new RadarrMediaNamingDto + ApiFetchOutput = new RadarrMediaNamingDto(), + ConfigOutput = new ProcessedNamingConfig { - RenameMovies = true, - StandardMovieFormat = "file_format", - MovieFolderFormat = "folder_format" + Dto = new RadarrMediaNamingDto + { + RenameMovies = true, + StandardMovieFormat = "file_format", + MovieFolderFormat = "folder_format" + } } }; - var result = sut.Execute(left, right); + sut.Execute(context); - result.Should().BeEquivalentTo(right.Dto, o => o.RespectingRuntimeTypes()); + context.TransactionOutput.Should().BeEquivalentTo(context.ConfigOutput.Dto, o => o.RespectingRuntimeTypes()); } - [Test] [AutoMockData] + [Test, AutoMockData] public void Radarr_right_null( MediaNamingTransactionPhase sut) { - var left = new RadarrMediaNamingDto + var context = new MediaNamingPipelineContext { - RenameMovies = true, - StandardMovieFormat = "file_format", - MovieFolderFormat = "folder_format" - }; - - var right = new ProcessedNamingConfig - { - Dto = new RadarrMediaNamingDto() + ApiFetchOutput = new RadarrMediaNamingDto + { + RenameMovies = true, + StandardMovieFormat = "file_format", + MovieFolderFormat = "folder_format" + }, + ConfigOutput = new ProcessedNamingConfig + { + Dto = new RadarrMediaNamingDto() + } }; - var result = sut.Execute(left, right); + sut.Execute(context); - result.Should().BeEquivalentTo(left, o => o.RespectingRuntimeTypes()); + context.TransactionOutput.Should().BeEquivalentTo(context.ApiFetchOutput, o => o.RespectingRuntimeTypes()); } - [Test] [AutoMockData] + [Test, AutoMockData] public void Radarr_right_and_left_with_rename( MediaNamingTransactionPhase sut) { - var left = new RadarrMediaNamingDto + var context = new MediaNamingPipelineContext { - RenameMovies = false, - StandardMovieFormat = "file_format", - MovieFolderFormat = "folder_format" - }; - - var right = new ProcessedNamingConfig - { - Dto = new RadarrMediaNamingDto + ApiFetchOutput = new RadarrMediaNamingDto { - RenameMovies = true, - StandardMovieFormat = "file_format2", - MovieFolderFormat = "folder_format2" + RenameMovies = false, + StandardMovieFormat = "file_format", + MovieFolderFormat = "folder_format" + }, + ConfigOutput = new ProcessedNamingConfig + { + Dto = new RadarrMediaNamingDto + { + RenameMovies = true, + StandardMovieFormat = "file_format2", + MovieFolderFormat = "folder_format2" + } } }; - var result = sut.Execute(left, right); + sut.Execute(context); - result.Should().BeEquivalentTo(right.Dto, o => o.RespectingRuntimeTypes()); + context.TransactionOutput.Should().BeEquivalentTo(context.ConfigOutput.Dto, o => o.RespectingRuntimeTypes()); } - [Test] [AutoMockData] + [Test, AutoMockData] public void Radarr_right_and_left_without_rename( MediaNamingTransactionPhase sut) { - var left = new RadarrMediaNamingDto - { - RenameMovies = true, - StandardMovieFormat = "file_format", - MovieFolderFormat = "folder_format" - }; - - var right = new ProcessedNamingConfig + var context = new MediaNamingPipelineContext { - Dto = new RadarrMediaNamingDto + ApiFetchOutput = new RadarrMediaNamingDto { - RenameMovies = false, - StandardMovieFormat = "file_format2", - MovieFolderFormat = "folder_format2" + RenameMovies = true, + StandardMovieFormat = "file_format", + MovieFolderFormat = "folder_format" + }, + ConfigOutput = new ProcessedNamingConfig + { + Dto = new RadarrMediaNamingDto + { + RenameMovies = false, + StandardMovieFormat = "file_format2", + MovieFolderFormat = "folder_format2" + } } }; - var result = sut.Execute(left, right); + sut.Execute(context); - result.Should().BeEquivalentTo(new RadarrMediaNamingDto + context.TransactionOutput.Should().BeEquivalentTo(new RadarrMediaNamingDto { RenameMovies = false, StandardMovieFormat = "file_format2", diff --git a/tests/Recyclarr.Cli.Tests/Pipelines/MediaNaming/MediaNamingTransactionPhaseSonarrTest.cs b/tests/Recyclarr.Cli.Tests/Pipelines/MediaNaming/MediaNamingTransactionPhaseSonarrTest.cs index 6cacb553..e4cc5937 100644 --- a/tests/Recyclarr.Cli.Tests/Pipelines/MediaNaming/MediaNamingTransactionPhaseSonarrTest.cs +++ b/tests/Recyclarr.Cli.Tests/Pipelines/MediaNaming/MediaNamingTransactionPhaseSonarrTest.cs @@ -1,121 +1,132 @@ +using System.Diagnostics.CodeAnalysis; +using Recyclarr.Cli.Pipelines.MediaNaming; using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases; using Recyclarr.ServarrApi.MediaNaming; namespace Recyclarr.Cli.Tests.Pipelines.MediaNaming; [TestFixture] +[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")] public class MediaNamingTransactionPhaseSonarrTest { - [Test] [AutoMockData] + [Test, AutoMockData] public void Sonarr_left_null( MediaNamingTransactionPhase sut) { - var left = new SonarrMediaNamingDto(); - - var right = new ProcessedNamingConfig + var context = new MediaNamingPipelineContext { - Dto = new SonarrMediaNamingDto + ApiFetchOutput = new SonarrMediaNamingDto(), + ConfigOutput = new ProcessedNamingConfig { - RenameEpisodes = true, - SeasonFolderFormat = "season_default", - SeriesFolderFormat = "series_plex", - StandardEpisodeFormat = "episodes_standard_default_3", - DailyEpisodeFormat = "episodes_daily_default_3", - AnimeEpisodeFormat = "episodes_anime_default_3" + Dto = new SonarrMediaNamingDto + { + RenameEpisodes = true, + SeasonFolderFormat = "season_default", + SeriesFolderFormat = "series_plex", + StandardEpisodeFormat = "episodes_standard_default_3", + DailyEpisodeFormat = "episodes_daily_default_3", + AnimeEpisodeFormat = "episodes_anime_default_3" + } } }; - var result = sut.Execute(left, right); + sut.Execute(context); - result.Should().BeEquivalentTo(right.Dto, o => o.RespectingRuntimeTypes()); + context.TransactionOutput.Should().BeEquivalentTo(context.ConfigOutput.Dto, o => o.RespectingRuntimeTypes()); } - [Test] [AutoMockData] + [Test, AutoMockData] public void Sonarr_right_null( MediaNamingTransactionPhase sut) { - var left = new SonarrMediaNamingDto - { - RenameEpisodes = true, - SeasonFolderFormat = "season_default", - SeriesFolderFormat = "series_plex", - StandardEpisodeFormat = "episodes_standard_default_3", - DailyEpisodeFormat = "episodes_daily_default_3", - AnimeEpisodeFormat = "episodes_anime_default_3" - }; - - var right = new ProcessedNamingConfig + var context = new MediaNamingPipelineContext { - Dto = new SonarrMediaNamingDto() + ApiFetchOutput = new SonarrMediaNamingDto + { + RenameEpisodes = true, + SeasonFolderFormat = "season_default", + SeriesFolderFormat = "series_plex", + StandardEpisodeFormat = "episodes_standard_default_3", + DailyEpisodeFormat = "episodes_daily_default_3", + AnimeEpisodeFormat = "episodes_anime_default_3" + }, + ConfigOutput = new ProcessedNamingConfig + { + Dto = new SonarrMediaNamingDto() + } }; - var result = sut.Execute(left, right); + sut.Execute(context); - result.Should().BeEquivalentTo(left, o => o.RespectingRuntimeTypes()); + context.TransactionOutput.Should().BeEquivalentTo(context.ApiFetchOutput, o => o.RespectingRuntimeTypes()); } - [Test] [AutoMockData] + [Test, AutoMockData] public void Sonarr_right_and_left_with_rename( MediaNamingTransactionPhase sut) { - var left = new SonarrMediaNamingDto - { - RenameEpisodes = false, - SeasonFolderFormat = "season_default", - SeriesFolderFormat = "series_plex", - StandardEpisodeFormat = "episodes_standard_default", - DailyEpisodeFormat = "episodes_daily_default", - AnimeEpisodeFormat = "episodes_anime_default" - }; - - var right = new ProcessedNamingConfig + var context = new MediaNamingPipelineContext { - Dto = new SonarrMediaNamingDto + ApiFetchOutput = new SonarrMediaNamingDto { - RenameEpisodes = true, - SeasonFolderFormat = "season_default2", - SeriesFolderFormat = "series_plex2", - StandardEpisodeFormat = "episodes_standard_default2", - DailyEpisodeFormat = "episodes_daily_default2", - AnimeEpisodeFormat = "episodes_anime_default2" + RenameEpisodes = false, + SeasonFolderFormat = "season_default", + SeriesFolderFormat = "series_plex", + StandardEpisodeFormat = "episodes_standard_default", + DailyEpisodeFormat = "episodes_daily_default", + AnimeEpisodeFormat = "episodes_anime_default" + }, + ConfigOutput = new ProcessedNamingConfig + { + Dto = new SonarrMediaNamingDto + { + RenameEpisodes = true, + SeasonFolderFormat = "season_default2", + SeriesFolderFormat = "series_plex2", + StandardEpisodeFormat = "episodes_standard_default2", + DailyEpisodeFormat = "episodes_daily_default2", + AnimeEpisodeFormat = "episodes_anime_default2" + } } }; - var result = sut.Execute(left, right); + sut.Execute(context); - result.Should().BeEquivalentTo(right.Dto, o => o.RespectingRuntimeTypes()); + context.TransactionOutput.Should().BeEquivalentTo(context.ConfigOutput.Dto, o => o.RespectingRuntimeTypes()); } - [Test] [AutoMockData] + [Test, AutoMockData] public void Sonarr_right_and_left_without_rename( MediaNamingTransactionPhase sut) { - var left = new SonarrMediaNamingDto - { - RenameEpisodes = true, - SeasonFolderFormat = "season_default", - SeriesFolderFormat = "series_plex", - StandardEpisodeFormat = "episodes_standard_default", - DailyEpisodeFormat = "episodes_daily_default", - AnimeEpisodeFormat = "episodes_anime_default" - }; - - var right = new ProcessedNamingConfig + var context = new MediaNamingPipelineContext { - Dto = new SonarrMediaNamingDto + ApiFetchOutput = new SonarrMediaNamingDto { - RenameEpisodes = false, - SeasonFolderFormat = "season_default2", - SeriesFolderFormat = "series_plex2", - StandardEpisodeFormat = "episodes_standard_default2", - DailyEpisodeFormat = "episodes_daily_default2", - AnimeEpisodeFormat = "episodes_anime_default2" + RenameEpisodes = true, + SeasonFolderFormat = "season_default", + SeriesFolderFormat = "series_plex", + StandardEpisodeFormat = "episodes_standard_default", + DailyEpisodeFormat = "episodes_daily_default", + AnimeEpisodeFormat = "episodes_anime_default" + }, + ConfigOutput = new ProcessedNamingConfig + { + Dto = new SonarrMediaNamingDto + { + RenameEpisodes = false, + SeasonFolderFormat = "season_default2", + SeriesFolderFormat = "series_plex2", + StandardEpisodeFormat = "episodes_standard_default2", + DailyEpisodeFormat = "episodes_daily_default2", + AnimeEpisodeFormat = "episodes_anime_default2" + } } }; - var result = sut.Execute(left, right); + sut.Execute(context); - result.Should().BeEquivalentTo(new SonarrMediaNamingDto + context.TransactionOutput.Should().BeEquivalentTo(new SonarrMediaNamingDto { RenameEpisodes = false, SeasonFolderFormat = "season_default2",