Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
181 lines
7.2 KiB
C#
181 lines
7.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.Metrics;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using StellaOps.Concelier.Models;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.Concelier.Models.Tests;
|
|
|
|
public sealed class ProvenanceDiagnosticsTests
|
|
{
|
|
[Fact]
|
|
public void RecordMissing_AddsExpectedTagsAndDeduplicates()
|
|
{
|
|
ResetState();
|
|
|
|
var measurements = new List<(string Instrument, long Value, IReadOnlyDictionary<string, object?> Tags)>();
|
|
using var listener = CreateListener(measurements);
|
|
|
|
var baseline = DateTimeOffset.UtcNow;
|
|
ProvenanceDiagnostics.RecordMissing("source-A", "range:pkg", baseline, new[] { ProvenanceFieldMasks.VersionRanges });
|
|
ProvenanceDiagnostics.RecordMissing("source-A", "range:pkg", baseline.AddMinutes(5), new[] { ProvenanceFieldMasks.VersionRanges });
|
|
ProvenanceDiagnostics.RecordMissing("source-A", "reference:https://example", baseline.AddMinutes(10), new[] { ProvenanceFieldMasks.References });
|
|
|
|
listener.Dispose();
|
|
|
|
Assert.Equal(2, measurements.Count);
|
|
|
|
var first = measurements[0];
|
|
Assert.Equal(1, first.Value);
|
|
Assert.Equal("concelier.provenance.missing", first.Instrument);
|
|
Assert.Equal("source-A", first.Tags["source"]);
|
|
Assert.Equal("range:pkg", first.Tags["component"]);
|
|
Assert.Equal("range", first.Tags["category"]);
|
|
Assert.Equal("high", first.Tags["severity"]);
|
|
Assert.Equal(ProvenanceFieldMasks.VersionRanges, first.Tags["fieldMask"]);
|
|
|
|
var second = measurements[1];
|
|
Assert.Equal("concelier.provenance.missing", second.Instrument);
|
|
Assert.Equal("reference", second.Tags["category"]);
|
|
Assert.Equal("low", second.Tags["severity"]);
|
|
Assert.Equal(ProvenanceFieldMasks.References, second.Tags["fieldMask"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void ReportResumeWindow_ClearsTrackedEntries_WhenWindowBackfills()
|
|
{
|
|
ResetState();
|
|
|
|
var timestamp = DateTimeOffset.UtcNow;
|
|
ProvenanceDiagnostics.RecordMissing("source-B", "package:lib", timestamp);
|
|
|
|
var (recorded, earliest, syncRoot) = GetInternalState();
|
|
lock (syncRoot)
|
|
{
|
|
Assert.True(earliest.ContainsKey("source-B"));
|
|
Assert.Contains(recorded, entry => entry.StartsWith("source-B|", StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
|
|
ProvenanceDiagnostics.ReportResumeWindow("source-B", timestamp.AddMinutes(-5), NullLogger.Instance);
|
|
|
|
lock (syncRoot)
|
|
{
|
|
Assert.False(earliest.ContainsKey("source-B"));
|
|
Assert.DoesNotContain(recorded, entry => entry.StartsWith("source-B|", StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ReportResumeWindow_RetainsEntries_WhenWindowTooRecent()
|
|
{
|
|
ResetState();
|
|
|
|
var timestamp = DateTimeOffset.UtcNow;
|
|
ProvenanceDiagnostics.RecordMissing("source-C", "range:pkg", timestamp);
|
|
|
|
ProvenanceDiagnostics.ReportResumeWindow("source-C", timestamp.AddMinutes(1), NullLogger.Instance);
|
|
|
|
var (recorded, earliest, syncRoot) = GetInternalState();
|
|
lock (syncRoot)
|
|
{
|
|
Assert.True(earliest.ContainsKey("source-C"));
|
|
Assert.Contains(recorded, entry => entry.StartsWith("source-C|", StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void RecordRangePrimitive_EmitsCoverageMetric()
|
|
{
|
|
var range = new AffectedVersionRange(
|
|
rangeKind: "evr",
|
|
introducedVersion: "1:1.1.1n-0+deb11u2",
|
|
fixedVersion: null,
|
|
lastAffectedVersion: null,
|
|
rangeExpression: null,
|
|
provenance: new AdvisoryProvenance("source-D", "range", "pkg", DateTimeOffset.UtcNow),
|
|
primitives: new RangePrimitives(
|
|
SemVer: null,
|
|
Nevra: null,
|
|
Evr: new EvrPrimitive(
|
|
new EvrComponent(1, "1.1.1n", "0+deb11u2"),
|
|
null,
|
|
null),
|
|
VendorExtensions: new Dictionary<string, string> { ["debian.release"] = "bullseye" }));
|
|
|
|
var measurements = new List<(string Instrument, long Value, IReadOnlyDictionary<string, object?> Tags)>();
|
|
using var listener = CreateListener(measurements, "concelier.range.primitives");
|
|
|
|
ProvenanceDiagnostics.RecordRangePrimitive("source-D", range);
|
|
|
|
listener.Dispose();
|
|
|
|
var record = Assert.Single(measurements);
|
|
Assert.Equal("concelier.range.primitives", record.Instrument);
|
|
Assert.Equal(1, record.Value);
|
|
Assert.Equal("source-D", record.Tags["source"]);
|
|
Assert.Equal("evr", record.Tags["rangeKind"]);
|
|
Assert.Equal("evr", record.Tags["primitiveKinds"]);
|
|
Assert.Equal("true", record.Tags["hasVendorExtensions"]);
|
|
}
|
|
|
|
private static MeterListener CreateListener(
|
|
List<(string Instrument, long Value, IReadOnlyDictionary<string, object?> Tags)> measurements,
|
|
params string[] instrumentNames)
|
|
{
|
|
var allowed = instrumentNames is { Length: > 0 } ? instrumentNames : new[] { "concelier.provenance.missing" };
|
|
var allowedSet = new HashSet<string>(allowed, StringComparer.OrdinalIgnoreCase);
|
|
|
|
var listener = new MeterListener
|
|
{
|
|
InstrumentPublished = (instrument, l) =>
|
|
{
|
|
if (instrument.Meter.Name == "StellaOps.Concelier.Models.Provenance" && allowedSet.Contains(instrument.Name))
|
|
{
|
|
l.EnableMeasurementEvents(instrument);
|
|
}
|
|
}
|
|
};
|
|
|
|
listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
|
|
{
|
|
var dict = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (var tag in tags)
|
|
{
|
|
dict[tag.Key] = tag.Value;
|
|
}
|
|
|
|
measurements.Add((instrument.Name, measurement, dict));
|
|
});
|
|
|
|
listener.Start();
|
|
return listener;
|
|
}
|
|
|
|
private static void ResetState()
|
|
{
|
|
var (_, _, syncRoot) = GetInternalState();
|
|
lock (syncRoot)
|
|
{
|
|
var (recorded, earliest, _) = GetInternalState();
|
|
recorded.Clear();
|
|
earliest.Clear();
|
|
}
|
|
}
|
|
|
|
private static (HashSet<string> Recorded, Dictionary<string, DateTimeOffset> Earliest, object SyncRoot) GetInternalState()
|
|
{
|
|
var type = typeof(ProvenanceDiagnostics);
|
|
var recordedField = type.GetField("RecordedComponents", BindingFlags.NonPublic | BindingFlags.Static) ?? throw new InvalidOperationException("RecordedComponents not found");
|
|
var earliestField = type.GetField("EarliestMissing", BindingFlags.NonPublic | BindingFlags.Static) ?? throw new InvalidOperationException("EarliestMissing not found");
|
|
var syncField = type.GetField("SyncRoot", BindingFlags.NonPublic | BindingFlags.Static) ?? throw new InvalidOperationException("SyncRoot not found");
|
|
|
|
var recorded = (HashSet<string>)recordedField.GetValue(null)!;
|
|
var earliest = (Dictionary<string, DateTimeOffset>)earliestField.GetValue(null)!;
|
|
var sync = syncField.GetValue(null)!;
|
|
return (recorded, earliest, sync);
|
|
}
|
|
}
|