mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-09-13 06:07:30 -04:00
The rollback test holds the file it restores open with the sharing mode .NET uses for a loaded assembly. Against the old File.Copy(overwrite: true) it fails with 'the process cannot access the file because it is being used by another process', which is the same refusal Windows gives for a loaded assembly and the reason the rollback could never finish. The rest pin the behaviour that was missing: recovery survives a log sink that throws on every call, a displaced file is swept up later, a stale or absent install assembly is recognised and both versions are read, a plain file path is not mistaken for an assembly reference so the EF Core message keeps its own wording, and the crash record appends to the newest Log*.log and reports the path it used. Co-authored-by: rmcrackan <rmcrackan@gmail.com>
85 lines
2.8 KiB
C#
85 lines
2.8 KiB
C#
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
namespace LibationFileManager.Tests;
|
|
|
|
/// <summary>
|
|
/// Issue #2001: startup ran long before Serilog was configured, so its log calls wrote nowhere, and on a
|
|
/// broken install they threw and ended startup instead.
|
|
/// </summary>
|
|
[TestClass]
|
|
[DoNotParallelize]
|
|
public class StartupLogTests
|
|
{
|
|
[TestInitialize]
|
|
public void Initialize() => StartupLog.ResetForTests();
|
|
|
|
[TestCleanup]
|
|
public void Cleanup() => StartupLog.ResetForTests();
|
|
|
|
[TestMethod]
|
|
public void Entries_recorded_before_a_sink_exists_are_kept_in_order()
|
|
{
|
|
StartupLog.Information("first");
|
|
StartupLog.Warning("second");
|
|
StartupLog.Error(new InvalidOperationException("boom"), "third");
|
|
|
|
var buffered = StartupLog.BufferedEntries;
|
|
|
|
Assert.AreEqual(3, buffered.Count);
|
|
CollectionAssert.AreEqual(new[] { "first", "second", "third" }, buffered.Select(e => e.Message).ToArray());
|
|
CollectionAssert.AreEqual(
|
|
new[] { StartupLogLevel.Information, StartupLogLevel.Warning, StartupLogLevel.Error },
|
|
buffered.Select(e => e.Level).ToArray());
|
|
Assert.AreEqual("boom", buffered[2].Exception?.Message);
|
|
}
|
|
|
|
// The point of buffering: these messages used to be dropped even on a healthy install, because
|
|
// Serilog.Log.Logger is still Serilog's silent logger until logging is configured.
|
|
[TestMethod]
|
|
public void Everything_buffered_reaches_the_sink_once_logging_exists()
|
|
{
|
|
StartupLog.Information("before");
|
|
|
|
var delivered = new List<StartupLogEntry>();
|
|
StartupLog.ReplayTo(delivered.Add);
|
|
|
|
StartupLog.Information("after");
|
|
|
|
CollectionAssert.AreEqual(new[] { "before", "after" }, delivered.Select(e => e.Message).ToArray());
|
|
Assert.AreEqual(0, StartupLog.BufferedEntries.Count, "a replayed entry should not also stay buffered");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void A_sink_that_throws_is_contained()
|
|
{
|
|
StartupLog.ReplayTo(_ => throw new FileNotFoundException(
|
|
"Could not load file or assembly 'Serilog, Version=4.3.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10'."));
|
|
|
|
// The caller is mid-recovery on a broken install. Logging must not be what ends it.
|
|
StartupLog.Error("this must not throw");
|
|
StartupLog.Warning(new Exception("nor this"), "still fine");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void A_sink_that_throws_while_draining_the_buffer_is_contained_too()
|
|
{
|
|
StartupLog.Information("buffered before the bad sink arrived");
|
|
|
|
StartupLog.ReplayTo(_ => throw new InvalidOperationException("sink is broken"));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void The_buffer_is_bounded_so_it_cannot_grow_without_a_sink()
|
|
{
|
|
for (var i = 0; i < 5_000; i++)
|
|
StartupLog.Information($"message {i}");
|
|
|
|
Assert.IsTrue(StartupLog.BufferedEntries.Count < 5_000, "the buffer should stop growing");
|
|
Assert.IsTrue(StartupLog.BufferedEntries.Count > 0);
|
|
}
|
|
}
|