parent
e3d6d4f79a
commit
cffb8d783a
@ -1,7 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="UserContentModel">
|
<component name="UserContentModel">
|
||||||
<attachedFolders />
|
<attachedFolders>
|
||||||
|
<Path>../../../code</Path>
|
||||||
|
</attachedFolders>
|
||||||
<explicitIncludes />
|
<explicitIncludes />
|
||||||
<explicitExcludes />
|
<explicitExcludes />
|
||||||
</component>
|
</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">
|
<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="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="WORKING_DIRECTORY" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
@ -1,35 +1,35 @@
|
|||||||
using System.IO.Abstractions;
|
using System.IO.Abstractions;
|
||||||
using System.IO.Abstractions.TestingHelpers;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
// ReSharper disable MemberCanBePrivate.Global
|
|
||||||
// ReSharper disable UnusedMember.Global
|
|
||||||
|
|
||||||
namespace Recyclarr.Common.TestLibrary;
|
namespace Recyclarr.Common.TestLibrary;
|
||||||
|
|
||||||
public static class CommonMockFileSystemExtensions
|
public static class CommonMockFileSystemExtensions
|
||||||
{
|
{
|
||||||
public static void AddFileFromResource(this MockFileSystem fs, string resourceFilename)
|
public static void AddFileFromEmbeddedResource(
|
||||||
{
|
this MockFileSystem fs,
|
||||||
fs.AddFileFromResource(resourceFilename, resourceFilename, Assembly.GetCallingAssembly());
|
IFileInfo path,
|
||||||
}
|
Assembly resourceAssembly,
|
||||||
|
string embeddedResourcePath)
|
||||||
public static void AddFileFromResource(this MockFileSystem fs, IFileInfo file, string resourceFilename,
|
|
||||||
string resourceDir = "Data")
|
|
||||||
{
|
{
|
||||||
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,
|
public static void AddSameFileFromEmbeddedResource(
|
||||||
string resourceDir = "Data")
|
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,
|
public static void AddFileFromEmbeddedResource(
|
||||||
Assembly assembly, string resourceDir = "Data")
|
this MockFileSystem fs,
|
||||||
|
IFileInfo path,
|
||||||
|
Type typeInAssembly,
|
||||||
|
string embeddedResourcePath)
|
||||||
{
|
{
|
||||||
var resourceReader = new ResourceDataReader(assembly, resourceDir);
|
var resourcePath = $"{typeInAssembly.Namespace}.{embeddedResourcePath}";
|
||||||
fs.AddFile(file, new MockFileData(resourceReader.ReadData(resourceFilename)));
|
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.Pipelines.CustomFormat.Models;
|
||||||
using Recyclarr.TrashLib.Services.CustomFormat.Models;
|
|
||||||
|
|
||||||
namespace Recyclarr.TrashLib.TestLibrary;
|
namespace Recyclarr.TrashLib.TestLibrary;
|
||||||
|
|
||||||
public static class NewCf
|
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}'}}"));
|
return new CustomFormatData
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
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