Add unit tests and implementations for MongoDB index models and OpenAPI metadata
- Implemented `MongoIndexModelTests` to verify index models for various stores. - Created `OpenApiMetadataFactory` with methods to generate OpenAPI metadata. - Added tests for `OpenApiMetadataFactory` to ensure expected defaults and URL overrides. - Introduced `ObserverSurfaceSecrets` and `WebhookSurfaceSecrets` for managing secrets. - Developed `RuntimeSurfaceFsClient` and `WebhookSurfaceFsClient` for manifest retrieval. - Added dependency injection tests for `SurfaceEnvironmentRegistration` in both Observer and Webhook contexts. - Implemented tests for secret resolution in `ObserverSurfaceSecretsTests` and `WebhookSurfaceSecretsTests`. - Created `EnsureLinkNotMergeCollectionsMigrationTests` to validate MongoDB migration logic. - Added project files for MongoDB tests and NuGet package mirroring.
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Findings.Ledger.Observability;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Findings.Ledger.Tests;
|
||||
|
||||
public class LedgerMetricsTests
|
||||
{
|
||||
[Fact]
|
||||
public void ProjectionLagGauge_RecordsLatestPerTenant()
|
||||
{
|
||||
using var listener = CreateListener();
|
||||
var measurements = new List<Measurement<double>>();
|
||||
|
||||
listener.SetMeasurementEventCallback<double>((instrument, measurement, tags, state) =>
|
||||
{
|
||||
if (instrument.Name == "ledger_projection_lag_seconds")
|
||||
{
|
||||
measurements.Add(measurement);
|
||||
}
|
||||
});
|
||||
|
||||
LedgerMetrics.RecordProjectionLag(TimeSpan.FromSeconds(42), "tenant-a");
|
||||
|
||||
listener.RecordObservableInstruments();
|
||||
|
||||
var measurement = measurements.Should().ContainSingle().Subject;
|
||||
measurement.Value.Should().BeApproximately(42, precision: 0.001);
|
||||
measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
|
||||
.Should().Contain(new KeyValuePair<string, object?>("tenant", "tenant-a"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MerkleAnchorDuration_EmitsHistogramMeasurement()
|
||||
{
|
||||
using var listener = CreateListener();
|
||||
var measurements = new List<Measurement<double>>();
|
||||
|
||||
listener.SetMeasurementEventCallback<double>((instrument, measurement, tags, state) =>
|
||||
{
|
||||
if (instrument.Name == "ledger_merkle_anchor_duration_seconds")
|
||||
{
|
||||
measurements.Add(measurement);
|
||||
}
|
||||
});
|
||||
|
||||
LedgerMetrics.RecordMerkleAnchorDuration(TimeSpan.FromSeconds(1.5), "tenant-b");
|
||||
|
||||
var measurement = measurements.Should().ContainSingle().Subject;
|
||||
measurement.Value.Should().BeApproximately(1.5, precision: 0.001);
|
||||
measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
|
||||
.Should().Contain(new KeyValuePair<string, object?>("tenant", "tenant-b"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MerkleAnchorFailure_IncrementsCounter()
|
||||
{
|
||||
using var listener = CreateListener();
|
||||
var measurements = new List<Measurement<long>>();
|
||||
|
||||
listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
|
||||
{
|
||||
if (instrument.Name == "ledger_merkle_anchor_failures_total")
|
||||
{
|
||||
measurements.Add(measurement);
|
||||
}
|
||||
});
|
||||
|
||||
LedgerMetrics.RecordMerkleAnchorFailure("tenant-c", "persist_failure");
|
||||
|
||||
var measurement = measurements.Should().ContainSingle().Subject;
|
||||
measurement.Value.Should().Be(1);
|
||||
var tags = measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
tags.Should().Contain(new KeyValuePair<string, object?>("tenant", "tenant-c"));
|
||||
tags.Should().Contain(new KeyValuePair<string, object?>("reason", "persist_failure"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AttachmentFailure_IncrementsCounter()
|
||||
{
|
||||
using var listener = CreateListener();
|
||||
var measurements = new List<Measurement<long>>();
|
||||
|
||||
listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
|
||||
{
|
||||
if (instrument.Name == "ledger_attachments_encryption_failures_total")
|
||||
{
|
||||
measurements.Add(measurement);
|
||||
}
|
||||
});
|
||||
|
||||
LedgerMetrics.RecordAttachmentFailure("tenant-d", "encrypt");
|
||||
|
||||
var measurement = measurements.Should().ContainSingle().Subject;
|
||||
measurement.Value.Should().Be(1);
|
||||
var tags = measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
tags.Should().Contain(new KeyValuePair<string, object?>("tenant", "tenant-d"));
|
||||
tags.Should().Contain(new KeyValuePair<string, object?>("stage", "encrypt"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BacklogGauge_ReflectsOutstandingQueue()
|
||||
{
|
||||
using var listener = CreateListener();
|
||||
var measurements = new List<Measurement<long>>();
|
||||
|
||||
// Reset
|
||||
LedgerMetrics.DecrementBacklog("tenant-q");
|
||||
|
||||
LedgerMetrics.IncrementBacklog("tenant-q");
|
||||
LedgerMetrics.IncrementBacklog("tenant-q");
|
||||
LedgerMetrics.DecrementBacklog("tenant-q");
|
||||
|
||||
listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
|
||||
{
|
||||
if (instrument.Name == "ledger_ingest_backlog_events")
|
||||
{
|
||||
measurements.Add(measurement);
|
||||
}
|
||||
});
|
||||
|
||||
listener.RecordObservableInstruments();
|
||||
|
||||
var measurement = measurements.Should().ContainSingle().Subject;
|
||||
measurement.Value.Should().Be(1);
|
||||
measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
|
||||
.Should().Contain(new KeyValuePair<string, object?>("tenant", "tenant-q"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectionRebuildHistogram_RecordsScenarioTags()
|
||||
{
|
||||
using var listener = CreateListener();
|
||||
var measurements = new List<Measurement<double>>();
|
||||
|
||||
listener.SetMeasurementEventCallback<double>((instrument, measurement, tags, state) =>
|
||||
{
|
||||
if (instrument.Name == "ledger_projection_rebuild_seconds")
|
||||
{
|
||||
measurements.Add(measurement);
|
||||
}
|
||||
});
|
||||
|
||||
LedgerMetrics.RecordProjectionRebuild(TimeSpan.FromSeconds(3.2), "tenant-r", "replay");
|
||||
|
||||
var measurement = measurements.Should().ContainSingle().Subject;
|
||||
measurement.Value.Should().BeApproximately(3.2, 0.001);
|
||||
var tags = measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
tags.Should().Contain(new KeyValuePair<string, object?>("tenant", "tenant-r"));
|
||||
tags.Should().Contain(new KeyValuePair<string, object?>("scenario", "replay"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DbConnectionsGauge_TracksRoleCounts()
|
||||
{
|
||||
using var listener = CreateListener();
|
||||
var measurements = new List<Measurement<long>>();
|
||||
|
||||
// Reset
|
||||
LedgerMetrics.DecrementDbConnection("writer");
|
||||
|
||||
LedgerMetrics.IncrementDbConnection("writer");
|
||||
|
||||
listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
|
||||
{
|
||||
if (instrument.Name == "ledger_db_connections_active")
|
||||
{
|
||||
measurements.Add(measurement);
|
||||
}
|
||||
});
|
||||
|
||||
listener.RecordObservableInstruments();
|
||||
|
||||
var measurement = measurements.Should().ContainSingle().Subject;
|
||||
measurement.Value.Should().Be(1);
|
||||
measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
|
||||
.Should().Contain(new KeyValuePair<string, object?>("role", "writer"));
|
||||
|
||||
LedgerMetrics.DecrementDbConnection("writer");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VersionInfoGauge_EmitsConstantOne()
|
||||
{
|
||||
using var listener = CreateListener();
|
||||
var measurements = new List<Measurement<long>>();
|
||||
|
||||
listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
|
||||
{
|
||||
if (instrument.Name == "ledger_app_version_info")
|
||||
{
|
||||
measurements.Add(measurement);
|
||||
}
|
||||
});
|
||||
|
||||
listener.RecordObservableInstruments();
|
||||
|
||||
var measurement = measurements.Should().ContainSingle().Subject;
|
||||
measurement.Value.Should().Be(1);
|
||||
var tags = measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
tags.Should().ContainKey("version");
|
||||
tags.Should().ContainKey("git_sha");
|
||||
}
|
||||
|
||||
private static MeterListener CreateListener()
|
||||
{
|
||||
var listener = new MeterListener
|
||||
{
|
||||
InstrumentPublished = (instrument, l) =>
|
||||
{
|
||||
if (instrument.Meter.Name == "StellaOps.Findings.Ledger")
|
||||
{
|
||||
l.EnableMeasurementEvents(instrument);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
listener.Start();
|
||||
return listener;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user