|
|
@ -1,16 +1,12 @@
|
|
|
|
using System.IO.Abstractions;
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
using System.IO.Abstractions.Extensions;
|
|
|
|
|
|
|
|
using System.IO.Abstractions.TestingHelpers;
|
|
|
|
using System.IO.Abstractions.TestingHelpers;
|
|
|
|
using AutoFixture.NUnit3;
|
|
|
|
using AutoFixture.NUnit3;
|
|
|
|
using FluentAssertions;
|
|
|
|
using FluentAssertions;
|
|
|
|
using NSubstitute;
|
|
|
|
using NSubstitute;
|
|
|
|
using NUnit.Framework;
|
|
|
|
using NUnit.Framework;
|
|
|
|
using Serilog;
|
|
|
|
|
|
|
|
using TestLibrary.AutoFixture;
|
|
|
|
using TestLibrary.AutoFixture;
|
|
|
|
using TestLibrary.NSubstitute;
|
|
|
|
|
|
|
|
using TrashLib.Cache;
|
|
|
|
using TrashLib.Cache;
|
|
|
|
using TrashLib.Config.Services;
|
|
|
|
using TrashLib.Services.CustomFormat.Models.Cache;
|
|
|
|
using TrashLib.Services.Radarr.Config;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace TrashLib.Tests.Cache;
|
|
|
|
namespace TrashLib.Tests.Cache;
|
|
|
|
|
|
|
|
|
|
|
@ -18,22 +14,6 @@ namespace TrashLib.Tests.Cache;
|
|
|
|
[Parallelizable(ParallelScope.All)]
|
|
|
|
[Parallelizable(ParallelScope.All)]
|
|
|
|
public class ServiceCacheTest
|
|
|
|
public class ServiceCacheTest
|
|
|
|
{
|
|
|
|
{
|
|
|
|
private class Context
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
public Context(IFileSystem? fs = null)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Filesystem = fs ?? Substitute.For<IFileSystem>();
|
|
|
|
|
|
|
|
StoragePath = Substitute.For<ICacheStoragePath>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var config = new RadarrConfiguration {BaseUrl = "http://localhost:1234"};
|
|
|
|
|
|
|
|
Cache = new ServiceCache(Filesystem, StoragePath, config, Substitute.For<ILogger>());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ServiceCache Cache { get; }
|
|
|
|
|
|
|
|
public ICacheStoragePath StoragePath { get; }
|
|
|
|
|
|
|
|
public IFileSystem Filesystem { get; }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private class ObjectWithoutAttribute
|
|
|
|
private class ObjectWithoutAttribute
|
|
|
|
{
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -51,90 +31,79 @@ public class ServiceCacheTest
|
|
|
|
{
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
[Test, AutoMockData]
|
|
|
|
public void Load_returns_null_when_file_does_not_exist()
|
|
|
|
public void Load_returns_null_when_file_does_not_exist(
|
|
|
|
|
|
|
|
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
|
|
|
|
|
|
|
|
ServiceCache sut)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var ctx = new Context();
|
|
|
|
var result = sut.Load<ObjectWithAttribute>();
|
|
|
|
ctx.Filesystem.File.Exists(Arg.Any<string>()).Returns(false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var result = ctx.Cache.Load<ObjectWithAttribute>();
|
|
|
|
|
|
|
|
result.Should().BeNull();
|
|
|
|
result.Should().BeNull();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Test, AutoMockData]
|
|
|
|
[Test, AutoMockData]
|
|
|
|
public void Loading_with_attribute_parses_correctly(
|
|
|
|
public void Loading_with_attribute_parses_correctly(
|
|
|
|
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
|
|
|
|
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
|
|
|
|
[Frozen] IServiceConfiguration config,
|
|
|
|
|
|
|
|
[Frozen] ICacheStoragePath storage,
|
|
|
|
[Frozen] ICacheStoragePath storage,
|
|
|
|
ServiceCache sut)
|
|
|
|
ServiceCache sut)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const string testJson = @"{'test_value': 'Foo'}";
|
|
|
|
const string testJson = @"{'test_value': 'Foo'}";
|
|
|
|
|
|
|
|
|
|
|
|
storage.Path.Returns("testpath");
|
|
|
|
const string testJsonPath = "cacheFile.json";
|
|
|
|
config.BaseUrl.Returns("http://localhost:1234");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var testJsonPath = fs.CurrentDirectory()
|
|
|
|
|
|
|
|
.SubDirectory("testpath")
|
|
|
|
|
|
|
|
.SubDirectory("be8fbc8f")
|
|
|
|
|
|
|
|
.File($"{ValidObjectName}.json").FullName;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fs.AddFile(testJsonPath, new MockFileData(testJson));
|
|
|
|
fs.AddFile(testJsonPath, new MockFileData(testJson));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
storage.CalculatePath(default!).ReturnsForAnyArgs(fs.FileInfo.FromFileName(testJsonPath));
|
|
|
|
|
|
|
|
|
|
|
|
var obj = sut.Load<ObjectWithAttribute>();
|
|
|
|
var obj = sut.Load<ObjectWithAttribute>();
|
|
|
|
|
|
|
|
|
|
|
|
obj.Should().NotBeNull();
|
|
|
|
obj.Should().NotBeNull();
|
|
|
|
obj!.TestValue.Should().Be("Foo");
|
|
|
|
obj!.TestValue.Should().Be("Foo");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
[Test, AutoMockData]
|
|
|
|
public void Loading_with_invalid_object_name_throws()
|
|
|
|
public void Loading_with_invalid_object_name_throws(ServiceCache sut)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var ctx = new Context();
|
|
|
|
Action act = () => sut.Load<ObjectWithAttributeInvalidChars>();
|
|
|
|
|
|
|
|
|
|
|
|
Action act = () => ctx.Cache.Load<ObjectWithAttributeInvalidChars>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
act.Should()
|
|
|
|
act.Should()
|
|
|
|
.Throw<ArgumentException>()
|
|
|
|
.Throw<ArgumentException>()
|
|
|
|
.WithMessage("*'invalid+name' has unacceptable characters*");
|
|
|
|
.WithMessage("*'invalid+name' has unacceptable characters*");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
[Test, AutoMockData]
|
|
|
|
public void Loading_without_attribute_throws()
|
|
|
|
public void Loading_without_attribute_throws(ServiceCache sut)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var ctx = new Context();
|
|
|
|
Action act = () => sut.Load<ObjectWithoutAttribute>();
|
|
|
|
|
|
|
|
|
|
|
|
Action act = () => ctx.Cache.Load<ObjectWithoutAttribute>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
act.Should()
|
|
|
|
act.Should()
|
|
|
|
.Throw<ArgumentException>()
|
|
|
|
.Throw<ArgumentException>()
|
|
|
|
.WithMessage("CacheObjectNameAttribute is missing*");
|
|
|
|
.WithMessage("CacheObjectNameAttribute is missing*");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
[Test, AutoMockData]
|
|
|
|
public void Properties_are_saved_using_snake_case()
|
|
|
|
public void Properties_are_saved_using_snake_case(
|
|
|
|
|
|
|
|
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
|
|
|
|
|
|
|
|
[Frozen] ICacheStoragePath storage,
|
|
|
|
|
|
|
|
ServiceCache sut)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var ctx = new Context();
|
|
|
|
storage.CalculatePath(default!).ReturnsForAnyArgs(_ => fs.FileInfo.FromFileName($"{ValidObjectName}.json"));
|
|
|
|
ctx.StoragePath.Path.Returns("testpath");
|
|
|
|
|
|
|
|
ctx.Cache.Save(new ObjectWithAttribute {TestValue = "Foo"});
|
|
|
|
sut.Save(new ObjectWithAttribute {TestValue = "Foo"});
|
|
|
|
|
|
|
|
|
|
|
|
ctx.Filesystem.File.Received()
|
|
|
|
fs.AllFiles.Should().ContainMatch($"*{ValidObjectName}.json");
|
|
|
|
.WriteAllText(Arg.Any<string>(), Verify.That<string>(json => json.Should().Contain("\"test_value\"")));
|
|
|
|
|
|
|
|
|
|
|
|
var file = fs.GetFile(storage.CalculatePath("").FullName);
|
|
|
|
|
|
|
|
file.Should().NotBeNull();
|
|
|
|
|
|
|
|
file.TextContents.Should().Contain("\"test_value\"");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Test, AutoMockData]
|
|
|
|
[Test, AutoMockData]
|
|
|
|
public void Saving_with_attribute_parses_correctly(
|
|
|
|
public void Saving_with_attribute_parses_correctly(
|
|
|
|
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
|
|
|
|
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
|
|
|
|
[Frozen] IServiceConfiguration config,
|
|
|
|
|
|
|
|
[Frozen] ICacheStoragePath storage,
|
|
|
|
[Frozen] ICacheStoragePath storage,
|
|
|
|
ServiceCache sut)
|
|
|
|
ServiceCache sut)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
storage.Path.Returns("testpath");
|
|
|
|
const string testJsonPath = "cacheFile.json";
|
|
|
|
config.BaseUrl.Returns("http://localhost:1234");
|
|
|
|
storage.CalculatePath(default!).ReturnsForAnyArgs(fs.FileInfo.FromFileName(testJsonPath));
|
|
|
|
|
|
|
|
|
|
|
|
var testJsonPath = fs.CurrentDirectory()
|
|
|
|
|
|
|
|
.SubDirectory("testpath")
|
|
|
|
|
|
|
|
.SubDirectory("be8fbc8f")
|
|
|
|
|
|
|
|
.File($"{ValidObjectName}.json").FullName;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sut.Save(new ObjectWithAttribute {TestValue = "Foo"});
|
|
|
|
sut.Save(new ObjectWithAttribute {TestValue = "Foo"});
|
|
|
|
|
|
|
|
|
|
|
@ -145,24 +114,20 @@ public class ServiceCacheTest
|
|
|
|
}");
|
|
|
|
}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
[Test, AutoMockData]
|
|
|
|
public void Saving_with_invalid_object_name_throws()
|
|
|
|
public void Saving_with_invalid_object_name_throws(ServiceCache sut)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var ctx = new Context();
|
|
|
|
var act = () => sut.Save(new ObjectWithAttributeInvalidChars());
|
|
|
|
|
|
|
|
|
|
|
|
var act = () => ctx.Cache.Save(new ObjectWithAttributeInvalidChars());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
act.Should()
|
|
|
|
act.Should()
|
|
|
|
.Throw<ArgumentException>()
|
|
|
|
.Throw<ArgumentException>()
|
|
|
|
.WithMessage("*'invalid+name' has unacceptable characters*");
|
|
|
|
.WithMessage("*'invalid+name' has unacceptable characters*");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
[Test, AutoMockData]
|
|
|
|
public void Saving_without_attribute_throws()
|
|
|
|
public void Saving_without_attribute_throws(ServiceCache sut)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var ctx = new Context();
|
|
|
|
var act = () => sut.Save(new ObjectWithoutAttribute());
|
|
|
|
|
|
|
|
|
|
|
|
var act = () => ctx.Cache.Save(new ObjectWithoutAttribute());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
act.Should()
|
|
|
|
act.Should()
|
|
|
|
.Throw<ArgumentException>()
|
|
|
|
.Throw<ArgumentException>()
|
|
|
@ -172,32 +137,66 @@ public class ServiceCacheTest
|
|
|
|
[Test, AutoMockData]
|
|
|
|
[Test, AutoMockData]
|
|
|
|
public void Switching_config_and_base_url_should_yield_different_cache_paths(
|
|
|
|
public void Switching_config_and_base_url_should_yield_different_cache_paths(
|
|
|
|
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
|
|
|
|
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
|
|
|
|
[Frozen] IServiceConfiguration config,
|
|
|
|
[Frozen] ICacheStoragePath storage,
|
|
|
|
ServiceCache sut)
|
|
|
|
ServiceCache sut)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
config.BaseUrl.Returns("http://localhost:1234");
|
|
|
|
storage.CalculatePath(default!).ReturnsForAnyArgs(fs.FileInfo.FromFileName("Foo.json"));
|
|
|
|
|
|
|
|
|
|
|
|
sut.Save(new ObjectWithAttribute {TestValue = "Foo"});
|
|
|
|
sut.Save(new ObjectWithAttribute {TestValue = "Foo"});
|
|
|
|
|
|
|
|
|
|
|
|
// Change the active config & base URL so we get a different path
|
|
|
|
storage.CalculatePath(default!).ReturnsForAnyArgs(fs.FileInfo.FromFileName("Bar.json"));
|
|
|
|
config.BaseUrl.Returns("http://localhost:5678");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sut.Save(new ObjectWithAttribute {TestValue = "Bar"});
|
|
|
|
sut.Save(new ObjectWithAttribute {TestValue = "Bar"});
|
|
|
|
|
|
|
|
|
|
|
|
fs.AllFiles.Should().HaveCount(2)
|
|
|
|
var expectedFiles = new[] {"*Foo.json", "*Bar.json"};
|
|
|
|
.And.AllSatisfy(x => x.Should().EndWith("json"));
|
|
|
|
foreach (var expectedFile in expectedFiles)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
fs.AllFiles.Should().ContainMatch(expectedFile);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
[Test, AutoMockData]
|
|
|
|
public void When_cache_file_is_empty_do_not_throw()
|
|
|
|
public void When_cache_file_is_empty_do_not_throw(
|
|
|
|
|
|
|
|
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
|
|
|
|
|
|
|
|
[Frozen] ICacheStoragePath storage,
|
|
|
|
|
|
|
|
ServiceCache sut)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var ctx = new Context();
|
|
|
|
storage.CalculatePath(default!).ReturnsForAnyArgs(fs.FileInfo.FromFileName("cacheFile.json"));
|
|
|
|
ctx.Filesystem.File.Exists(Arg.Any<string>()).Returns(true);
|
|
|
|
fs.AddFile("cacheFile.json", new MockFileData(""));
|
|
|
|
ctx.Filesystem.File.ReadAllText(Arg.Any<string>())
|
|
|
|
|
|
|
|
.Returns(_ => "");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Action act = () => ctx.Cache.Load<ObjectWithAttribute>();
|
|
|
|
Action act = () => sut.Load<ObjectWithAttribute>();
|
|
|
|
|
|
|
|
|
|
|
|
act.Should().NotThrow();
|
|
|
|
act.Should().NotThrow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Test, AutoMockData]
|
|
|
|
|
|
|
|
public void Name_properties_get_truncated_on_load(
|
|
|
|
|
|
|
|
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
|
|
|
|
|
|
|
|
[Frozen] ICacheStoragePath storage,
|
|
|
|
|
|
|
|
ServiceCache sut)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const string cacheJson = @"
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
'version': 1,
|
|
|
|
|
|
|
|
'trash_id_mappings': [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
'custom_format_name': '4K Remaster',
|
|
|
|
|
|
|
|
'trash_id': 'eca37840c13c6ef2dd0262b141a5482f',
|
|
|
|
|
|
|
|
'custom_format_id': 4
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fs.AddFile("cacheFile.json", new MockFileData(cacheJson));
|
|
|
|
|
|
|
|
storage.CalculatePath(default!).ReturnsForAnyArgs(fs.FileInfo.FromFileName("cacheFile.json"));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var result = sut.Load<CustomFormatCache>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result.Should().BeEquivalentTo(new CustomFormatCache
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
TrashIdMappings = new Collection<TrashIdMapping>
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
new("eca37840c13c6ef2dd0262b141a5482f", 4)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|