using Serilog; using Serilog.Core; using Serilog.Events; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; using ZB.MOM.WW.OtOpcUa.Core.Observability; namespace ZB.MOM.WW.OtOpcUa.Core.Tests.Observability; [Trait("Category", "Unit")] public sealed class LogContextEnricherTests { [Fact] public void Scope_Attaches_AllFour_Properties() { var captured = new InMemorySink(); var logger = new LoggerConfiguration() .Enrich.FromLogContext() .WriteTo.Sink(captured) .CreateLogger(); using (LogContextEnricher.Push("drv-1", "Modbus", DriverCapability.Read, "abc123")) { logger.Information("test message"); } var evt = captured.Events.ShouldHaveSingleItem(); evt.Properties["DriverInstanceId"].ToString().ShouldBe("\"drv-1\""); evt.Properties["DriverType"].ToString().ShouldBe("\"Modbus\""); evt.Properties["CapabilityName"].ToString().ShouldBe("\"Read\""); evt.Properties["CorrelationId"].ToString().ShouldBe("\"abc123\""); } [Fact] public void Scope_Dispose_Pops_Properties() { var captured = new InMemorySink(); var logger = new LoggerConfiguration() .Enrich.FromLogContext() .WriteTo.Sink(captured) .CreateLogger(); using (LogContextEnricher.Push("drv-1", "Modbus", DriverCapability.Read, "abc123")) { logger.Information("inside"); } logger.Information("outside"); captured.Events.Count.ShouldBe(2); captured.Events[0].Properties.ContainsKey("DriverInstanceId").ShouldBeTrue(); captured.Events[1].Properties.ContainsKey("DriverInstanceId").ShouldBeFalse(); } [Fact] public void NewCorrelationId_Returns_12_Hex_Chars() { var id = LogContextEnricher.NewCorrelationId(); id.Length.ShouldBe(12); id.ShouldMatch("^[0-9a-f]{12}$"); } [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] public void Push_Throws_OnMissingDriverInstanceId(string? id) { Should.Throw(() => LogContextEnricher.Push(id!, "Modbus", DriverCapability.Read, "c")); } private sealed class InMemorySink : ILogEventSink { public List Events { get; } = []; public void Emit(LogEvent logEvent) => Events.Add(logEvent); } }