fix(radarr): Load cache json using snake case

The main symptom of this was that CFs were not deleted when the
`delete_old_custom_formats` property was enabled.

This fixes #71
pull/78/head
Robert Dailey 2 years ago
parent d4380bc11d
commit f6248b6b76

@ -1,10 +1,14 @@
using System.IO.Abstractions;
using System.IO.Abstractions.Extensions;
using System.IO.Abstractions.TestingHelpers;
using AutoFixture.NUnit3;
using FluentAssertions;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using NSubstitute;
using NUnit.Framework;
using Serilog;
using TestLibrary.AutoFixture;
using TestLibrary.NSubstitute;
using TrashLib.Cache;
using TrashLib.Config.Services;
@ -72,23 +76,29 @@ public class ServiceCacheTest
result.Should().BeNull();
}
[Test]
public void Loading_with_attribute_parses_correctly()
[Test, AutoMockData]
public void Loading_with_attribute_parses_correctly(
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
[Frozen] IServiceConfiguration config,
[Frozen] ICacheStoragePath storage,
ServiceCache sut)
{
var ctx = new Context();
const string testJson = @"{'test_value': 'Foo'}";
ctx.StoragePath.Path.Returns("testpath");
storage.Path.Returns("testpath");
config.BaseUrl.Returns("http://localhost:1234");
dynamic testJson = new {TestValue = "Foo"};
ctx.Filesystem.File.Exists(Arg.Any<string>()).Returns(true);
ctx.Filesystem.File.ReadAllText(Arg.Any<string>())
.Returns(_ => JsonConvert.SerializeObject(testJson));
var testJsonPath = fs.CurrentDirectory()
.SubDirectory("testpath")
.SubDirectory("be8fbc8f")
.File($"{ValidObjectName}.json").FullName;
var obj = ctx.Cache.Load<ObjectWithAttribute>();
fs.AddFile(testJsonPath, new MockFileData(testJson));
var obj = sut.Load<ObjectWithAttribute>();
obj.Should().NotBeNull();
obj!.TestValue.Should().Be("Foo");
ctx.Filesystem.File.Received().ReadAllText(Path.Combine("testpath", "be8fbc8f", $"{ValidObjectName}.json"));
}
[Test]
@ -126,22 +136,28 @@ public class ServiceCacheTest
.WriteAllText(Arg.Any<string>(), Verify.That<string>(json => json.Should().Contain("\"test_value\"")));
}
[Test]
public void Saving_with_attribute_parses_correctly()
[Test, AutoMockData]
public void Saving_with_attribute_parses_correctly(
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
[Frozen] IServiceConfiguration config,
[Frozen] ICacheStoragePath storage,
ServiceCache sut)
{
var ctx = new Context();
ctx.StoragePath.Path.Returns("testpath");
storage.Path.Returns("testpath");
config.BaseUrl.Returns("http://localhost:1234");
ctx.Cache.Save(new ObjectWithAttribute {TestValue = "Foo"});
var testJsonPath = fs.CurrentDirectory()
.SubDirectory("testpath")
.SubDirectory("be8fbc8f")
.File($"{ValidObjectName}.json").FullName;
var expectedParentDirectory = Path.Combine("testpath", "be8fbc8f");
ctx.Filesystem.Directory.Received().CreateDirectory(expectedParentDirectory);
sut.Save(new ObjectWithAttribute {TestValue = "Foo"});
dynamic expectedJson = new {TestValue = "Foo"};
var expectedPath = Path.Combine(expectedParentDirectory, $"{ValidObjectName}.json");
ctx.Filesystem.File.Received()
.WriteAllText(expectedPath, JsonConvert.SerializeObject(expectedJson, ctx.JsonSettings));
var expectedFile = fs.GetFile(testJsonPath);
expectedFile.Should().NotBeNull();
expectedFile.TextContents.Should().Be(@"{
""test_value"": ""Foo""
}");
}
[Test]
@ -168,29 +184,23 @@ public class ServiceCacheTest
.WithMessage("CacheObjectNameAttribute is missing*");
}
[Test]
public void Switching_config_and_base_url_should_yield_different_cache_paths()
[Test, AutoMockData]
public void Switching_config_and_base_url_should_yield_different_cache_paths(
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
[Frozen] IConfigurationProvider provider,
ServiceCache sut)
{
var ctx = new Context();
ctx.StoragePath.Path.Returns("testpath");
var actualPaths = new List<string>();
dynamic testJson = new {TestValue = "Foo"};
ctx.Filesystem.File.Exists(Arg.Any<string>()).Returns(true);
ctx.Filesystem.File.ReadAllText(Arg.Do<string>(s => actualPaths.Add(s)))
.Returns(_ => JsonConvert.SerializeObject(testJson));
provider.ActiveConfiguration.BaseUrl.Returns("http://localhost:1234");
ctx.Cache.Load<ObjectWithAttribute>();
sut.Save(new ObjectWithAttribute {TestValue = "Foo"});
// Change the active config & base URL so we get a different path
ctx.ConfigProvider.ActiveConfiguration = Substitute.For<IServiceConfiguration>();
ctx.ConfigProvider.ActiveConfiguration.BaseUrl.Returns("http://localhost:5678");
provider.ActiveConfiguration.BaseUrl.Returns("http://localhost:5678");
ctx.Cache.Load<ObjectWithAttribute>();
sut.Save(new ObjectWithAttribute {TestValue = "Bar"});
actualPaths.Count.Should().Be(2);
actualPaths.Should().OnlyHaveUniqueItems();
fs.AllFiles.Should().HaveCount(2)
.And.AllSatisfy(x => x.Should().EndWith("json"));
}
[Test]

@ -4,30 +4,40 @@ using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Serilog;
using TrashLib.Config.Services;
namespace TrashLib.Cache;
internal class ServiceCache : IServiceCache
public class ServiceCache : IServiceCache
{
private static readonly Regex AllowedObjectNameCharacters = new(@"^[\w-]+$", RegexOptions.Compiled);
private readonly IConfigurationProvider _configProvider;
private readonly IFileSystem _fileSystem;
private readonly IFileSystem _fs;
private readonly IFNV1a _hash;
private readonly ICacheStoragePath _storagePath;
private readonly JsonSerializerSettings _jsonSettings;
public ServiceCache(IFileSystem fileSystem, ICacheStoragePath storagePath,
public ServiceCache(
IFileSystem fs,
ICacheStoragePath storagePath,
IConfigurationProvider configProvider,
ILogger log)
{
_fileSystem = fileSystem;
_fs = fs;
_storagePath = storagePath;
_configProvider = configProvider;
Log = log;
_hash = FNV1aFactory.Instance.Create(FNVConfig.GetPredefinedConfig(32));
_jsonSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
}
};
}
private ILogger Log { get; }
@ -35,16 +45,16 @@ internal class ServiceCache : IServiceCache
public T? Load<T>() where T : class
{
var path = PathFromAttribute<T>();
if (!_fileSystem.File.Exists(path))
if (!_fs.File.Exists(path))
{
return null;
}
var json = _fileSystem.File.ReadAllText(path);
var json = _fs.File.ReadAllText(path);
try
{
return JObject.Parse(json).ToObject<T>();
return JsonConvert.DeserializeObject<T>(json, _jsonSettings);
}
catch (JsonException e)
{
@ -57,15 +67,8 @@ internal class ServiceCache : IServiceCache
public void Save<T>(T obj) where T : class
{
var path = PathFromAttribute<T>();
_fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(path));
_fileSystem.File.WriteAllText(path, JsonConvert.SerializeObject(obj, new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
}
}));
_fs.Directory.CreateDirectory(_fs.Path.GetDirectoryName(path));
_fs.File.WriteAllText(path, JsonConvert.SerializeObject(obj, _jsonSettings));
}
private static string GetCacheObjectNameAttribute<T>()
@ -93,6 +96,6 @@ internal class ServiceCache : IServiceCache
throw new ArgumentException($"Object name '{objectName}' has unacceptable characters");
}
return Path.Combine(_storagePath.Path, BuildServiceGuid(), objectName + ".json");
return _fs.Path.Combine(_storagePath.Path, BuildServiceGuid(), objectName + ".json");
}
}

Loading…
Cancel
Save