Files
Libation/Source/_Tests/LibationFileManager.Tests/SerilogConfigurationTests.cs
T
Cursor Agentandrmcrackan a297715b8a Revert "docs: the PDF and log fixes ship in 13.7.10, not 13.7.9"
This reverts 6daaf33d. Master is 13.7.8 and the next release is the 0.0.1
increment from it, so the original 13.7.9 references were correct.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-16 18:24:25 +00:00

256 lines
8.0 KiB
C#

using AssertionHelper;
using FileManager;
using LibationFileManager;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json.Linq;
using System.IO;
using System.Linq;
namespace SerilogConfigurationTests;
[TestClass]
[DoNotParallelize]
public class SerilogConfigurationTests
{
[TestCleanup]
public void Cleanup()
{
Configuration.RestoreSingletonInstance();
}
[TestMethod]
public void Validate_accepts_File_sink()
{
var config = Configuration.CreateMockInstance();
config.SetNonString(CreateSerilog("File"), "Serilog");
config.ValidateSerilogConfiguration();
}
[TestMethod]
public void Validate_accepts_Console_sink()
{
var config = Configuration.CreateMockInstance();
config.SetNonString(CreateSerilog("Console"), "Serilog");
config.ValidateSerilogConfiguration();
}
[TestMethod]
public void Validate_accepts_hand_edited_custom_sink_name()
{
var config = Configuration.CreateMockInstance();
config.SetNonString(CreateSerilog("Seq"), "Serilog");
config.ValidateSerilogConfiguration();
}
[TestMethod]
public void EnsureSerilogConfig_migrates_ZipFile_to_File()
{
var config = Configuration.CreateMockInstance();
config.SetNonString(CreateSerilog("ZipFile"), "Serilog");
config.EnsureSerilogConfig();
var serilog = (JObject)config.GetObject("Serilog")!;
var name = serilog.SelectToken("$.WriteTo[0].Name")?.Value<string>();
Assert.AreEqual("File", name);
config.ValidateSerilogConfiguration();
}
[TestMethod]
public void EnsureSerilogConfig_migrates_all_ZipFile_sinks()
{
var config = Configuration.CreateMockInstance();
config.SetNonString(new JObject
{
["MinimumLevel"] = "Information",
["WriteTo"] = new JArray
{
new JObject { ["Name"] = "ZipFile", ["Args"] = new JObject { ["path"] = "a.log" } },
new JObject { ["Name"] = "Console" },
new JObject { ["Name"] = "ZipFile", ["Args"] = new JObject { ["path"] = "b.log" } },
}
}, "Serilog");
config.EnsureSerilogConfig();
var serilog = (JObject)config.GetObject("Serilog")!;
var names = serilog.SelectTokens("$.WriteTo[*].Name").Select(t => t.Value<string>()).ToList();
CollectionAssert.AreEqual(new[] { "File", "Console", "File" }, names);
config.ValidateSerilogConfiguration();
}
[TestMethod]
public void EnsureSerilogConfig_default_rolls_the_log_on_size()
{
var config = Configuration.CreateMockInstance();
config.EnsureSerilogConfig();
var args = (JObject)((JObject)config.GetObject("Serilog")!).SelectToken("$.WriteTo[0].Args")!;
Assert.AreEqual(Configuration.LogFileSizeLimitBytes, args["fileSizeLimitBytes"]!.Value<long>());
Assert.IsTrue(args["rollOnFileSizeLimit"]!.Value<bool>());
Assert.AreEqual(Configuration.LogRetainedFileCountLimit, args["retainedFileCountLimit"]!.Value<int>());
config.ValidateSerilogConfiguration();
}
[TestMethod]
public void EnsureSerilogConfig_adds_size_rolling_to_an_existing_config()
{
// Existing installs kept the pre-13.7.9 default: monthly rolling only, which let a single
// month's log grow past the point where it can be attached to a bug report.
var config = Configuration.CreateMockInstance();
config.SetNonString(CreateSerilog("File"), "Serilog");
config.EnsureSerilogConfig();
var args = (JObject)((JObject)config.GetObject("Serilog")!).SelectToken("$.WriteTo[0].Args")!;
Assert.AreEqual(Configuration.LogFileSizeLimitBytes, args["fileSizeLimitBytes"]!.Value<long>());
Assert.IsTrue(args["rollOnFileSizeLimit"]!.Value<bool>());
Assert.AreEqual(Configuration.LogRetainedFileCountLimit, args["retainedFileCountLimit"]!.Value<int>());
// the migration must not disturb what the user already had
Assert.AreEqual("Month", args["rollingInterval"]!.Value<string>());
}
[TestMethod]
public void EnsureSerilogConfig_keeps_hand_tuned_size_rolling_args()
{
var config = Configuration.CreateMockInstance();
var serilog = CreateSerilog("File");
var existingArgs = (JObject)serilog.SelectToken("$.WriteTo[0].Args")!;
existingArgs["fileSizeLimitBytes"] = 1234;
existingArgs["rollOnFileSizeLimit"] = false;
existingArgs["retainedFileCountLimit"] = 3;
config.SetNonString(serilog, "Serilog");
config.EnsureSerilogConfig();
var args = (JObject)((JObject)config.GetObject("Serilog")!).SelectToken("$.WriteTo[0].Args")!;
Assert.AreEqual(1234, args["fileSizeLimitBytes"]!.Value<long>());
Assert.IsFalse(args["rollOnFileSizeLimit"]!.Value<bool>());
Assert.AreEqual(3, args["retainedFileCountLimit"]!.Value<int>());
}
[TestMethod]
public void EnsureSerilogConfig_leaves_a_non_File_sink_alone()
{
var config = Configuration.CreateMockInstance();
config.SetNonString(CreateSerilog("Console"), "Serilog");
config.EnsureSerilogConfig();
var args = (JObject)((JObject)config.GetObject("Serilog")!).SelectToken("$.WriteTo[0].Args")!;
Assert.IsNull(args["fileSizeLimitBytes"]);
Assert.IsNull(args["rollOnFileSizeLimit"]);
}
[TestMethod]
public void Validate_rejects_invalid_MinimumLevel()
{
var config = Configuration.CreateMockInstance();
var serilog = CreateSerilog("File");
serilog["MinimumLevel"] = "Loud";
config.SetNonString(serilog, "Serilog");
var ex = Assert.ThrowsExactly<InvalidConfigurationValueException>(config.ValidateSerilogConfiguration);
StringAssert.Contains(ex.Message, "MinimumLevel");
StringAssert.Contains(ex.Message, "Loud");
}
[TestMethod]
public void Validate_rejects_malformed_WriteTo()
{
var config = Configuration.CreateMockInstance();
config.SetNonString(new JObject
{
["MinimumLevel"] = "Information",
["WriteTo"] = "File"
}, "Serilog");
var ex = Assert.ThrowsExactly<InvalidConfigurationValueException>(config.ValidateSerilogConfiguration);
StringAssert.Contains(ex.Message, "WriteTo");
}
[TestMethod]
public void Validate_rejects_empty_WriteTo()
{
var config = Configuration.CreateMockInstance();
config.SetNonString(new JObject
{
["MinimumLevel"] = "Information",
["WriteTo"] = new JArray()
}, "Serilog");
var ex = Assert.ThrowsExactly<InvalidConfigurationValueException>(config.ValidateSerilogConfiguration);
StringAssert.Contains(ex.Message, "empty");
}
[TestMethod]
public void Validate_rejects_missing_sink_Name()
{
var config = Configuration.CreateMockInstance();
config.SetNonString(new JObject
{
["MinimumLevel"] = "Information",
["WriteTo"] = new JArray { new JObject { ["Args"] = new JObject() } }
}, "Serilog");
var ex = Assert.ThrowsExactly<InvalidConfigurationValueException>(config.ValidateSerilogConfiguration);
StringAssert.Contains(ex.Message, "Name");
}
[TestMethod]
public void Ephemeral_ZipFile_migration_is_visible_to_Validate_without_disk_write()
{
// Mirrors CLI ephemeral startup: migrate in memory, then validate from the same store ConfigureLogging reads.
var config = Configuration.CreateMockInstance();
config.IsEphemeralInstance.Should().BeTrue();
config.SetNonString(CreateSerilog("ZipFile"), "Serilog");
config.EnsureSerilogConfig();
config.ValidateSerilogConfiguration();
var serilog = (JObject)config.GetObject("Serilog")!;
Assert.AreEqual("File", serilog.SelectToken("$.WriteTo[0].Name")?.Value<string>());
}
[TestMethod]
public void Fatal_startup_message_uses_invalid_configuration_body()
{
var ex = InvalidConfigurationValueException.ForEnum(
"TokenStorageMethod",
"Nope",
typeof(AudibleApi.Authorization.TokenStorageMethod));
var message = StartupAssemblyBootstrap.GetStartupFailureMessage(ex);
Assert.IsNotNull(message);
Assert.AreEqual("Invalid Settings.json", message!.Title);
StringAssert.Contains(message.Body, "TokenStorageMethod");
StringAssert.Contains(message.Body, "Nope");
}
private static JObject CreateSerilog(string sinkName) => new()
{
["MinimumLevel"] = "Information",
["WriteTo"] = new JArray
{
new JObject
{
["Name"] = sinkName,
["Args"] = new JObject
{
["path"] = Path.Combine(Path.GetTempPath(), "LibationTestLog.log"),
["rollingInterval"] = "Month"
}
}
},
["Using"] = new JArray { "Dinah.Core", "Serilog.Exceptions" },
["Enrich"] = new JArray { "WithCaller", "WithExceptionDetails" }
};
}