using System.Diagnostics.Metrics; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Commons.Observability; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; using ZB.MOM.WW.OtOpcUa.Runtime.Historian; namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Historian; /// /// The otopcua.historian.outbox.unreadable gauge reports the bound outbox's /// . The listener is filtered by the instrument INSTANCE (the /// shared object plus the instrument name), never by meter name alone, so a /// parallel test's measurements on another meter of the same name cannot reach it; and the bound outbox reports a /// distinctive count, so the asserted value can only have come from this test's outbox. /// public sealed class ContinuousHistorizationMetricsTests { private const string GaugeName = "otopcua.historian.outbox.unreadable"; [Fact] public void The_unreadable_gauge_reports_the_bound_outboxs_unreadable_count() { const long distinctive = 7_331_919L; ContinuousHistorizationMetrics.BindOutbox(new UnreadableOutbox(distinctive)); var observed = new List(); using var listener = new MeterListener { InstrumentPublished = (instrument, l) => { if (ReferenceEquals(instrument.Meter, OtOpcUaTelemetry.Meter) && instrument.Name == GaugeName) l.EnableMeasurementEvents(instrument); }, }; listener.SetMeasurementEventCallback((instrument, value, _, _) => { if (instrument.Name == GaugeName) observed.Add(value); }); listener.Start(); listener.RecordObservableInstruments(); observed.ShouldBe([distinctive]); } private sealed class UnreadableOutbox(long unreadable) : IHistorizationOutbox { public long DroppedCount => 0; public long UnreadableCount => unreadable; public ValueTask AppendAsync(HistorizationOutboxEntry entry, CancellationToken ct) => ValueTask.CompletedTask; public ValueTask AppendBatchAsync(IReadOnlyList entries, CancellationToken ct) => ValueTask.CompletedTask; public ValueTask> PeekBatchAsync(int max, CancellationToken ct) => ValueTask.FromResult>([]); public ValueTask RemoveAsync(Guid id, CancellationToken ct) => ValueTask.CompletedTask; public ValueTask CountAsync(CancellationToken ct) => ValueTask.FromResult(0); public void Dispose() { } } }