using System.Diagnostics.Metrics; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Admin.Services; namespace ZB.MOM.WW.OtOpcUa.Admin.Tests; [Trait("Category", "Unit")] public sealed class RedundancyMetricsTests { [Fact] public void RecordRoleTransition_Increments_Counter_WithExpectedTags() { using var metrics = new RedundancyMetrics(); using var listener = new MeterListener(); var observed = new List<(long Value, Dictionary Tags)>(); listener.InstrumentPublished = (instrument, l) => { if (instrument.Meter.Name == RedundancyMetrics.MeterName && instrument.Name == "otopcua.redundancy.role_transition") { l.EnableMeasurementEvents(instrument); } }; listener.SetMeasurementEventCallback((_, value, tags, _) => { var dict = new Dictionary(); foreach (var tag in tags) dict[tag.Key] = tag.Value; observed.Add((value, dict)); }); listener.Start(); metrics.RecordRoleTransition("c1", "node-a", "Primary", "Secondary"); observed.Count.ShouldBe(1); observed[0].Value.ShouldBe(1); observed[0].Tags["cluster.id"].ShouldBe("c1"); observed[0].Tags["node.id"].ShouldBe("node-a"); observed[0].Tags["from_role"].ShouldBe("Primary"); observed[0].Tags["to_role"].ShouldBe("Secondary"); } [Fact] public void SetClusterCounts_Observed_Via_ObservableGauges() { using var metrics = new RedundancyMetrics(); metrics.SetClusterCounts("c1", primary: 1, secondary: 2, stale: 0); metrics.SetClusterCounts("c2", primary: 0, secondary: 1, stale: 1); var observations = new List<(string Name, long Value, string Cluster)>(); using var listener = new MeterListener(); listener.InstrumentPublished = (instrument, l) => { if (instrument.Meter.Name == RedundancyMetrics.MeterName) l.EnableMeasurementEvents(instrument); }; listener.SetMeasurementEventCallback((instrument, value, tags, _) => { string? cluster = null; foreach (var t in tags) if (t.Key == "cluster.id") cluster = t.Value as string; observations.Add((instrument.Name, value, cluster ?? "?")); }); listener.Start(); listener.RecordObservableInstruments(); observations.ShouldContain(o => o.Name == "otopcua.redundancy.primary_count" && o.Cluster == "c1" && o.Value == 1); observations.ShouldContain(o => o.Name == "otopcua.redundancy.secondary_count" && o.Cluster == "c1" && o.Value == 2); observations.ShouldContain(o => o.Name == "otopcua.redundancy.stale_count" && o.Cluster == "c2" && o.Value == 1); } }