parent
e3d6d4f79a
commit
cffb8d783a
@ -1,7 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<attachedFolders>
|
||||
<Path>../../../code</Path>
|
||||
</attachedFolders>
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
|
@ -1,20 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="radarr --list-custom-formats" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0/recyclarr.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="radarr --list-custom-formats" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/Recyclarr.Cli.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net7.0" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,20 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="radarr --list-qualities" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/Recyclarr/bin/Debug/net7.0/recyclarr.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="radarr --list-qualities" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Recyclarr/bin/Debug/net7.0" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/Recyclarr.Cli.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net7.0" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,20 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="radarr --preview" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0/recyclarr.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="radarr --preview" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/Recyclarr.Cli.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net7.0" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,20 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="sonarr" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0/recyclarr.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="sonarr" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/Recyclarr.Cli.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net7.0" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,20 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="sonarr --list-custom-formats" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0/recyclarr.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="sonarr --list-custom-formats" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/Recyclarr.Cli.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net7.0" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,20 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="sonarr --list-qualities" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/Recyclarr/bin/Debug/net7.0/recyclarr.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="sonarr --list-qualities" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Recyclarr/bin/Debug/net7.0" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/Recyclarr.Cli.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net7.0" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,20 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="sonarr --list-release-profiles" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0/recyclarr.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="sonarr --list-release-profiles" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/Recyclarr.Cli.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net7.0" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,20 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="sonarr --preview" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/Recyclarr/bin/Debug/net7.0/recyclarr.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="sonarr --preview" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Recyclarr/bin/Debug/net7.0" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/Recyclarr.Cli.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net7.0" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,23 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="sonarr (custom config dir)" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/Recyclarr/bin/Debug/net7.0/recyclarr.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="sonarr" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Recyclarr/bin/Debug/net7.0" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<envs>
|
||||
<env name="RECYCLARR_APP_DATA" value="$PROJECT_DIR$/../docker/config" />
|
||||
</envs>
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/Recyclarr.Cli.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net7.0" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,7 +1,7 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="radarr" type="DotNetProject" factoryName=".NET Project">
|
||||
<configuration default="false" name="sync --preview" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0/recyclarr.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="radarr" />
|
||||
<option name="PROGRAM_PARAMETERS" value="sync --preview" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
@ -1,35 +1,35 @@
|
||||
using System.IO.Abstractions;
|
||||
using System.IO.Abstractions.TestingHelpers;
|
||||
using System.Reflection;
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace Recyclarr.Common.TestLibrary;
|
||||
|
||||
public static class CommonMockFileSystemExtensions
|
||||
{
|
||||
public static void AddFileFromResource(this MockFileSystem fs, string resourceFilename)
|
||||
{
|
||||
fs.AddFileFromResource(resourceFilename, resourceFilename, Assembly.GetCallingAssembly());
|
||||
}
|
||||
|
||||
public static void AddFileFromResource(this MockFileSystem fs, IFileInfo file, string resourceFilename,
|
||||
string resourceDir = "Data")
|
||||
public static void AddFileFromEmbeddedResource(
|
||||
this MockFileSystem fs,
|
||||
IFileInfo path,
|
||||
Assembly resourceAssembly,
|
||||
string embeddedResourcePath)
|
||||
{
|
||||
fs.AddFileFromResource(file.FullName, resourceFilename, Assembly.GetCallingAssembly(), resourceDir);
|
||||
fs.AddFileFromEmbeddedResource(path.FullName, resourceAssembly, embeddedResourcePath);
|
||||
}
|
||||
|
||||
public static void AddFileFromResource(this MockFileSystem fs, string file, string resourceFilename,
|
||||
string resourceDir = "Data")
|
||||
public static void AddSameFileFromEmbeddedResource(
|
||||
this MockFileSystem fs,
|
||||
IFileInfo path,
|
||||
Type typeInAssembly,
|
||||
string resourceSubPath = "Data")
|
||||
{
|
||||
fs.AddFileFromResource(file, resourceFilename, Assembly.GetCallingAssembly(), resourceDir);
|
||||
fs.AddFileFromEmbeddedResource(path, typeInAssembly, $"{resourceSubPath}.{path.Name}");
|
||||
}
|
||||
|
||||
public static void AddFileFromResource(this MockFileSystem fs, string file, string resourceFilename,
|
||||
Assembly assembly, string resourceDir = "Data")
|
||||
public static void AddFileFromEmbeddedResource(
|
||||
this MockFileSystem fs,
|
||||
IFileInfo path,
|
||||
Type typeInAssembly,
|
||||
string embeddedResourcePath)
|
||||
{
|
||||
var resourceReader = new ResourceDataReader(assembly, resourceDir);
|
||||
fs.AddFile(file, new MockFileData(resourceReader.ReadData(resourceFilename)));
|
||||
var resourcePath = $"{typeInAssembly.Namespace}.{embeddedResourcePath}";
|
||||
fs.AddFileFromEmbeddedResource(path, typeInAssembly.Assembly, resourcePath);
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
using Autofac;
|
||||
|
||||
namespace Recyclarr.Common.Autofac;
|
||||
|
||||
public sealed class LifetimeScopedValue<T> : IDisposable
|
||||
{
|
||||
private readonly ILifetimeScope _scope;
|
||||
|
||||
public LifetimeScopedValue(ILifetimeScope scope, T value)
|
||||
{
|
||||
_scope = scope;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public T Value { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_scope.Dispose();
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
namespace Recyclarr.Common;
|
||||
|
||||
public sealed class GenericEqualityComparer<T> : IEqualityComparer<T>
|
||||
{
|
||||
private readonly Func<T, T, bool> _equalsPredicate;
|
||||
private readonly Func<T, int> _hashPredicate;
|
||||
|
||||
public GenericEqualityComparer(Func<T, T, bool> equalsPredicate, Func<T, int> hashPredicate)
|
||||
{
|
||||
_equalsPredicate = equalsPredicate;
|
||||
_hashPredicate = hashPredicate;
|
||||
}
|
||||
|
||||
public bool Equals(T? x, T? y)
|
||||
{
|
||||
if (ReferenceEquals(x, y))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(x, null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(y, null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (x.GetType() != y.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _equalsPredicate(x, y);
|
||||
}
|
||||
|
||||
public int GetHashCode(T obj)
|
||||
{
|
||||
return _hashPredicate(obj);
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models;
|
||||
|
||||
namespace Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
public static class CfTestUtils
|
||||
{
|
||||
public static QualityProfileCustomFormatScoreMapping NewMapping(params FormatMappingEntry[] entries)
|
||||
{
|
||||
return new QualityProfileCustomFormatScoreMapping(false) {Mapping = entries.ToList()};
|
||||
}
|
||||
|
||||
public static QualityProfileCustomFormatScoreMapping NewMappingWithReset(params FormatMappingEntry[] entries)
|
||||
{
|
||||
return new QualityProfileCustomFormatScoreMapping(true) {Mapping = entries.ToList()};
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
using Recyclarr.TrashLib.Config;
|
||||
using Recyclarr.TrashLib.Processors;
|
||||
|
||||
namespace Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
public static class MockSyncSettings
|
||||
{
|
||||
private static ISyncSettings MakeSyncSettings(SupportedServices? service, params string[] instances)
|
||||
{
|
||||
var settings = Substitute.For<ISyncSettings>();
|
||||
settings.Service.Returns(service);
|
||||
settings.Instances.Returns(instances);
|
||||
return settings;
|
||||
}
|
||||
|
||||
public static ISyncSettings Radarr(params string[] instances)
|
||||
{
|
||||
return MakeSyncSettings(SupportedServices.Radarr, instances);
|
||||
}
|
||||
|
||||
public static ISyncSettings Sonarr(params string[] instances)
|
||||
{
|
||||
return MakeSyncSettings(SupportedServices.Sonarr, instances);
|
||||
}
|
||||
|
||||
public static ISyncSettings AnyService(params string[] instances)
|
||||
{
|
||||
return MakeSyncSettings(null, instances);
|
||||
}
|
||||
}
|
@ -1,51 +1,27 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models;
|
||||
using Recyclarr.TrashLib.Pipelines.CustomFormat.Models;
|
||||
|
||||
namespace Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
public static class NewCf
|
||||
{
|
||||
public static CustomFormatData Data(string name, string trashId, int? score = null)
|
||||
public static CustomFormatData DataWithScore(string name, string trashId, int score, int id = 0)
|
||||
{
|
||||
return Data(name, trashId, score, JObject.Parse($"{{'name':'{name}'}}"));
|
||||
}
|
||||
|
||||
public static CustomFormatData Data(string name, string trashId, int? score, JObject json)
|
||||
{
|
||||
return new CustomFormatData("", name, trashId, score, json);
|
||||
}
|
||||
|
||||
public static ProcessedCustomFormatData ProcessedWithScore(string name, string trashId, int score, JObject json)
|
||||
{
|
||||
return new ProcessedCustomFormatData(Data(name, trashId, score, json));
|
||||
}
|
||||
|
||||
public static ProcessedCustomFormatData ProcessedWithScore(string name, string trashId, int score, int formatId = 0)
|
||||
{
|
||||
return new ProcessedCustomFormatData(Data(name, trashId, score))
|
||||
{
|
||||
FormatId = formatId
|
||||
};
|
||||
}
|
||||
|
||||
public static ProcessedCustomFormatData Processed(string name, string trashId, JObject json)
|
||||
{
|
||||
return Processed(name, trashId, 0, json);
|
||||
}
|
||||
|
||||
public static ProcessedCustomFormatData Processed(string name, string trashId, int formatId = 0)
|
||||
{
|
||||
return new ProcessedCustomFormatData(Data(name, trashId))
|
||||
return new CustomFormatData
|
||||
{
|
||||
FormatId = formatId
|
||||
Id = id,
|
||||
Name = name,
|
||||
TrashId = trashId,
|
||||
TrashScore = score
|
||||
};
|
||||
}
|
||||
|
||||
public static ProcessedCustomFormatData Processed(string name, string trashId, int formatId, JObject json)
|
||||
public static CustomFormatData Data(string name, string trashId, int id = 0)
|
||||
{
|
||||
return new ProcessedCustomFormatData(Data(name, trashId, null, json))
|
||||
return new CustomFormatData
|
||||
{
|
||||
FormatId = formatId
|
||||
Id = id,
|
||||
Name = name,
|
||||
TrashId = trashId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
using Recyclarr.TrashLib.Config.Services;
|
||||
using Recyclarr.TrashLib.Pipelines.QualityProfile.PipelinePhases;
|
||||
|
||||
namespace Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
public static class NewQp
|
||||
{
|
||||
public static ProcessedQualityProfileData Processed(
|
||||
string profileName,
|
||||
params (int FormatId, int Score)[] scores)
|
||||
{
|
||||
return Processed(profileName, false, scores);
|
||||
}
|
||||
|
||||
public static ProcessedQualityProfileData Processed(
|
||||
string profileName,
|
||||
bool resetUnmatchedScores,
|
||||
params (int FormatId, int Score)[] scores)
|
||||
{
|
||||
return new ProcessedQualityProfileData(new QualityProfileConfig(profileName, resetUnmatchedScores))
|
||||
{
|
||||
CfScores = scores.ToDictionary(x => x.FormatId, x => x.Score)
|
||||
};
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"trash_id": "abc",
|
||||
"name": "No Score"
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"trash_id": "xyz",
|
||||
"trash_score": -100,
|
||||
"name": "One that won't be in config"
|
||||
}
|
@ -1,167 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using FluentAssertions;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using Recyclarr.Common;
|
||||
using Recyclarr.TestLibrary.FluentAssertions;
|
||||
using Recyclarr.TrashLib.Config.Services;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Guide;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Processors;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Processors.GuideSteps;
|
||||
using Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
namespace Recyclarr.TrashLib.Tests.CustomFormat.Processors;
|
||||
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public class GuideProcessorTest
|
||||
{
|
||||
private sealed class TestGuideProcessorSteps : IGuideProcessorSteps
|
||||
{
|
||||
public ICustomFormatStep CustomFormat { get; } = new CustomFormatStep();
|
||||
public IConfigStep Config { get; } = new ConfigStep();
|
||||
public IQualityProfileStep QualityProfile { get; } = new QualityProfileStep();
|
||||
}
|
||||
|
||||
private sealed class Context
|
||||
{
|
||||
public Context()
|
||||
{
|
||||
Data = new ResourceDataReader(typeof(GuideProcessorTest), "Data");
|
||||
}
|
||||
|
||||
public ResourceDataReader Data { get; }
|
||||
|
||||
public CustomFormatData ReadCustomFormat(string textFile)
|
||||
{
|
||||
var parser = new CustomFormatParser();
|
||||
return parser.ParseCustomFormatData(ReadText(textFile), "");
|
||||
}
|
||||
|
||||
public string ReadText(string textFile)
|
||||
{
|
||||
return Data.ReadData(textFile);
|
||||
}
|
||||
|
||||
public JObject ReadJson(string jsonFile)
|
||||
{
|
||||
return JObject.Parse(ReadText(jsonFile));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[SuppressMessage("Maintainability", "CA1506", Justification = "Designed to be a high-level integration test")]
|
||||
public async Task Guide_processor_behaves_as_expected_with_normal_guide_data()
|
||||
{
|
||||
var ctx = new Context();
|
||||
var guideService = Substitute.For<ICustomFormatGuideService>();
|
||||
var guideProcessor = new GuideProcessor(new TestGuideProcessorSteps(), guideService);
|
||||
|
||||
// simulate guide data
|
||||
guideService.GetCustomFormatData(default!).ReturnsForAnyArgs(new[]
|
||||
{
|
||||
ctx.ReadCustomFormat("ImportableCustomFormat1.json"),
|
||||
ctx.ReadCustomFormat("ImportableCustomFormat2.json"),
|
||||
ctx.ReadCustomFormat("NoScore.json"),
|
||||
ctx.ReadCustomFormat("WontBeInConfig.json")
|
||||
});
|
||||
|
||||
// Simulate user config in YAML
|
||||
var config = new List<CustomFormatConfig>
|
||||
{
|
||||
new()
|
||||
{
|
||||
TrashIds = new List<string>
|
||||
{
|
||||
"43bb5f09c79641e7a22e48d440bd8868", // Surround SOUND
|
||||
"4eb3c272d48db8ab43c2c85283b69744", // DTS-HD/DTS:X
|
||||
"abc", // no score
|
||||
"not in guide 1"
|
||||
},
|
||||
QualityProfiles = new List<QualityProfileScoreConfig>
|
||||
{
|
||||
new() {Name = "profile1"},
|
||||
new() {Name = "profile2", Score = -1234}
|
||||
}
|
||||
},
|
||||
new()
|
||||
{
|
||||
TrashIds = new List<string>
|
||||
{
|
||||
"abc", // no score
|
||||
"not in guide 2"
|
||||
},
|
||||
QualityProfiles = new List<QualityProfileScoreConfig>
|
||||
{
|
||||
new() {Name = "profile3"},
|
||||
new() {Name = "profile4", Score = 5678}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await guideProcessor.BuildGuideDataAsync(config, null, default!);
|
||||
|
||||
var expectedProcessedCustomFormatData = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.ProcessedWithScore("Surround Sound", "43bb5f09c79641e7a22e48d440bd8868", 500,
|
||||
ctx.ReadJson("ImportableCustomFormat1_Processed.json")),
|
||||
NewCf.ProcessedWithScore("DTS-HD/DTS:X", "4eb3c272d48db8ab43c2c85283b69744", 480,
|
||||
ctx.ReadJson("ImportableCustomFormat2_Processed.json")),
|
||||
NewCf.Processed("No Score", "abc")
|
||||
};
|
||||
|
||||
guideProcessor.ProcessedCustomFormats.Should()
|
||||
.BeEquivalentTo(expectedProcessedCustomFormatData, op => op.Using(new JsonEquivalencyStep()));
|
||||
|
||||
guideProcessor.ConfigData.Should()
|
||||
.BeEquivalentTo(new List<ProcessedConfigData>
|
||||
{
|
||||
new()
|
||||
{
|
||||
CustomFormats = expectedProcessedCustomFormatData,
|
||||
QualityProfiles = config[0].QualityProfiles
|
||||
},
|
||||
new()
|
||||
{
|
||||
CustomFormats = expectedProcessedCustomFormatData.GetRange(2, 1),
|
||||
QualityProfiles = config[1].QualityProfiles
|
||||
}
|
||||
}, op => op.Using(new JsonEquivalencyStep()));
|
||||
|
||||
guideProcessor.CustomFormatsWithoutScore.Should()
|
||||
.Equal(new List<(string name, string trashId, string profileName)>
|
||||
{
|
||||
("No Score", "abc", "profile1"),
|
||||
("No Score", "abc", "profile3")
|
||||
});
|
||||
|
||||
guideProcessor.CustomFormatsNotInGuide.Should().Equal(new List<string>
|
||||
{
|
||||
"not in guide 1", "not in guide 2"
|
||||
});
|
||||
|
||||
guideProcessor.ProfileScores.Should()
|
||||
.BeEquivalentTo(new Dictionary<string, QualityProfileCustomFormatScoreMapping>
|
||||
{
|
||||
{
|
||||
"profile1", CfTestUtils.NewMapping(
|
||||
new FormatMappingEntry(expectedProcessedCustomFormatData[0], 500),
|
||||
new FormatMappingEntry(expectedProcessedCustomFormatData[1], 480))
|
||||
},
|
||||
{
|
||||
"profile2", CfTestUtils.NewMapping(
|
||||
new FormatMappingEntry(expectedProcessedCustomFormatData[0], -1234),
|
||||
new FormatMappingEntry(expectedProcessedCustomFormatData[1], -1234),
|
||||
new FormatMappingEntry(expectedProcessedCustomFormatData[2], -1234))
|
||||
},
|
||||
{
|
||||
"profile4", CfTestUtils.NewMapping(
|
||||
new FormatMappingEntry(expectedProcessedCustomFormatData[2], 5678))
|
||||
}
|
||||
}, op => op
|
||||
.Using(new JsonEquivalencyStep())
|
||||
.ComparingByMembers<FormatMappingEntry>());
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
using Recyclarr.TestLibrary.AutoFixture;
|
||||
using Recyclarr.TrashLib.Config.Services;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Processors.GuideSteps;
|
||||
using Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
namespace Recyclarr.TrashLib.Tests.CustomFormat.Processors.GuideSteps;
|
||||
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public class ConfigStepTest
|
||||
{
|
||||
[Test, AutoMockData]
|
||||
public void Custom_formats_missing_from_config_are_skipped(ConfigStep processor)
|
||||
{
|
||||
var testProcessedCfs = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("name1", "id1"),
|
||||
NewCf.Processed("name2", "id2")
|
||||
};
|
||||
|
||||
var testConfig = new CustomFormatConfig[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
TrashIds = new List<string> {"id1"}
|
||||
}
|
||||
};
|
||||
|
||||
processor.Process(testProcessedCfs, testConfig);
|
||||
|
||||
processor.CustomFormatsNotInGuide.Should().BeEmpty();
|
||||
processor.ConfigData.Should().BeEquivalentTo(new List<ProcessedConfigData>
|
||||
{
|
||||
new()
|
||||
{
|
||||
CustomFormats = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("name1", "id1")
|
||||
}
|
||||
}
|
||||
}, op => op
|
||||
.Using<JToken>(jctx => jctx.Subject.Should().BeEquivalentTo(jctx.Expectation))
|
||||
.WhenTypeIs<JToken>());
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Custom_formats_missing_from_guide_are_added_to_not_in_guide_list(ConfigStep processor)
|
||||
{
|
||||
var testProcessedCfs = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("name1", "id1"),
|
||||
NewCf.Processed("name2", "id2")
|
||||
};
|
||||
|
||||
var testConfig = new CustomFormatConfig[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
TrashIds = new List<string> {"id1", "id3"}
|
||||
}
|
||||
};
|
||||
|
||||
processor.Process(testProcessedCfs, testConfig);
|
||||
|
||||
processor.CustomFormatsNotInGuide.Should().BeEquivalentTo(new List<string> {"id3"}, op => op
|
||||
.Using<JToken>(jctx => jctx.Subject.Should().BeEquivalentTo(jctx.Expectation))
|
||||
.WhenTypeIs<JToken>());
|
||||
processor.ConfigData.Should().BeEquivalentTo(new List<ProcessedConfigData>
|
||||
{
|
||||
new()
|
||||
{
|
||||
CustomFormats = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("name1", "id1")
|
||||
}
|
||||
}
|
||||
}, op => op
|
||||
.Using<JToken>(jctx => jctx.Subject.Should().BeEquivalentTo(jctx.Expectation))
|
||||
.WhenTypeIs<JToken>());
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Duplicate_config_trash_ids_are_ignored(ConfigStep processor)
|
||||
{
|
||||
var testProcessedCfs = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("name1", "id1")
|
||||
};
|
||||
|
||||
var testConfig = new CustomFormatConfig[]
|
||||
{
|
||||
new() {TrashIds = new List<string> {"id1", "id1"}}
|
||||
};
|
||||
|
||||
processor.Process(testProcessedCfs, testConfig);
|
||||
|
||||
processor.CustomFormatsNotInGuide.Should().BeEmpty();
|
||||
processor.ConfigData.Should().BeEquivalentTo(new List<ProcessedConfigData>
|
||||
{
|
||||
new()
|
||||
{
|
||||
CustomFormats = new List<ProcessedCustomFormatData> {testProcessedCfs[0]}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using Recyclarr.TestLibrary.AutoFixture;
|
||||
using Recyclarr.TestLibrary.FluentAssertions;
|
||||
using Recyclarr.TrashLib.Config.Services;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Processors.GuideSteps;
|
||||
using Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
namespace Recyclarr.TrashLib.Tests.CustomFormat.Processors.GuideSteps;
|
||||
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public class CustomFormatStepTest
|
||||
{
|
||||
private sealed class Context
|
||||
{
|
||||
public List<CustomFormatData> TestGuideData { get; } = new()
|
||||
{
|
||||
NewCf.Data("name1", "id1"),
|
||||
NewCf.Data("name2", "id2"),
|
||||
NewCf.Data("name3", "id3")
|
||||
};
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Cfs_not_in_config_are_skipped(CustomFormatStep processor)
|
||||
{
|
||||
var ctx = new Context();
|
||||
var testConfig = new List<CustomFormatConfig>
|
||||
{
|
||||
new() {TrashIds = new List<string> {"id1", "id3"}}
|
||||
};
|
||||
|
||||
processor.Process(ctx.TestGuideData, testConfig, new CustomFormatCache());
|
||||
|
||||
processor.DeletedCustomFormatsInCache.Should().BeEmpty();
|
||||
processor.ProcessedCustomFormats.Should()
|
||||
.BeEquivalentTo(new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("name1", "id1"),
|
||||
NewCf.Processed("name3", "id3")
|
||||
});
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Config_cfs_in_different_sections_are_processed(CustomFormatStep processor)
|
||||
{
|
||||
var ctx = new Context();
|
||||
var testConfig = new List<CustomFormatConfig>
|
||||
{
|
||||
new() {TrashIds = new List<string> {"id1", "id3"}},
|
||||
new() {TrashIds = new List<string> {"id2"}}
|
||||
};
|
||||
|
||||
processor.Process(ctx.TestGuideData, testConfig, new CustomFormatCache());
|
||||
|
||||
processor.DeletedCustomFormatsInCache.Should().BeEmpty();
|
||||
processor.ProcessedCustomFormats.Should().BeEquivalentTo(new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("name1", "id1"),
|
||||
NewCf.Processed("name2", "id2"),
|
||||
NewCf.Processed("name3", "id3")
|
||||
},
|
||||
op => op.Using(new JsonEquivalencyStep()));
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Custom_format_is_deleted_if_in_config_and_cache_but_not_in_guide(CustomFormatStep processor)
|
||||
{
|
||||
var guideData = new List<CustomFormatData>
|
||||
{
|
||||
NewCf.Data("name1", "id1")
|
||||
};
|
||||
|
||||
var testConfig = new List<CustomFormatConfig>
|
||||
{
|
||||
new() {TrashIds = new List<string> {"id1"}}
|
||||
};
|
||||
|
||||
var testCache = new CustomFormatCache
|
||||
{
|
||||
TrashIdMappings = new Collection<TrashIdMapping> {new("id1000", "", 1)}
|
||||
};
|
||||
|
||||
processor.Process(guideData, testConfig, testCache);
|
||||
|
||||
processor.DeletedCustomFormatsInCache.Should()
|
||||
.BeEquivalentTo(new[] {new TrashIdMapping("id1000", "", 1)});
|
||||
processor.ProcessedCustomFormats.Should().BeEquivalentTo(new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("name1", "id1")
|
||||
});
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Custom_format_is_deleted_if_not_in_config_but_in_cache_and_in_guide(CustomFormatStep processor)
|
||||
{
|
||||
var cache = new CustomFormatCache
|
||||
{
|
||||
TrashIdMappings = new Collection<TrashIdMapping> {new("id1", "", 9)}
|
||||
};
|
||||
|
||||
var guideCfs = new List<CustomFormatData>
|
||||
{
|
||||
NewCf.Data("3D", "id1")
|
||||
};
|
||||
|
||||
processor.Process(guideCfs, Array.Empty<CustomFormatConfig>(), cache);
|
||||
|
||||
processor.DeletedCustomFormatsInCache.Should().BeEquivalentTo(new[] {cache.TrashIdMappings[0]});
|
||||
processor.ProcessedCustomFormats.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Match_custom_format_using_trash_id(CustomFormatStep processor)
|
||||
{
|
||||
var guideData = new List<CustomFormatData>
|
||||
{
|
||||
NewCf.Data("name1", "id1"),
|
||||
NewCf.Data("name2", "id2")
|
||||
};
|
||||
|
||||
var testConfig = new List<CustomFormatConfig>
|
||||
{
|
||||
new() {TrashIds = new List<string> {"id2"}}
|
||||
};
|
||||
|
||||
processor.Process(guideData, testConfig, null);
|
||||
|
||||
processor.DeletedCustomFormatsInCache.Should().BeEmpty();
|
||||
processor.ProcessedCustomFormats.Should()
|
||||
.BeEquivalentTo(new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("name2", "id2")
|
||||
});
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Non_existent_cfs_in_config_are_skipped(CustomFormatStep processor)
|
||||
{
|
||||
var ctx = new Context();
|
||||
var testConfig = new List<CustomFormatConfig>
|
||||
{
|
||||
new() {TrashIds = new List<string> {"doesnt_exist"}}
|
||||
};
|
||||
|
||||
processor.Process(ctx.TestGuideData, testConfig, new CustomFormatCache());
|
||||
|
||||
processor.DeletedCustomFormatsInCache.Should().BeEmpty();
|
||||
processor.ProcessedCustomFormats.Should().BeEmpty();
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using Recyclarr.TrashLib.Config.Services;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Processors.GuideSteps;
|
||||
using Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
namespace Recyclarr.TrashLib.Tests.CustomFormat.Processors.GuideSteps;
|
||||
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public class QualityProfileStepTest
|
||||
{
|
||||
[Test]
|
||||
public void No_score_used_if_no_score_in_config_or_guide()
|
||||
{
|
||||
var testConfigData = new List<ProcessedConfigData>
|
||||
{
|
||||
new()
|
||||
{
|
||||
CustomFormats = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("name1", "id1")
|
||||
},
|
||||
QualityProfiles = new List<QualityProfileScoreConfig>
|
||||
{
|
||||
new() {Name = "profile1"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var processor = new QualityProfileStep();
|
||||
processor.Process(testConfigData);
|
||||
|
||||
processor.ProfileScores.Should().BeEmpty();
|
||||
processor.CustomFormatsWithoutScore.Should().Equal(("name1", "id1", "profile1"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Overwrite_score_from_guide_if_config_defines_score()
|
||||
{
|
||||
var testConfigData = new List<ProcessedConfigData>
|
||||
{
|
||||
new()
|
||||
{
|
||||
CustomFormats = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("", "id1", 100)
|
||||
},
|
||||
QualityProfiles = new List<QualityProfileScoreConfig>
|
||||
{
|
||||
new() {Name = "profile1", Score = 50}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var processor = new QualityProfileStep();
|
||||
processor.Process(testConfigData);
|
||||
|
||||
processor.ProfileScores.Should()
|
||||
.ContainKey("profile1").WhoseValue.Should()
|
||||
.BeEquivalentTo(
|
||||
CfTestUtils.NewMapping(new FormatMappingEntry(testConfigData[0].CustomFormats.First(), 50)));
|
||||
|
||||
processor.CustomFormatsWithoutScore.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Use_guide_score_if_no_score_in_config()
|
||||
{
|
||||
var testConfigData = new List<ProcessedConfigData>
|
||||
{
|
||||
new()
|
||||
{
|
||||
CustomFormats = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.ProcessedWithScore("", "id1", 100)
|
||||
},
|
||||
QualityProfiles = new List<QualityProfileScoreConfig>
|
||||
{
|
||||
new() {Name = "profile1"},
|
||||
new() {Name = "profile2", Score = null}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var processor = new QualityProfileStep();
|
||||
processor.Process(testConfigData);
|
||||
|
||||
var expectedScoreEntries =
|
||||
CfTestUtils.NewMapping(new FormatMappingEntry(testConfigData[0].CustomFormats.First(), 100));
|
||||
|
||||
processor.ProfileScores.Should().BeEquivalentTo(
|
||||
new Dictionary<string, QualityProfileCustomFormatScoreMapping>
|
||||
{
|
||||
{"profile1", expectedScoreEntries},
|
||||
{"profile2", expectedScoreEntries}
|
||||
});
|
||||
|
||||
processor.CustomFormatsWithoutScore.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Zero_score_is_not_ignored()
|
||||
{
|
||||
var testConfigData = new List<ProcessedConfigData>
|
||||
{
|
||||
new()
|
||||
{
|
||||
CustomFormats = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.ProcessedWithScore("name1", "id1", 0)
|
||||
},
|
||||
QualityProfiles = new List<QualityProfileScoreConfig>
|
||||
{
|
||||
new() {Name = "profile1"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var processor = new QualityProfileStep();
|
||||
processor.Process(testConfigData);
|
||||
|
||||
processor.ProfileScores.Should()
|
||||
.ContainKey("profile1").WhoseValue.Should()
|
||||
.BeEquivalentTo(CfTestUtils.NewMapping(new FormatMappingEntry(testConfigData[0].CustomFormats.First(), 0)));
|
||||
|
||||
processor.CustomFormatsWithoutScore.Should().BeEmpty();
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Api;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Processors;
|
||||
using Recyclarr.TrashLib.Services.Radarr.Config;
|
||||
|
||||
namespace Recyclarr.TrashLib.Tests.CustomFormat.Processors;
|
||||
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public class PersistenceProcessorTest
|
||||
{
|
||||
[Test]
|
||||
public async Task Custom_formats_are_deleted_if_deletion_option_is_enabled_in_config()
|
||||
{
|
||||
var steps = Substitute.For<IPersistenceProcessorSteps>();
|
||||
var cfApi = Substitute.For<ICustomFormatService>();
|
||||
|
||||
var config = new RadarrConfiguration {DeleteOldCustomFormats = true};
|
||||
|
||||
var guideCfs = Array.Empty<ProcessedCustomFormatData>();
|
||||
var deletedCfsInCache = new Collection<TrashIdMapping>();
|
||||
var profileScores = new Dictionary<string, QualityProfileCustomFormatScoreMapping>();
|
||||
|
||||
var processor = new PersistenceProcessor(cfApi, steps);
|
||||
await processor.PersistCustomFormats(config, guideCfs, deletedCfsInCache, profileScores);
|
||||
|
||||
steps.JsonTransactionStep.Received().RecordDeletions(Arg.Is(deletedCfsInCache), Arg.Any<List<JObject>>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Custom_formats_are_not_deleted_if_deletion_option_is_disabled_in_config()
|
||||
{
|
||||
var steps = Substitute.For<IPersistenceProcessorSteps>();
|
||||
var cfApi = Substitute.For<ICustomFormatService>();
|
||||
|
||||
var config = new RadarrConfiguration {DeleteOldCustomFormats = false};
|
||||
|
||||
var guideCfs = Array.Empty<ProcessedCustomFormatData>();
|
||||
var deletedCfsInCache = Array.Empty<TrashIdMapping>();
|
||||
var profileScores = new Dictionary<string, QualityProfileCustomFormatScoreMapping>();
|
||||
|
||||
var processor = new PersistenceProcessor(cfApi, steps);
|
||||
await processor.PersistCustomFormats(config, guideCfs, deletedCfsInCache, profileScores);
|
||||
|
||||
steps.JsonTransactionStep.DidNotReceive()
|
||||
.RecordDeletions(Arg.Any<IEnumerable<TrashIdMapping>>(), Arg.Any<List<JObject>>());
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using Recyclarr.TrashLib.Config.Services;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Api;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps;
|
||||
using Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
namespace Recyclarr.TrashLib.Tests.CustomFormat.Processors.PersistenceSteps;
|
||||
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public class CustomFormatApiPersistenceStepTest
|
||||
{
|
||||
private static ProcessedCustomFormatData QuickMakeCf(string cfName, string trashId, int cfId)
|
||||
{
|
||||
return NewCf.Processed(cfName, trashId, cfId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task All_api_operations_behave_normally()
|
||||
{
|
||||
var transactions = new CustomFormatTransactionData();
|
||||
transactions.NewCustomFormats.Add(QuickMakeCf("cfname1", "trashid1", 1));
|
||||
transactions.UpdatedCustomFormats.Add(QuickMakeCf("cfname2", "trashid2", 2));
|
||||
transactions.UnchangedCustomFormats.Add(QuickMakeCf("cfname3", "trashid3", 3));
|
||||
transactions.DeletedCustomFormatIds.Add(new TrashIdMapping("trashid4", "cfname4", 4));
|
||||
|
||||
var api = Substitute.For<ICustomFormatService>();
|
||||
|
||||
var processor = new CustomFormatApiPersistenceStep(api);
|
||||
var config = Substitute.For<IServiceConfiguration>();
|
||||
await processor.Process(config, transactions);
|
||||
|
||||
Received.InOrder(() =>
|
||||
{
|
||||
api.CreateCustomFormat(config, transactions.NewCustomFormats.First());
|
||||
api.UpdateCustomFormat(config, transactions.UpdatedCustomFormats.First());
|
||||
api.DeleteCustomFormat(config, 4);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,359 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
using Recyclarr.TestLibrary.AutoFixture;
|
||||
using Recyclarr.TestLibrary.FluentAssertions;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps;
|
||||
using Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
/* Sample Custom Format response from Radarr API
|
||||
{
|
||||
"id": 1,
|
||||
"name": "test",
|
||||
"includeCustomFormatWhenRenaming": false,
|
||||
"specifications": [
|
||||
{
|
||||
"name": "asdf",
|
||||
"implementation": "ReleaseTitleSpecification",
|
||||
"implementationName": "Release Title",
|
||||
"infoLink": "https://wiki.servarr.com/Radarr_Settings#Custom_Formats_2",
|
||||
"negate": false,
|
||||
"required": false,
|
||||
"fields": [
|
||||
{
|
||||
"order": 0,
|
||||
"name": "value",
|
||||
"label": "Regular Expression",
|
||||
"value": "asdf",
|
||||
"type": "textbox",
|
||||
"advanced": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
namespace Recyclarr.TrashLib.Tests.CustomFormat.Processors.PersistenceSteps;
|
||||
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public class JsonTransactionStepTest
|
||||
{
|
||||
[Test, AutoMockData]
|
||||
public void Combination_of_create_update_and_unchanged_and_verify_proper_json_merging(
|
||||
JsonTransactionStep processor)
|
||||
{
|
||||
var radarrCfs = JsonConvert.DeserializeObject<List<JObject>>(@"
|
||||
[{
|
||||
'id': 1,
|
||||
'name': 'user_defined',
|
||||
'specifications': [{
|
||||
'name': 'spec1',
|
||||
'negate': false,
|
||||
'fields': [{
|
||||
'name': 'value',
|
||||
'value': 'value1'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
'id': 2,
|
||||
'name': 'updated',
|
||||
'specifications': [{
|
||||
'name': 'spec2',
|
||||
'negate': false,
|
||||
'fields': [{
|
||||
'name': 'value',
|
||||
'untouchable': 'field',
|
||||
'value': 'value1'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
'id': 3,
|
||||
'name': 'no_change',
|
||||
'specifications': [{
|
||||
'name': 'spec4',
|
||||
'negate': false,
|
||||
'fields': [{
|
||||
'name': 'value',
|
||||
'value': 'value1'
|
||||
}]
|
||||
}]
|
||||
}]")!;
|
||||
var guideCfData = JsonConvert.DeserializeObject<List<JObject>>(@"
|
||||
[{
|
||||
'name': 'created',
|
||||
'specifications': [{
|
||||
'name': 'spec5',
|
||||
'fields': {
|
||||
'value': 'value2'
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
'name': 'updated_different_name',
|
||||
'specifications': [{
|
||||
'name': 'spec2',
|
||||
'negate': true,
|
||||
'new_spec_field': 'new_spec_value',
|
||||
'fields': {
|
||||
'value': 'value2',
|
||||
'new_field': 'new_value'
|
||||
}
|
||||
}, {
|
||||
'name': 'new_spec',
|
||||
'fields': {
|
||||
'value': 'value3'
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
'name': 'no_change',
|
||||
'specifications': [{
|
||||
'name': 'spec4',
|
||||
'negate': false,
|
||||
'fields': {
|
||||
'value': 'value1'
|
||||
}
|
||||
}]
|
||||
}]")!;
|
||||
|
||||
var guideCfs = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("created", "id1", guideCfData[0]),
|
||||
NewCf.Processed("updated_different_name", "id2", 2, guideCfData[1]),
|
||||
NewCf.Processed("no_change", "id3", 3, guideCfData[2])
|
||||
};
|
||||
|
||||
processor.Process(guideCfs, radarrCfs);
|
||||
|
||||
var expectedJson = new[]
|
||||
{
|
||||
@"{
|
||||
'name': 'created',
|
||||
'specifications': [{
|
||||
'name': 'spec5',
|
||||
'fields': [{
|
||||
'name': 'value',
|
||||
'value': 'value2'
|
||||
}]
|
||||
}]
|
||||
}",
|
||||
@"{
|
||||
'id': 2,
|
||||
'name': 'updated_different_name',
|
||||
'specifications': [{
|
||||
'name': 'spec2',
|
||||
'negate': true,
|
||||
'new_spec_field': 'new_spec_value',
|
||||
'fields': [{
|
||||
'name': 'value',
|
||||
'untouchable': 'field',
|
||||
'value': 'value2',
|
||||
'new_field': 'new_value'
|
||||
}]
|
||||
}, {
|
||||
'name': 'new_spec',
|
||||
'fields': [{
|
||||
'name': 'value',
|
||||
'value': 'value3'
|
||||
}]
|
||||
}]
|
||||
}",
|
||||
@"{
|
||||
'id': 3,
|
||||
'name': 'no_change',
|
||||
'specifications': [{
|
||||
'name': 'spec4',
|
||||
'negate': false,
|
||||
'fields': [{
|
||||
'name': 'value',
|
||||
'value': 'value1'
|
||||
}]
|
||||
}]
|
||||
}"
|
||||
};
|
||||
|
||||
var expectedTransactions = new CustomFormatTransactionData();
|
||||
expectedTransactions.NewCustomFormats.Add(guideCfs[0]);
|
||||
expectedTransactions.UpdatedCustomFormats.Add(guideCfs[1]);
|
||||
expectedTransactions.UnchangedCustomFormats.Add(guideCfs[2]);
|
||||
processor.Transactions.Should().BeEquivalentTo(expectedTransactions);
|
||||
|
||||
processor.Transactions.NewCustomFormats.First().Json.Should()
|
||||
.BeEquivalentTo(JObject.Parse(expectedJson[0]), op => op.Using(new JsonEquivalencyStep()));
|
||||
|
||||
processor.Transactions.UpdatedCustomFormats.First().Json.Should()
|
||||
.BeEquivalentTo(JObject.Parse(expectedJson[1]), op => op.Using(new JsonEquivalencyStep()));
|
||||
|
||||
processor.Transactions.UnchangedCustomFormats.First().Json.Should()
|
||||
.BeEquivalentTo(JObject.Parse(expectedJson[2]), op => op.Using(new JsonEquivalencyStep()));
|
||||
|
||||
processor.Transactions.ConflictingCustomFormats.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Deletes_happen_before_updates(
|
||||
JsonTransactionStep processor)
|
||||
{
|
||||
const string radarrCfData = @"[{
|
||||
'id': 1,
|
||||
'name': 'updated',
|
||||
'specifications': [{
|
||||
'name': 'spec1',
|
||||
'fields': [{
|
||||
'name': 'value',
|
||||
'value': 'value1'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
'id': 2,
|
||||
'name': 'deleted',
|
||||
'specifications': [{
|
||||
'name': 'spec2',
|
||||
'negate': false,
|
||||
'fields': [{
|
||||
'name': 'value',
|
||||
'untouchable': 'field',
|
||||
'value': 'value1'
|
||||
}]
|
||||
}]
|
||||
}]";
|
||||
var guideCfData = JObject.Parse(@"{
|
||||
'name': 'updated',
|
||||
'specifications': [{
|
||||
'name': 'spec2',
|
||||
'fields': {
|
||||
'value': 'value2'
|
||||
}
|
||||
}]
|
||||
}");
|
||||
var deletedCfsInCache = new List<TrashIdMapping>
|
||||
{
|
||||
new("", "", 1) {CustomFormatId = 2}
|
||||
};
|
||||
|
||||
var guideCfs = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("updated", "", 1, guideCfData)
|
||||
};
|
||||
|
||||
var radarrCfs = JsonConvert.DeserializeObject<List<JObject>>(radarrCfData);
|
||||
|
||||
processor.Process(guideCfs, radarrCfs!);
|
||||
processor.RecordDeletions(deletedCfsInCache, radarrCfs!);
|
||||
|
||||
const string expectedJson = @"{
|
||||
'id': 1,
|
||||
'name': 'updated',
|
||||
'specifications': [{
|
||||
'name': 'spec2',
|
||||
'fields': [{
|
||||
'name': 'value',
|
||||
'value': 'value2'
|
||||
}]
|
||||
}]
|
||||
}";
|
||||
var expectedTransactions = new CustomFormatTransactionData();
|
||||
expectedTransactions.DeletedCustomFormatIds.Add(new TrashIdMapping("", "", 2));
|
||||
expectedTransactions.UpdatedCustomFormats.Add(guideCfs[0]);
|
||||
processor.Transactions.Should().BeEquivalentTo(expectedTransactions);
|
||||
|
||||
processor.Transactions.UpdatedCustomFormats.First().Json.Should()
|
||||
.BeEquivalentTo(JObject.Parse(expectedJson), op => op.Using(new JsonEquivalencyStep()));
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Only_delete_correct_cfs(
|
||||
JsonTransactionStep processor)
|
||||
{
|
||||
const string radarrCfData = @"[{
|
||||
'id': 1,
|
||||
'name': 'not_deleted',
|
||||
'specifications': [{
|
||||
'name': 'spec1',
|
||||
'negate': false,
|
||||
'fields': [{
|
||||
'name': 'value',
|
||||
'value': 'value1'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
'id': 2,
|
||||
'name': 'deleted',
|
||||
'specifications': [{
|
||||
'name': 'spec2',
|
||||
'negate': false,
|
||||
'fields': [{
|
||||
'name': 'value',
|
||||
'untouchable': 'field',
|
||||
'value': 'value1'
|
||||
}]
|
||||
}]
|
||||
}]";
|
||||
var deletedCfsInCache = new List<TrashIdMapping>
|
||||
{
|
||||
new("testtrashid", "", 2),
|
||||
new("", "", 3)
|
||||
};
|
||||
|
||||
var radarrCfs = JsonConvert.DeserializeObject<List<JObject>>(radarrCfData);
|
||||
|
||||
processor.RecordDeletions(deletedCfsInCache, radarrCfs!);
|
||||
|
||||
var expectedTransactions = new CustomFormatTransactionData();
|
||||
expectedTransactions.DeletedCustomFormatIds.Add(new TrashIdMapping("testtrashid", "", 2));
|
||||
processor.Transactions.Should().BeEquivalentTo(expectedTransactions);
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Conflicting_ids_detected(
|
||||
JsonTransactionStep processor)
|
||||
{
|
||||
const string serviceCfData = @"
|
||||
[{
|
||||
'id': 1,
|
||||
'name': 'first'
|
||||
}, {
|
||||
'id': 2,
|
||||
'name': 'second'
|
||||
}]";
|
||||
|
||||
var serviceCfs = JsonConvert.DeserializeObject<List<JObject>>(serviceCfData)!;
|
||||
|
||||
var guideCfs = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("first", "", 2)
|
||||
};
|
||||
|
||||
processor.Process(guideCfs, serviceCfs);
|
||||
|
||||
var expectedTransactions = new CustomFormatTransactionData();
|
||||
expectedTransactions.ConflictingCustomFormats.Add(new ConflictingCustomFormat(guideCfs[0], 1));
|
||||
processor.Transactions.Should().BeEquivalentTo(expectedTransactions);
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Service_cf_id_set_when_no_cache_entry(JsonTransactionStep processor)
|
||||
{
|
||||
const string serviceCfData = @"
|
||||
[{
|
||||
'id': 1,
|
||||
'name': 'first'
|
||||
}]";
|
||||
|
||||
var serviceCfs = JsonConvert.DeserializeObject<List<JObject>>(serviceCfData)!;
|
||||
|
||||
var guideCfs = new List<ProcessedCustomFormatData>
|
||||
{
|
||||
NewCf.Processed("first", "")
|
||||
};
|
||||
|
||||
processor.Process(guideCfs, serviceCfs);
|
||||
|
||||
processor.Transactions.UpdatedCustomFormats.Should().BeEquivalentTo(
|
||||
new[] {NewCf.Processed("first", "", 1)},
|
||||
o => o.Including(x => x.FormatId));
|
||||
}
|
||||
}
|
@ -1,257 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Json;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using Recyclarr.TestLibrary.NSubstitute;
|
||||
using Recyclarr.TrashLib.Config.Services;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Api;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Models;
|
||||
using Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps;
|
||||
using Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
namespace Recyclarr.TrashLib.Tests.CustomFormat.Processors.PersistenceSteps;
|
||||
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public class QualityProfileApiPersistenceStepTest
|
||||
{
|
||||
[Test]
|
||||
public async Task Do_not_invoke_api_if_no_scores_to_update()
|
||||
{
|
||||
const string radarrQualityProfileData = @"[{
|
||||
'name': 'profile1',
|
||||
'formatItems': [{
|
||||
'format': 1,
|
||||
'name': 'cf1',
|
||||
'score': 1
|
||||
},
|
||||
{
|
||||
'format': 2,
|
||||
'name': 'cf2',
|
||||
'score': 0
|
||||
},
|
||||
{
|
||||
'format': 3,
|
||||
'name': 'cf3',
|
||||
'score': 3
|
||||
}
|
||||
],
|
||||
'id': 1
|
||||
}]";
|
||||
|
||||
var api = Substitute.For<IQualityProfileService>();
|
||||
api.GetQualityProfiles(default!)!.ReturnsForAnyArgs(
|
||||
JsonConvert.DeserializeObject<List<JObject>>(radarrQualityProfileData));
|
||||
|
||||
var cfScores = new Dictionary<string, QualityProfileCustomFormatScoreMapping>
|
||||
{
|
||||
{
|
||||
"profile1", CfTestUtils.NewMapping(new FormatMappingEntry(NewCf.Processed("", "", 4), 100))
|
||||
}
|
||||
};
|
||||
|
||||
var processor = new QualityProfileApiPersistenceStep(api);
|
||||
await processor.Process(Substitute.For<IServiceConfiguration>(), cfScores);
|
||||
|
||||
await api.DidNotReceiveWithAnyArgs().UpdateQualityProfile(default!, default!, default!);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Invalid_quality_profile_names_are_reported()
|
||||
{
|
||||
const string radarrQualityProfileData = @"[{'name': 'profile1'}]";
|
||||
|
||||
var api = Substitute.For<IQualityProfileService>();
|
||||
api.GetQualityProfiles(default!)!.ReturnsForAnyArgs(
|
||||
JsonConvert.DeserializeObject<List<JObject>>(radarrQualityProfileData));
|
||||
|
||||
var cfScores = new Dictionary<string, QualityProfileCustomFormatScoreMapping>
|
||||
{
|
||||
{"wrong_profile_name", CfTestUtils.NewMapping()}
|
||||
};
|
||||
|
||||
var processor = new QualityProfileApiPersistenceStep(api);
|
||||
await processor.Process(Substitute.For<IServiceConfiguration>(), cfScores);
|
||||
|
||||
processor.InvalidProfileNames.Should().Equal("wrong_profile_name");
|
||||
processor.UpdatedScores.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Reset_scores_for_unmatched_cfs_if_enabled()
|
||||
{
|
||||
const string radarrQualityProfileData = @"[{
|
||||
'name': 'profile1',
|
||||
'formatItems': [{
|
||||
'format': 1,
|
||||
'name': 'cf1',
|
||||
'score': 1
|
||||
},
|
||||
{
|
||||
'format': 2,
|
||||
'name': 'cf2',
|
||||
'score': 50
|
||||
},
|
||||
{
|
||||
'format': 3,
|
||||
'name': 'cf3',
|
||||
'score': 3
|
||||
}
|
||||
],
|
||||
'id': 1
|
||||
}]";
|
||||
|
||||
var api = Substitute.For<IQualityProfileService>();
|
||||
api.GetQualityProfiles(default!)!.ReturnsForAnyArgs(
|
||||
JsonConvert.DeserializeObject<List<JObject>>(radarrQualityProfileData));
|
||||
|
||||
var cfScores = new Dictionary<string, QualityProfileCustomFormatScoreMapping>
|
||||
{
|
||||
{
|
||||
"profile1", CfTestUtils.NewMappingWithReset(
|
||||
new FormatMappingEntry(NewCf.Processed("", "", 2), 100))
|
||||
}
|
||||
};
|
||||
|
||||
var processor = new QualityProfileApiPersistenceStep(api);
|
||||
await processor.Process(Substitute.For<IServiceConfiguration>(), cfScores);
|
||||
|
||||
processor.InvalidProfileNames.Should().BeEmpty();
|
||||
processor.UpdatedScores.Should()
|
||||
.ContainKey("profile1").WhoseValue.Should()
|
||||
.BeEquivalentTo(new List<UpdatedFormatScore>
|
||||
{
|
||||
new("cf1", 0, FormatScoreUpdateReason.Reset),
|
||||
new("cf2", 100, FormatScoreUpdateReason.Updated),
|
||||
new("cf3", 0, FormatScoreUpdateReason.Reset)
|
||||
});
|
||||
|
||||
await api.Received().UpdateQualityProfile(
|
||||
Arg.Any<IServiceConfiguration>(),
|
||||
Verify.That<JObject>(j => j["formatItems"]!.Children().Should().HaveCount(3)),
|
||||
Arg.Any<int>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Scores_are_set_in_quality_profile()
|
||||
{
|
||||
const string radarrQualityProfileData = @"[{
|
||||
'name': 'profile1',
|
||||
'upgradeAllowed': false,
|
||||
'cutoff': 20,
|
||||
'items': [{
|
||||
'quality': {
|
||||
'id': 10,
|
||||
'name': 'Raw-HD',
|
||||
'source': 'tv',
|
||||
'resolution': 1080,
|
||||
'modifier': 'rawhd'
|
||||
},
|
||||
'items': [],
|
||||
'allowed': false
|
||||
}
|
||||
],
|
||||
'minFormatScore': 0,
|
||||
'cutoffFormatScore': 0,
|
||||
'formatItems': [{
|
||||
'format': 4,
|
||||
'name': '3D',
|
||||
'score': 0
|
||||
},
|
||||
{
|
||||
'format': 3,
|
||||
'name': 'BR-DISK',
|
||||
'score': 0
|
||||
},
|
||||
{
|
||||
'format': 1,
|
||||
'name': 'asdf2',
|
||||
'score': 0
|
||||
}
|
||||
],
|
||||
'language': {
|
||||
'id': 1,
|
||||
'name': 'English'
|
||||
},
|
||||
'id': 1
|
||||
}]";
|
||||
|
||||
var api = Substitute.For<IQualityProfileService>();
|
||||
api.GetQualityProfiles(default!)!.ReturnsForAnyArgs(
|
||||
JsonConvert.DeserializeObject<List<JObject>>(radarrQualityProfileData));
|
||||
|
||||
var cfScores = new Dictionary<string, QualityProfileCustomFormatScoreMapping>
|
||||
{
|
||||
{
|
||||
"profile1", CfTestUtils.NewMapping(
|
||||
// First match by ID
|
||||
new FormatMappingEntry(NewCf.Processed("", "", 4), 100),
|
||||
// Should NOT match because we do not use names to assign scores
|
||||
new FormatMappingEntry(NewCf.Processed("", ""), 101),
|
||||
// Second match by ID
|
||||
new FormatMappingEntry(NewCf.Processed("", "", 1), 102))
|
||||
}
|
||||
};
|
||||
|
||||
var processor = new QualityProfileApiPersistenceStep(api);
|
||||
var config = Substitute.For<IServiceConfiguration>();
|
||||
await processor.Process(config, cfScores);
|
||||
|
||||
var expectedProfileJson = JObject.Parse(@"{
|
||||
'name': 'profile1',
|
||||
'upgradeAllowed': false,
|
||||
'cutoff': 20,
|
||||
'items': [{
|
||||
'quality': {
|
||||
'id': 10,
|
||||
'name': 'Raw-HD',
|
||||
'source': 'tv',
|
||||
'resolution': 1080,
|
||||
'modifier': 'rawhd'
|
||||
},
|
||||
'items': [],
|
||||
'allowed': false
|
||||
}
|
||||
],
|
||||
'minFormatScore': 0,
|
||||
'cutoffFormatScore': 0,
|
||||
'formatItems': [{
|
||||
'format': 4,
|
||||
'name': '3D',
|
||||
'score': 100
|
||||
},
|
||||
{
|
||||
'format': 3,
|
||||
'name': 'BR-DISK',
|
||||
'score': 0
|
||||
},
|
||||
{
|
||||
'format': 1,
|
||||
'name': 'asdf2',
|
||||
'score': 102
|
||||
}
|
||||
],
|
||||
'language': {
|
||||
'id': 1,
|
||||
'name': 'English'
|
||||
},
|
||||
'id': 1
|
||||
}");
|
||||
|
||||
await api.Received()
|
||||
.UpdateQualityProfile(
|
||||
config,
|
||||
Verify.That<JObject>(a => a.Should().BeEquivalentTo(expectedProfileJson)),
|
||||
1);
|
||||
processor.InvalidProfileNames.Should().BeEmpty();
|
||||
processor.UpdatedScores.Should()
|
||||
.ContainKey("profile1").WhoseValue.Should()
|
||||
.BeEquivalentTo(new List<UpdatedFormatScore>
|
||||
{
|
||||
new("3D", 100, FormatScoreUpdateReason.Updated),
|
||||
new("asdf2", 102, FormatScoreUpdateReason.Updated)
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
// Global using directives
|
||||
|
||||
global using Recyclarr.TestLibrary.AutoFixture;
|
@ -0,0 +1,116 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Recyclarr.Common.Extensions;
|
||||
using Recyclarr.TrashLib.Json;
|
||||
using Recyclarr.TrashLib.Pipelines.CustomFormat.Guide;
|
||||
using Recyclarr.TrashLib.Pipelines.CustomFormat.Models;
|
||||
|
||||
namespace Recyclarr.TrashLib.Tests.Pipelines.CustomFormat.Guide;
|
||||
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public class CustomFormatParserTest
|
||||
{
|
||||
[Test, AutoMockData]
|
||||
public void Deserialize_works(CustomFormatParser sut)
|
||||
{
|
||||
var jsonData = @"
|
||||
{
|
||||
'trash_id': '90cedc1fea7ea5d11298bebd3d1d3223',
|
||||
'trash_score': '-10000',
|
||||
'name': 'EVO (no WEBDL)',
|
||||
'includeCustomFormatWhenRenaming': false,
|
||||
'specifications': [
|
||||
{
|
||||
'name': 'EVO',
|
||||
'implementation': 'ReleaseTitleSpecification',
|
||||
'negate': false,
|
||||
'required': true,
|
||||
'fields': [{
|
||||
'value': '\\bEVO(TGX)?\\b'
|
||||
}]
|
||||
},
|
||||
{
|
||||
'name': 'WEBDL',
|
||||
'implementation': 'SourceSpecification',
|
||||
'negate': true,
|
||||
'required': true,
|
||||
'fields': {
|
||||
'value': 7
|
||||
}
|
||||
},
|
||||
{
|
||||
'name': 'WEBRIP',
|
||||
'implementation': 'SourceSpecification',
|
||||
'negate': true,
|
||||
'required': true,
|
||||
'fields': {
|
||||
'value': 8
|
||||
}
|
||||
}
|
||||
]
|
||||
}";
|
||||
var result = sut.ParseCustomFormatData(jsonData, "file.json");
|
||||
|
||||
result.Should().BeEquivalentTo(new CustomFormatData
|
||||
{
|
||||
FileName = "file.json",
|
||||
TrashId = "90cedc1fea7ea5d11298bebd3d1d3223",
|
||||
TrashScore = -10000,
|
||||
Name = "EVO (no WEBDL)",
|
||||
IncludeCustomFormatWhenRenaming = false,
|
||||
Specifications = new[]
|
||||
{
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "EVO",
|
||||
Implementation = "ReleaseTitleSpecification",
|
||||
Negate = false,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = "\\bEVO(TGX)?\\b"
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBDL",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 7
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBRIP",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 8
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Serialize_skips_trash_properties(CustomFormatParser sut)
|
||||
{
|
||||
var cf = new CustomFormatData
|
||||
{
|
||||
FileName = "file.json",
|
||||
TrashId = "90cedc1fea7ea5d11298bebd3d1d3223",
|
||||
TrashScore = -10000,
|
||||
Name = "EVO (no WEBDL)",
|
||||
IncludeCustomFormatWhenRenaming = false
|
||||
};
|
||||
|
||||
var json = JObject.FromObject(cf, ServiceJsonSerializerFactory.Create());
|
||||
|
||||
json.Children<JProperty>().Should().NotContain(x => x.Name.ContainsIgnoreCase("trash"));
|
||||
}
|
||||
}
|
@ -0,0 +1,371 @@
|
||||
using Recyclarr.TrashLib.Pipelines.CustomFormat.Models;
|
||||
|
||||
namespace Recyclarr.TrashLib.Tests.Pipelines.CustomFormat.Models;
|
||||
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public class CustomFormatDataComparerTest
|
||||
{
|
||||
[Test]
|
||||
public void Custom_formats_equal()
|
||||
{
|
||||
var a = new CustomFormatData
|
||||
{
|
||||
Name = "EVO (no WEBDL)",
|
||||
IncludeCustomFormatWhenRenaming = false,
|
||||
Specifications = new[]
|
||||
{
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "EVO",
|
||||
Implementation = "ReleaseTitleSpecification",
|
||||
Negate = false,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = "\\bEVO(TGX)?\\b"
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBDL",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 7
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBRIP",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 8
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var b = new CustomFormatData
|
||||
{
|
||||
Name = "EVO (no WEBDL)",
|
||||
IncludeCustomFormatWhenRenaming = false,
|
||||
Specifications = new[]
|
||||
{
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "EVO",
|
||||
Implementation = "ReleaseTitleSpecification",
|
||||
Negate = false,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = "\\bEVO(TGX)?\\b"
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBDL",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 7
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBRIP",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 8
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = CustomFormatData.Comparer.Equals(a, b);
|
||||
|
||||
result.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Custom_formats_not_equal_when_field_value_different()
|
||||
{
|
||||
var a = new CustomFormatData
|
||||
{
|
||||
Name = "EVO (no WEBDL)",
|
||||
IncludeCustomFormatWhenRenaming = false,
|
||||
Specifications = new[]
|
||||
{
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "EVO",
|
||||
Implementation = "ReleaseTitleSpecification",
|
||||
Negate = false,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = "\\bEVO(TGX)?\\b"
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBDL",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 7
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBRIP",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 8
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var b = new CustomFormatData
|
||||
{
|
||||
Name = "EVO (no WEBDL)",
|
||||
IncludeCustomFormatWhenRenaming = false,
|
||||
Specifications = new[]
|
||||
{
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "EVO",
|
||||
Implementation = "ReleaseTitleSpecification",
|
||||
Negate = false,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = "\\bEVO(TGX)?\\b"
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBDL",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 10 // this is different
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBRIP",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 8
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = CustomFormatData.Comparer.Equals(a, b);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Equal_when_ignored_fields_are_different()
|
||||
{
|
||||
var a = new CustomFormatData
|
||||
{
|
||||
FileName = "file1.json",
|
||||
TrashId = "a",
|
||||
TrashScore = 1,
|
||||
Category = "one"
|
||||
};
|
||||
|
||||
var b = new CustomFormatData
|
||||
{
|
||||
FileName = "file2.json",
|
||||
TrashId = "b",
|
||||
TrashScore = 2,
|
||||
Category = "two"
|
||||
};
|
||||
|
||||
var result = CustomFormatData.Comparer.Equals(a, b);
|
||||
|
||||
result.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Not_equal_when_right_is_null()
|
||||
{
|
||||
var a = new CustomFormatData();
|
||||
var b = (CustomFormatData?) null;
|
||||
|
||||
var result = CustomFormatData.Comparer.Equals(a, b);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Not_equal_when_left_is_null()
|
||||
{
|
||||
var a = (CustomFormatData?) null;
|
||||
var b = new CustomFormatData();
|
||||
|
||||
var result = CustomFormatData.Comparer.Equals(a, b);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Equal_for_same_reference()
|
||||
{
|
||||
var a = new CustomFormatData();
|
||||
|
||||
var result = CustomFormatData.Comparer.Equals(a, a);
|
||||
|
||||
result.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Not_equal_when_different_spec_count()
|
||||
{
|
||||
var a = new CustomFormatData
|
||||
{
|
||||
Name = "EVO (no WEBDL)",
|
||||
IncludeCustomFormatWhenRenaming = false,
|
||||
Specifications = new[]
|
||||
{
|
||||
new CustomFormatSpecificationData(),
|
||||
new CustomFormatSpecificationData()
|
||||
}
|
||||
};
|
||||
|
||||
var b = new CustomFormatData
|
||||
{
|
||||
Name = "EVO (no WEBDL)",
|
||||
IncludeCustomFormatWhenRenaming = false,
|
||||
Specifications = new[]
|
||||
{
|
||||
new CustomFormatSpecificationData(),
|
||||
new CustomFormatSpecificationData(),
|
||||
new CustomFormatSpecificationData()
|
||||
}
|
||||
};
|
||||
|
||||
var result = CustomFormatData.Comparer.Equals(a, b);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Not_equal_when_non_matching_spec_names()
|
||||
{
|
||||
var a = new CustomFormatData
|
||||
{
|
||||
Name = "EVO (no WEBDL)",
|
||||
IncludeCustomFormatWhenRenaming = false,
|
||||
Specifications = new[]
|
||||
{
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "EVO",
|
||||
Implementation = "ReleaseTitleSpecification",
|
||||
Negate = false,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = "\\bEVO(TGX)?\\b"
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBDL",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 7
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBRIP",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 8
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var b = new CustomFormatData
|
||||
{
|
||||
Name = "EVO (no WEBDL)",
|
||||
IncludeCustomFormatWhenRenaming = false,
|
||||
Specifications = new[]
|
||||
{
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "EVO",
|
||||
Implementation = "ReleaseTitleSpecification",
|
||||
Negate = false,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = "\\bEVO(TGX)?\\b"
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBDL",
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 7
|
||||
}
|
||||
},
|
||||
new CustomFormatSpecificationData
|
||||
{
|
||||
Name = "WEBRIP2", // This name is different
|
||||
Implementation = "SourceSpecification",
|
||||
Negate = true,
|
||||
Required = true,
|
||||
Fields = new CustomFormatFieldData
|
||||
{
|
||||
Value = 8
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = CustomFormatData.Comparer.Equals(a, b);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
using Recyclarr.TrashLib.Config.Services;
|
||||
using Recyclarr.TrashLib.Pipelines.CustomFormat.Guide;
|
||||
using Recyclarr.TrashLib.Pipelines.CustomFormat.PipelinePhases;
|
||||
using Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
namespace Recyclarr.TrashLib.Tests.Pipelines.CustomFormat.PipelinePhases;
|
||||
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public class CustomFormatConfigPhaseTest
|
||||
{
|
||||
[Test, AutoMockData]
|
||||
public void Return_configs_that_exist_in_guide(
|
||||
[Frozen] ICustomFormatGuideService guide,
|
||||
CustomFormatConfigPhase sut)
|
||||
{
|
||||
guide.GetCustomFormatData(default!).ReturnsForAnyArgs(new[]
|
||||
{
|
||||
NewCf.Data("one", "cf1"),
|
||||
NewCf.Data("two", "cf2")
|
||||
});
|
||||
|
||||
var config = new TestConfig
|
||||
{
|
||||
CustomFormats = new List<CustomFormatConfig>
|
||||
{
|
||||
new()
|
||||
{
|
||||
TrashIds = new List<string>
|
||||
{
|
||||
"cf1",
|
||||
"cf2"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = sut.Execute(config);
|
||||
|
||||
result.Should().BeEquivalentTo(new[]
|
||||
{
|
||||
NewCf.Data("one", "cf1"),
|
||||
NewCf.Data("two", "cf2")
|
||||
});
|
||||
}
|
||||
|
||||
[Test, AutoMockData]
|
||||
public void Skip_configs_that_do_not_exist_in_guide(
|
||||
[Frozen] ICustomFormatGuideService guide,
|
||||
CustomFormatConfigPhase sut)
|
||||
{
|
||||
guide.GetCustomFormatData(default!).ReturnsForAnyArgs(new[]
|
||||
{
|
||||
NewCf.Data("", "cf4")
|
||||
});
|
||||
|
||||
var config = new TestConfig
|
||||
{
|
||||
CustomFormats = new List<CustomFormatConfig>
|
||||
{
|
||||
new()
|
||||
{
|
||||
TrashIds = new List<string>
|
||||
{
|
||||
"cf1",
|
||||
"cf2",
|
||||
"cf3"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = sut.Execute(config);
|
||||
|
||||
result.Should().BeEmpty();
|
||||
}
|
||||
}
|
@ -0,0 +1,413 @@
|
||||
using Recyclarr.Cli.TestLibrary;
|
||||
using Recyclarr.TrashLib.Pipelines.CustomFormat;
|
||||
using Recyclarr.TrashLib.Pipelines.CustomFormat.Models;
|
||||
using Recyclarr.TrashLib.Pipelines.CustomFormat.PipelinePhases;
|
||||
using Recyclarr.TrashLib.TestLibrary;
|
||||
|
||||
namespace Recyclarr.TrashLib.Tests.Pipelines.CustomFormat.PipelinePhases;
|
||||
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public class CustomFormatTransactionPhaseTest : IntegrationFixture
|
||||
{
|
||||
[Test]
|
||||
public void Add_new_cf()
|
||||
{
|
||||
var sut = Resolve<CustomFormatTransactionPhase>();
|
||||
|
||||
var guideCfs = new[]
|
||||
{
|
||||
NewCf.Data("one", "cf1")
|
||||
};
|
||||
|
||||
var serviceData = Array.Empty<CustomFormatData>();
|
||||
|
||||
var cache = new CustomFormatCache();
|
||||
|
||||
var config = new TestConfig();
|
||||
|
||||
var result = sut.Execute(config, guideCfs, serviceData, cache);
|
||||
|
||||
result.Should().BeEquivalentTo(new CustomFormatTransactionData
|
||||
{
|
||||
NewCustomFormats =
|
||||
{
|
||||
NewCf.Data("one", "cf1")
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Update_cf_by_matching_name()
|
||||
{
|
||||
var sut = Resolve<CustomFormatTransactionPhase>();
|
||||
|
||||
var guideCfs = new[]
|
||||
{
|
||||
new CustomFormatData
|
||||
{
|
||||
Name = "one",
|
||||
TrashId = "cf1",
|
||||
// Only set the below value to make it different from the service CF
|
||||
IncludeCustomFormatWhenRenaming = true
|
||||
}
|
||||
};
|
||||
|
||||
var serviceData = new[]
|
||||
{
|
||||
new CustomFormatData {Name = "one"}
|
||||
};
|
||||
|
||||
var cache = new CustomFormatCache();
|
||||
|
||||
var config = new TestConfig();
|
||||
|
||||
var result = sut.Execute(config, guideCfs, serviceData, cache);
|
||||
|
||||
result.Should().BeEquivalentTo(new CustomFormatTransactionData
|
||||
{
|
||||
UpdatedCustomFormats =
|
||||
{
|
||||
new CustomFormatData
|
||||
{
|
||||
Name = "one",
|
||||
IncludeCustomFormatWhenRenaming = true
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Update_cf_by_matching_id_different_names()
|
||||
{
|
||||
var sut = Resolve<CustomFormatTransactionPhase>();
|
||||
|
||||
var guideCfs = new[]
|
||||
{
|
||||
new CustomFormatData
|
||||
{
|
||||
Name = "different1",
|
||||
TrashId = "cf1",
|
||||
// Only set the below value to make it different from the service CF
|
||||
IncludeCustomFormatWhenRenaming = true
|
||||
}
|
||||
};
|
||||
|
||||
var serviceData = new[]
|
||||
{
|
||||
new CustomFormatData {Name = "different2", Id = 2}
|
||||
};
|
||||
|
||||
var cache = new CustomFormatCache
|
||||
{
|
||||
TrashIdMappings = new[]
|
||||
{
|
||||
new TrashIdMapping("cf1", "", 2)
|
||||
}
|
||||
};
|
||||
|
||||
var config = new TestConfig();
|
||||
|
||||
var result = sut.Execute(config, guideCfs, serviceData, cache);
|
||||
|
||||
result.Should().BeEquivalentTo(new CustomFormatTransactionData
|
||||
{
|
||||
UpdatedCustomFormats =
|
||||
{
|
||||
new CustomFormatData
|
||||
{
|
||||
Name = "different1",
|
||||
Id = 2,
|
||||
IncludeCustomFormatWhenRenaming = true
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Update_cf_by_matching_id_same_names()
|
||||
{
|
||||
var sut = Resolve<CustomFormatTransactionPhase>();
|
||||
|
||||
var guideCfs = new[]
|
||||
{
|
||||
new CustomFormatData
|
||||
{
|
||||
Name = "different1",
|
||||
TrashId = "cf1",
|
||||
// Only set the below value to make it different from the service CF
|
||||
IncludeCustomFormatWhenRenaming = true
|
||||
}
|
||||
};
|
||||
|
||||
var serviceData = new[]
|
||||
{
|
||||
new CustomFormatData {Name = "different1", Id = 2}
|
||||
};
|
||||
|
||||
var cache = new CustomFormatCache();
|
||||
|
||||
var config = new TestConfig();
|
||||
|
||||
var result = sut.Execute(config, guideCfs, serviceData, cache);
|
||||
|
||||
result.Should().BeEquivalentTo(new CustomFormatTransactionData
|
||||
{
|
||||
UpdatedCustomFormats =
|
||||
{
|
||||
new CustomFormatData
|
||||
{
|
||||
Name = "different1",
|
||||
Id = 2,
|
||||
IncludeCustomFormatWhenRenaming = true
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Conflicting_cf_when_new_cf_has_name_of_existing()
|
||||
{
|
||||
var sut = Resolve<CustomFormatTransactionPhase>();
|
||||
|
||||
var guideCfs = new[]
|
||||
{
|
||||
NewCf.Data("one", "cf1")
|
||||
};
|
||||
|
||||
var serviceData = new[]
|
||||
{
|
||||
new CustomFormatData {Name = "one", Id = 2},
|
||||
new CustomFormatData {Name = "two", Id = 1}
|
||||
};
|
||||
|
||||
var cache = new CustomFormatCache();
|
||||
|
||||
var config = new TestConfig
|
||||
{
|
||||
ReplaceExistingCustomFormats = false
|
||||
};
|
||||
|
||||
var result = sut.Execute(config, guideCfs, serviceData, cache);
|
||||
|
||||
result.Should().BeEquivalentTo(new CustomFormatTransactionData
|
||||
{
|
||||
ConflictingCustomFormats =
|
||||
{
|
||||
new ConflictingCustomFormat(guideCfs[0], 2)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Conflicting_cf_when_cached_cf_has_name_of_existing()
|
||||
{
|
||||
var sut = Resolve<CustomFormatTransactionPhase>();
|
||||
|
||||
var guideCfs = new[]
|
||||
{
|
||||
NewCf.Data("one", "cf1")
|
||||
};
|
||||
|
||||
var serviceData = new[]
|
||||
{
|
||||
new CustomFormatData {Name = "one", Id = 2},
|
||||
new CustomFormatData {Name = "two", Id = 1}
|
||||
};
|
||||
|
||||
var cache = new CustomFormatCache
|
||||
{
|
||||
TrashIdMappings = new[]
|
||||
{
|
||||
new TrashIdMapping("cf1", "one", 1)
|
||||
}
|
||||
};
|
||||
|
||||
var config = new TestConfig
|
||||
{
|
||||
ReplaceExistingCustomFormats = false
|
||||
};
|
||||
|
||||
var result = sut.Execute(config, guideCfs, serviceData, cache);
|
||||
|
||||
result.Should().BeEquivalentTo(new CustomFormatTransactionData
|
||||
{
|
||||
ConflictingCustomFormats =
|
||||
{
|
||||
new ConflictingCustomFormat(guideCfs[0], 2)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Updated_cf_with_matching_name_and_id()
|
||||
{
|
||||
var sut = Resolve<CustomFormatTransactionPhase>();
|
||||
|
||||
var guideCfs = new[]
|
||||
{
|
||||
new CustomFormatData
|
||||
{
|
||||
Name = "one",
|
||||
TrashId = "cf1",
|
||||
// Only set the below value to make it different from the service CF
|
||||
IncludeCustomFormatWhenRenaming = true
|
||||
}
|
||||
};
|
||||
|
||||
var serviceData = new[]
|
||||
{
|
||||
new CustomFormatData {Name = "two", Id = 2},
|
||||
new CustomFormatData {Name = "one", Id = 1}
|
||||
};
|
||||
|
||||
var cache = new CustomFormatCache
|
||||
{
|
||||
TrashIdMappings = new[]
|
||||
{
|
||||
new TrashIdMapping("cf1", "one", 1)
|
||||
}
|
||||
};
|
||||
|
||||
var config = new TestConfig
|
||||
{
|
||||
ReplaceExistingCustomFormats = false
|
||||
};
|
||||
|
||||
var result = sut.Execute(config, guideCfs, serviceData, cache);
|
||||
|
||||
result.Should().BeEquivalentTo(new CustomFormatTransactionData
|
||||
{
|
||||
UpdatedCustomFormats =
|
||||
{
|
||||
NewCf.Data("one", "cf1", 1) with {IncludeCustomFormatWhenRenaming = true}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Unchanged_cfs_with_replace()
|
||||
{
|
||||
var sut = Resolve<CustomFormatTransactionPhase>();
|
||||
|
||||
var guideCfs = new[]
|
||||
{
|
||||
NewCf.Data("one", "cf1")
|
||||
};
|
||||
|
||||
var serviceData = new[]
|
||||
{
|
||||
new CustomFormatData {Name = "one", Id = 1}
|
||||
};
|
||||
|
||||
var cache = new CustomFormatCache();
|
||||
|
||||
var config = new TestConfig();
|
||||
|
||||
var result = sut.Execute(config, guideCfs, serviceData, cache);
|
||||
|
||||
result.Should().BeEquivalentTo(new CustomFormatTransactionData
|
||||
{
|
||||
UnchangedCustomFormats = {guideCfs[0]}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Unchanged_cfs_without_replace()
|
||||
{
|
||||
var sut = Resolve<CustomFormatTransactionPhase>();
|
||||
|
||||
var guideCfs = new[]
|
||||
{
|
||||
NewCf.Data("one", "cf1")
|
||||
};
|
||||
|
||||
var serviceData = new[]
|
||||
{
|
||||
new CustomFormatData {Name = "one", Id = 1}
|
||||
};
|
||||
|
||||
var cache = new CustomFormatCache
|
||||
{
|
||||
TrashIdMappings = new[]
|
||||
{
|
||||
new TrashIdMapping("cf1", "one", 1)
|
||||
}
|
||||
};
|
||||
|
||||
var config = new TestConfig
|
||||
{
|
||||
ReplaceExistingCustomFormats = false
|
||||
};
|
||||
|
||||
var result = sut.Execute(config, guideCfs, serviceData, cache);
|
||||
|
||||
result.Should().BeEquivalentTo(new CustomFormatTransactionData
|
||||
{
|
||||
UnchangedCustomFormats = {guideCfs[0]}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Deleted_cfs()
|
||||
{
|
||||
var sut = Resolve<CustomFormatTransactionPhase>();
|
||||
|
||||
var guideCfs = Array.Empty<CustomFormatData>();
|
||||
|
||||
var serviceData = new[]
|
||||
{
|
||||
new CustomFormatData {Name = "two", Id = 2}
|
||||
};
|
||||
|
||||
var cache = new CustomFormatCache
|
||||
{
|
||||
TrashIdMappings = new[]
|
||||
{
|
||||
new TrashIdMapping("cf2", "two", 2)
|
||||
}
|
||||
};
|
||||
|
||||
var config = new TestConfig();
|
||||
|
||||
var result = sut.Execute(config, guideCfs, serviceData, cache);
|
||||
|
||||
result.Should().BeEquivalentTo(new CustomFormatTransactionData
|
||||
{
|
||||
DeletedCustomFormats =
|
||||
{
|
||||
new TrashIdMapping("cf2", "two", 2)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Do_not_delete_cfs_in_config()
|
||||
{
|
||||
var sut = Resolve<CustomFormatTransactionPhase>();
|
||||
|
||||
var guideCfs = new[]
|
||||
{
|
||||
NewCf.Data("two", "cf2", 2)
|
||||
};
|
||||
|
||||
var serviceData = new[]
|
||||
{
|
||||
new CustomFormatData {Name = "two", Id = 2}
|
||||
};
|
||||
|
||||
var cache = new CustomFormatCache
|
||||
{
|
||||
TrashIdMappings = new[]
|
||||
{
|
||||
new TrashIdMapping("cf2", "two", 2)
|
||||
}
|
||||
};
|
||||
|
||||
var config = new TestConfig();
|
||||
|
||||
var result = sut.Execute(config, guideCfs, serviceData, cache);
|
||||
|
||||
result.DeletedCustomFormats.Should().BeEmpty();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue