partly or unimplemented features - now implemented
This commit is contained in:
@@ -0,0 +1,281 @@
|
||||
// <copyright file="DeltaIfPresentIntegrationTests.cs" company="StellaOps">
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// Sprint: SPRINT_20260208_043_Policy_delta_if_present_calculations_for_missing_signals (TSF-004)
|
||||
// Task: T2 - Wire API/CLI/UI integration tests
|
||||
// </copyright>
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Policy.Determinization;
|
||||
using StellaOps.Policy.Determinization.Evidence;
|
||||
using StellaOps.Policy.Determinization.Models;
|
||||
using StellaOps.Policy.Determinization.Scoring;
|
||||
using StellaOps.Policy.Engine.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Tests.Integration;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for delta-if-present service DI wiring and functionality.
|
||||
/// </summary>
|
||||
[Trait("Category", "Integration")]
|
||||
[Trait("Sprint", "20260208.043")]
|
||||
[Trait("Task", "T2")]
|
||||
public sealed class DeltaIfPresentIntegrationTests
|
||||
{
|
||||
private readonly FakeTimeProvider _timeProvider = new();
|
||||
|
||||
private static ServiceCollection CreateServicesWithConfiguration()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection()
|
||||
.Build();
|
||||
services.AddSingleton<IConfiguration>(configuration);
|
||||
return services;
|
||||
}
|
||||
|
||||
#region DI Wiring Tests
|
||||
|
||||
[Fact(DisplayName = "AddDeterminization registers IDeltaIfPresentCalculator")]
|
||||
public void AddDeterminization_RegistersDeltaIfPresentCalculator()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateServicesWithConfiguration();
|
||||
|
||||
// Act
|
||||
services.AddLogging();
|
||||
services.AddSingleton<TimeProvider>(_timeProvider);
|
||||
services.AddDeterminization();
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
// Assert
|
||||
var calculator = provider.GetService<IDeltaIfPresentCalculator>();
|
||||
calculator.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "DeltaIfPresentCalculator is registered as singleton")]
|
||||
public void DeltaIfPresentCalculator_IsRegisteredAsSingleton()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateServicesWithConfiguration();
|
||||
services.AddLogging();
|
||||
services.AddSingleton<TimeProvider>(_timeProvider);
|
||||
services.AddDeterminization();
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
// Act
|
||||
var first = provider.GetService<IDeltaIfPresentCalculator>();
|
||||
var second = provider.GetService<IDeltaIfPresentCalculator>();
|
||||
|
||||
// Assert
|
||||
first.Should().BeSameAs(second);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "AddDeterminizationEngine also registers delta-if-present")]
|
||||
public void AddDeterminizationEngine_IncludesDeltaIfPresentCalculator()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateServicesWithConfiguration();
|
||||
services.AddLogging();
|
||||
services.AddSingleton<TimeProvider>(_timeProvider);
|
||||
services.AddDeterminizationEngine();
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
// Assert
|
||||
var calculator = provider.GetService<IDeltaIfPresentCalculator>();
|
||||
calculator.Should().NotBeNull();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region End-to-End Service Tests
|
||||
|
||||
[Fact(DisplayName = "CalculateSingleSignalDelta works through DI container")]
|
||||
public void CalculateSingleSignalDelta_WorksThroughDI()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateServicesWithConfiguration();
|
||||
services.AddLogging();
|
||||
services.AddSingleton<TimeProvider>(_timeProvider);
|
||||
services.AddDeterminization();
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
var calculator = provider.GetRequiredService<IDeltaIfPresentCalculator>();
|
||||
var snapshot = CreatePartialSnapshot();
|
||||
|
||||
// Act
|
||||
var result = calculator.CalculateSingleSignalDelta(snapshot, "VEX", 0.0);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.Signal.Should().Be("VEX");
|
||||
result.HypotheticalEntropy.Should().BeLessThan(result.CurrentEntropy);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "CalculateFullAnalysis returns prioritized gaps")]
|
||||
public void CalculateFullAnalysis_ReturnsPrioritizedGaps()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateServicesWithConfiguration();
|
||||
services.AddLogging();
|
||||
services.AddSingleton<TimeProvider>(_timeProvider);
|
||||
services.AddDeterminization();
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
var calculator = provider.GetRequiredService<IDeltaIfPresentCalculator>();
|
||||
var snapshot = CreatePartialSnapshot();
|
||||
|
||||
// Act
|
||||
var analysis = calculator.CalculateFullAnalysis(snapshot);
|
||||
|
||||
// Assert
|
||||
analysis.Should().NotBeNull();
|
||||
analysis.GapAnalysis.Should().HaveCountGreaterThan(0);
|
||||
analysis.PrioritizedGaps.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "CalculateScoreBounds returns valid range")]
|
||||
public void CalculateScoreBounds_ReturnsValidRange()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateServicesWithConfiguration();
|
||||
services.AddLogging();
|
||||
services.AddSingleton<TimeProvider>(_timeProvider);
|
||||
services.AddDeterminization();
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
var calculator = provider.GetRequiredService<IDeltaIfPresentCalculator>();
|
||||
var snapshot = CreatePartialSnapshot();
|
||||
|
||||
// Act
|
||||
var bounds = calculator.CalculateScoreBounds(snapshot);
|
||||
|
||||
// Assert
|
||||
bounds.Should().NotBeNull();
|
||||
bounds.MinimumScore.Should().BeLessThanOrEqualTo(bounds.MaximumScore);
|
||||
bounds.Range.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Calculator produces deterministic results through DI")]
|
||||
public void Calculator_ProducesDeterministicResults()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateServicesWithConfiguration();
|
||||
services.AddLogging();
|
||||
services.AddSingleton<TimeProvider>(_timeProvider);
|
||||
services.AddDeterminization();
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
var calculator = provider.GetRequiredService<IDeltaIfPresentCalculator>();
|
||||
var snapshot = CreatePartialSnapshot();
|
||||
|
||||
// Act
|
||||
var result1 = calculator.CalculateSingleSignalDelta(snapshot, "EPSS", 0.5);
|
||||
var result2 = calculator.CalculateSingleSignalDelta(snapshot, "EPSS", 0.5);
|
||||
|
||||
// Assert - Results should be identical
|
||||
result1.CurrentScore.Should().Be(result2.CurrentScore);
|
||||
result1.HypotheticalScore.Should().Be(result2.HypotheticalScore);
|
||||
result1.CurrentEntropy.Should().Be(result2.CurrentEntropy);
|
||||
result1.HypotheticalEntropy.Should().Be(result2.HypotheticalEntropy);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "All signals can be analyzed without exceptions")]
|
||||
public void AllSignals_CanBeAnalyzed()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateServicesWithConfiguration();
|
||||
services.AddLogging();
|
||||
services.AddSingleton<TimeProvider>(_timeProvider);
|
||||
services.AddDeterminization();
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
var calculator = provider.GetRequiredService<IDeltaIfPresentCalculator>();
|
||||
var snapshot = CreateEmptySnapshot();
|
||||
var signals = new[] { "VEX", "EPSS", "Reachability", "Runtime", "Backport", "SBOMLineage" };
|
||||
|
||||
// Act & Assert - All signals should be analyzable
|
||||
foreach (var signal in signals)
|
||||
{
|
||||
var result = calculator.CalculateSingleSignalDelta(snapshot, signal, 0.5);
|
||||
result.Signal.Should().Be(signal);
|
||||
result.SignalWeight.Should().BeGreaterThan(0);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Integration with Dependencies
|
||||
|
||||
[Fact(DisplayName = "Calculator uses injected UncertaintyScoreCalculator")]
|
||||
public void Calculator_UsesInjectedDependencies()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateServicesWithConfiguration();
|
||||
services.AddLogging();
|
||||
services.AddSingleton<TimeProvider>(_timeProvider);
|
||||
services.AddDeterminization();
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
// Act - Get both services
|
||||
var calculator = provider.GetRequiredService<IDeltaIfPresentCalculator>();
|
||||
var uncertaintyCalc = provider.GetRequiredService<IUncertaintyScoreCalculator>();
|
||||
|
||||
// Assert - Both should be available
|
||||
calculator.Should().NotBeNull();
|
||||
uncertaintyCalc.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Calculator uses injected TrustScoreAggregator")]
|
||||
public void Calculator_UsesInjectedTrustAggregator()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateServicesWithConfiguration();
|
||||
services.AddLogging();
|
||||
services.AddSingleton<TimeProvider>(_timeProvider);
|
||||
services.AddDeterminization();
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
// Act - Get both services
|
||||
var calculator = provider.GetRequiredService<IDeltaIfPresentCalculator>();
|
||||
var aggregator = provider.GetRequiredService<TrustScoreAggregator>();
|
||||
|
||||
// Assert - Both should be available
|
||||
calculator.Should().NotBeNull();
|
||||
aggregator.Should().NotBeNull();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
private SignalSnapshot CreateEmptySnapshot()
|
||||
{
|
||||
return SignalSnapshot.Empty("CVE-2024-1234", "pkg:maven/test@1.0", _timeProvider.GetUtcNow());
|
||||
}
|
||||
|
||||
private SignalSnapshot CreatePartialSnapshot()
|
||||
{
|
||||
var now = _timeProvider.GetUtcNow();
|
||||
return new SignalSnapshot
|
||||
{
|
||||
Cve = "CVE-2024-1234",
|
||||
Purl = "pkg:maven/test@1.0",
|
||||
Vex = SignalState<VexClaimSummary>.NotQueried(),
|
||||
Epss = SignalState<EpssEvidence>.NotQueried(),
|
||||
Reachability = SignalState<ReachabilityEvidence>.Queried(
|
||||
new ReachabilityEvidence { Status = ReachabilityStatus.Reachable, AnalyzedAt = now }, now),
|
||||
Runtime = SignalState<RuntimeEvidence>.NotQueried(),
|
||||
Backport = SignalState<BackportEvidence>.NotQueried(),
|
||||
Sbom = SignalState<SbomLineageEvidence>.Queried(
|
||||
new SbomLineageEvidence { SbomDigest = "sha256:abc", Format = "SPDX", ComponentCount = 150, GeneratedAt = now, HasProvenance = true }, now),
|
||||
Cvss = SignalState<CvssEvidence>.NotQueried(),
|
||||
SnapshotAt = now
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user