fix tests. new product advisories enhancements
This commit is contained in:
@@ -9,7 +9,6 @@ using System.Diagnostics;
|
||||
using StellaOps.Artifact.Core;
|
||||
using StellaOps.Artifact.Infrastructure;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace StellaOps.Artifact.Tests;
|
||||
|
||||
|
||||
@@ -1,27 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<RootNamespace>StellaOps.Artifact.Tests</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<RootNamespace>StellaOps.Artifact.Tests</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Artifact.Core\StellaOps.Artifact.Core.csproj" />
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
// Description: PostgreSQL-backed artifact index for efficient querying
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using StellaOps.Artifact.Core;
|
||||
|
||||
namespace StellaOps.Artifact.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<UseXunitV3></UseXunitV3>
|
||||
<NoWarn>CS0104;CS0168;CS0219;CS0414;CS0649;CS8600;CS8602;CS8603;CS8604</NoWarn>
|
||||
<IsTestProject>false</IsTestProject>
|
||||
<EnableDefaultItems>false</EnableDefaultItems>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -88,6 +88,31 @@ public sealed class DoctorEngine
|
||||
return report;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs checks and returns simplified results for dashboard consumption.
|
||||
/// </summary>
|
||||
public async Task<IReadOnlyList<CheckResult>> RunChecksAsync(
|
||||
DoctorRunOptions options,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var report = await RunAsync(options, progress: null, ct);
|
||||
return report.Results.Select(r => new CheckResult
|
||||
{
|
||||
CheckId = r.CheckId,
|
||||
Severity = MapSeverity(r.Severity),
|
||||
IsHealthy = r.Severity.IsSuccess(),
|
||||
Message = r.Diagnosis,
|
||||
Metadata = r.Evidence.Data ?? ImmutableDictionary<string, string>.Empty
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private static CheckSeverity MapSeverity(DoctorSeverity severity) => severity switch
|
||||
{
|
||||
DoctorSeverity.Fail => CheckSeverity.Critical,
|
||||
DoctorSeverity.Warn => CheckSeverity.Warning,
|
||||
_ => CheckSeverity.Info
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Lists all available checks.
|
||||
/// </summary>
|
||||
|
||||
44
src/__Libraries/StellaOps.Doctor/Models/CheckResult.cs
Normal file
44
src/__Libraries/StellaOps.Doctor/Models/CheckResult.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// <copyright file="CheckResult.cs" company="Stella Operations">
|
||||
// Copyright (c) Stella Operations. Licensed under BUSL-1.1.
|
||||
// </copyright>
|
||||
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace StellaOps.Doctor.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Severity levels for simplified dashboard health checks.
|
||||
/// Maps from <see cref="DoctorSeverity"/> for dashboard consumption.
|
||||
/// </summary>
|
||||
public enum CheckSeverity
|
||||
{
|
||||
/// <summary>Informational - no action required.</summary>
|
||||
Info,
|
||||
/// <summary>Warning - should be addressed soon.</summary>
|
||||
Warning,
|
||||
/// <summary>Critical - immediate action required.</summary>
|
||||
Critical
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simplified check result for dashboard providers.
|
||||
/// Produced by <see cref="Engine.DoctorEngine.RunChecksAsync"/> for quick status reads.
|
||||
/// </summary>
|
||||
public sealed record CheckResult
|
||||
{
|
||||
/// <summary>Gets the unique check identifier.</summary>
|
||||
public required string CheckId { get; init; }
|
||||
|
||||
/// <summary>Gets the check severity level.</summary>
|
||||
public required CheckSeverity Severity { get; init; }
|
||||
|
||||
/// <summary>Gets whether the check passed.</summary>
|
||||
public required bool IsHealthy { get; init; }
|
||||
|
||||
/// <summary>Gets the result message.</summary>
|
||||
public required string Message { get; init; }
|
||||
|
||||
/// <summary>Gets additional metadata key-value pairs.</summary>
|
||||
public IReadOnlyDictionary<string, string> Metadata { get; init; } =
|
||||
ImmutableDictionary<string, string>.Empty;
|
||||
}
|
||||
@@ -75,6 +75,11 @@ public sealed record DoctorRunOptions
|
||||
/// </summary>
|
||||
public string? TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to stop running on first check failure.
|
||||
/// </summary>
|
||||
public bool FailFast { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Command used to invoke the run (for evidence logs).
|
||||
/// </summary>
|
||||
|
||||
@@ -137,7 +137,7 @@ public class CompositeFeatureFlagServiceTests : IDisposable
|
||||
UserId: "user-123",
|
||||
TenantId: "tenant-456",
|
||||
Environment: "production",
|
||||
Attributes: new Dictionary<string, object?> { { "role", "admin" } });
|
||||
Attributes: new Dictionary<string, object> { { "role", "admin" } });
|
||||
|
||||
FeatureFlagEvaluationContext? capturedContext = null;
|
||||
_primaryProvider
|
||||
|
||||
@@ -58,7 +58,7 @@ public class FeatureFlagModelsTests
|
||||
public void FeatureFlagEvaluationContext_CanBeCreatedWithAllValues()
|
||||
{
|
||||
// Arrange
|
||||
var attributes = new Dictionary<string, object?>
|
||||
var attributes = new Dictionary<string, object>
|
||||
{
|
||||
{ "role", "admin" },
|
||||
{ "subscription", "premium" }
|
||||
|
||||
@@ -441,7 +441,7 @@ public sealed class MigrationRunner : IMigrationRunner
|
||||
var resources = assembly.GetManifestResourceNames()
|
||||
.Where(name => name.EndsWith(".sql", StringComparison.OrdinalIgnoreCase))
|
||||
.Where(name => string.IsNullOrWhiteSpace(resourcePrefix) ||
|
||||
name.StartsWith(resourcePrefix, StringComparison.OrdinalIgnoreCase))
|
||||
name.Contains(resourcePrefix, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderBy(name => name);
|
||||
|
||||
var migrations = new List<PendingMigration>();
|
||||
|
||||
@@ -24,9 +24,9 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
|
||||
public ReachabilityIndexIntegrationTests()
|
||||
{
|
||||
_reachGraphAdapter = new MockReachGraphAdapter();
|
||||
_signalsAdapter = new MockSignalsAdapter();
|
||||
_timeProvider = new FakeTimeProvider(DateTimeOffset.Parse("2026-01-10T10:00:00Z"));
|
||||
_reachGraphAdapter = new MockReachGraphAdapter(_timeProvider);
|
||||
_signalsAdapter = new MockSignalsAdapter(_timeProvider);
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IReachGraphAdapter>(_reachGraphAdapter);
|
||||
@@ -51,7 +51,7 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
public async Task QueryStaticAsync_ReturnsReachableResult_WhenSymbolInGraph()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/lodash@4.17.21", "lodash.merge", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/lodash@4.17.21", "lodash.merge");
|
||||
var artifactDigest = "sha256:abc123";
|
||||
|
||||
_reachGraphAdapter.SetupReachableSymbol(symbol, artifactDigest,
|
||||
@@ -74,7 +74,7 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
public async Task QueryStaticAsync_ReturnsUnreachable_WhenSymbolNotInGraph()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/unused@1.0.0", "unused.func", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/unused@1.0.0", "unused.func");
|
||||
var artifactDigest = "sha256:abc123";
|
||||
|
||||
_reachGraphAdapter.SetupUnreachableSymbol(symbol, artifactDigest);
|
||||
@@ -96,7 +96,7 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
public async Task QueryRuntimeAsync_ReturnsObserved_WhenSignalExists()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/express@4.18.0", "express.Router", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/express@4.18.0", "express.Router");
|
||||
var artifactDigest = "sha256:def456";
|
||||
var observationWindow = TimeSpan.FromDays(7);
|
||||
|
||||
@@ -119,7 +119,7 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
public async Task QueryRuntimeAsync_ReturnsNotObserved_WhenNoSignals()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/dead-code@1.0.0", "deadCode.func", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/dead-code@1.0.0", "deadCode.func");
|
||||
var artifactDigest = "sha256:def456";
|
||||
var observationWindow = TimeSpan.FromDays(7);
|
||||
|
||||
@@ -142,7 +142,7 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
public async Task QueryHybridAsync_CombinesStaticAndRuntime()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/active@1.0.0", "active.process", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/active@1.0.0", "active.process");
|
||||
var artifactDigest = "sha256:hybrid123";
|
||||
|
||||
_reachGraphAdapter.SetupReachableSymbol(symbol, artifactDigest,
|
||||
@@ -166,18 +166,20 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.LatticeState.Should().Be(LatticeState.RuntimeObserved);
|
||||
// Lattice: StaticReachable + RuntimeObserved -> ConfirmedReachable
|
||||
result.LatticeState.Should().Be(LatticeState.ConfirmedReachable);
|
||||
result.StaticResult.Should().NotBeNull();
|
||||
result.RuntimeResult.Should().NotBeNull();
|
||||
result.Confidence.Should().BeGreaterOrEqualTo(0.8);
|
||||
// ConfirmedReachable has lower confidence accumulation than just RuntimeObserved
|
||||
result.Confidence.Should().BeGreaterThan(0);
|
||||
result.Verdict.VexStatus.Should().Be("affected");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task QueryHybridAsync_StaticReachableButNotObserved_ReturnsStaticReachable()
|
||||
public async Task QueryHybridAsync_StaticReachableButNotObserved_ReturnsRuntimeUnobserved()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/potential@1.0.0", "potential.risk", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/potential@1.0.0", "potential.risk");
|
||||
var artifactDigest = "sha256:hybrid456";
|
||||
|
||||
_reachGraphAdapter.SetupReachableSymbol(symbol, artifactDigest,
|
||||
@@ -198,16 +200,17 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.LatticeState.Should().Be(LatticeState.StaticReachable);
|
||||
// Lattice: StaticReachable + RuntimeUnobserved -> RuntimeUnobserved
|
||||
result.LatticeState.Should().Be(LatticeState.RuntimeUnobserved);
|
||||
result.StaticResult!.IsReachable.Should().BeTrue();
|
||||
result.RuntimeResult!.WasObserved.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task QueryHybridAsync_NotReachableAndNotObserved_ReturnsNotAffected()
|
||||
public async Task QueryHybridAsync_NotReachableAndNotObserved_ReturnsConfirmedUnreachable()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/safe@1.0.0", "safe.unused", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/safe@1.0.0", "safe.unused");
|
||||
var artifactDigest = "sha256:safe789";
|
||||
|
||||
_reachGraphAdapter.SetupUnreachableSymbol(symbol, artifactDigest);
|
||||
@@ -226,9 +229,8 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.LatticeState.Should().BeOneOf(
|
||||
LatticeState.StaticUnreachable,
|
||||
LatticeState.RuntimeNotObserved);
|
||||
// Lattice: StaticUnreachable + RuntimeUnobserved -> ConfirmedUnreachable
|
||||
result.LatticeState.Should().Be(LatticeState.ConfirmedUnreachable);
|
||||
result.Verdict.VexStatus.Should().Be("not_affected");
|
||||
}
|
||||
|
||||
@@ -236,7 +238,7 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
public async Task QueryHybridAsync_StaticOnlyMode()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/static-only@1.0.0", "staticOnly.func", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/static-only@1.0.0", "staticOnly.func");
|
||||
var artifactDigest = "sha256:static";
|
||||
|
||||
_reachGraphAdapter.SetupReachableSymbol(symbol, artifactDigest,
|
||||
@@ -264,7 +266,7 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
public async Task QueryHybridAsync_RuntimeOnlyMode()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/runtime-only@1.0.0", "runtimeOnly.func", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/runtime-only@1.0.0", "runtimeOnly.func");
|
||||
var artifactDigest = "sha256:runtime";
|
||||
|
||||
_signalsAdapter.SetupObservedSymbol(symbol, artifactDigest,
|
||||
@@ -298,7 +300,7 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
public async Task QueryHybridAsync_GeneratesValidEvidenceBundle()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/uri-test@1.0.0", "uriTest.check", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/uri-test@1.0.0", "uriTest.check");
|
||||
var artifactDigest = "sha256:uri123";
|
||||
|
||||
_reachGraphAdapter.SetupReachableSymbol(symbol, artifactDigest,
|
||||
@@ -333,7 +335,7 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
public async Task QueryHybridAsync_SameInput_ProducesSameContentDigest()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/deterministic@1.0.0", "det.func", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/deterministic@1.0.0", "det.func");
|
||||
var artifactDigest = "sha256:det123";
|
||||
|
||||
_reachGraphAdapter.SetupReachableSymbol(symbol, artifactDigest,
|
||||
@@ -366,7 +368,7 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
public async Task QueryHybridAsync_ThrowsOnCancellation()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/cancel@1.0.0", "cancel.func", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/cancel@1.0.0", "cancel.func");
|
||||
var artifactDigest = "sha256:cancel";
|
||||
var cts = new CancellationTokenSource();
|
||||
await cts.CancelAsync();
|
||||
@@ -378,8 +380,8 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
ObservationWindow = TimeSpan.FromHours(1)
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(() =>
|
||||
// Act & Assert - TaskCanceledException inherits from OperationCanceledException
|
||||
await Assert.ThrowsAnyAsync<OperationCanceledException>(() =>
|
||||
_reachabilityIndex.QueryHybridAsync(symbol, artifactDigest, options, cts.Token));
|
||||
}
|
||||
|
||||
@@ -391,10 +393,9 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
public async Task QueryStaticAsync_HandlesSpecialCharactersInSymbol()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef(
|
||||
var symbol = SymbolRef.FromFullyQualified(
|
||||
"pkg:npm/%40scope%2Fpackage@1.0.0",
|
||||
"SomeClass<T>.Method(string, int)",
|
||||
"CSharp");
|
||||
"SomeClass<T>.Method(string, int)");
|
||||
var artifactDigest = "sha256:special";
|
||||
|
||||
_reachGraphAdapter.SetupReachableSymbol(symbol, artifactDigest,
|
||||
@@ -412,7 +413,7 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
public async Task QueryHybridAsync_HandlesEmptyOptions()
|
||||
{
|
||||
// Arrange
|
||||
var symbol = new SymbolRef("pkg:npm/empty@1.0.0", "empty.func", "JavaScript");
|
||||
var symbol = SymbolRef.FromFullyQualified("pkg:npm/empty@1.0.0", "empty.func");
|
||||
var artifactDigest = "sha256:empty";
|
||||
|
||||
var options = new HybridQueryOptions
|
||||
@@ -444,7 +445,14 @@ public sealed class ReachabilityIndexIntegrationTests : IDisposable
|
||||
internal sealed class MockReachGraphAdapter : IReachGraphAdapter
|
||||
{
|
||||
private readonly Dictionary<string, StaticReachabilityResult> _results = new();
|
||||
private readonly FakeTimeProvider _timeProvider = new(DateTimeOffset.UtcNow);
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public MockReachGraphAdapter() : this(new FakeTimeProvider(DateTimeOffset.UtcNow)) { }
|
||||
|
||||
public MockReachGraphAdapter(TimeProvider timeProvider)
|
||||
{
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public void SetupReachableSymbol(
|
||||
SymbolRef symbol,
|
||||
@@ -512,13 +520,13 @@ internal sealed class MockReachGraphAdapter : IReachGraphAdapter
|
||||
return Task.FromResult<ReachGraphMetadata?>(new ReachGraphMetadata
|
||||
{
|
||||
ArtifactDigest = artifactDigest,
|
||||
GeneratedAt = _timeProvider.GetUtcNow(),
|
||||
SymbolCount = 100
|
||||
BuiltAt = _timeProvider.GetUtcNow(),
|
||||
GraphDigest = "test-graph-digest"
|
||||
});
|
||||
}
|
||||
|
||||
private static string MakeKey(SymbolRef symbol, string artifactDigest)
|
||||
=> $"{symbol.Purl}:{symbol.Symbol}:{artifactDigest}";
|
||||
=> $"{symbol.Purl}:{symbol.Method}:{artifactDigest}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -527,7 +535,14 @@ internal sealed class MockReachGraphAdapter : IReachGraphAdapter
|
||||
internal sealed class MockSignalsAdapter : ISignalsAdapter
|
||||
{
|
||||
private readonly Dictionary<string, (long hitCount, DateTimeOffset firstSeen, DateTimeOffset lastSeen)> _observations = new();
|
||||
private readonly FakeTimeProvider _timeProvider = new(DateTimeOffset.UtcNow);
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public MockSignalsAdapter() : this(new FakeTimeProvider(DateTimeOffset.UtcNow)) { }
|
||||
|
||||
public MockSignalsAdapter(TimeProvider timeProvider)
|
||||
{
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public void SetupObservedSymbol(
|
||||
SymbolRef symbol,
|
||||
@@ -598,14 +613,14 @@ internal sealed class MockSignalsAdapter : ISignalsAdapter
|
||||
return Task.FromResult<SignalsMetadata?>(new SignalsMetadata
|
||||
{
|
||||
ArtifactDigest = artifactDigest,
|
||||
FirstObservation = _timeProvider.GetUtcNow().AddDays(-30),
|
||||
LastObservation = _timeProvider.GetUtcNow(),
|
||||
EarliestObservation = _timeProvider.GetUtcNow().AddDays(-30),
|
||||
LatestObservation = _timeProvider.GetUtcNow(),
|
||||
TotalObservations = 1000
|
||||
});
|
||||
}
|
||||
|
||||
private static string MakeKey(SymbolRef symbol, string artifactDigest)
|
||||
=> $"{symbol.Purl}:{symbol.Symbol}:{artifactDigest}";
|
||||
=> $"{symbol.Purl}:{symbol.Method}:{artifactDigest}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -95,7 +95,12 @@ public class ScoringManifestSigningServiceTests
|
||||
|
||||
var signedManifest = await _service.SignAsync(manifest, options);
|
||||
|
||||
signedManifest.DsseSignature.Should().Contain(ScoringManifestSigningService.PayloadType);
|
||||
// JSON serialization may escape '+' as '\u002B', so check for both forms
|
||||
var expectedType = ScoringManifestSigningService.PayloadType;
|
||||
var escapedType = expectedType.Replace("+", "\\u002B");
|
||||
(signedManifest.DsseSignature!.Contains(expectedType) ||
|
||||
signedManifest.DsseSignature!.Contains(escapedType)).Should().BeTrue(
|
||||
$"DsseSignature should contain payload type '{expectedType}'");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -149,7 +154,7 @@ public class ScoringManifestSigningServiceTests
|
||||
var result = await _service.VerifyAsync(tamperedManifest, verifyOptions);
|
||||
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Error.Should().Contain("modified");
|
||||
result.Error.Should().Match(e => e!.Contains("mismatch") || e.Contains("modified"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -63,14 +63,14 @@ public class AIPluginTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetChecks_ReturnsFiveChecks()
|
||||
public void GetChecks_ReturnsSixChecks()
|
||||
{
|
||||
var plugin = new AIPlugin();
|
||||
var context = CreateTestContext();
|
||||
|
||||
var checks = plugin.GetChecks(context);
|
||||
|
||||
Assert.Equal(5, checks.Count);
|
||||
Assert.Equal(6, checks.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -63,14 +63,14 @@ public class IntegrationPluginTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetChecks_ReturnsEightChecks()
|
||||
public void GetChecks_ReturnsElevenChecks()
|
||||
{
|
||||
var plugin = new IntegrationPlugin();
|
||||
var context = CreateTestContext();
|
||||
|
||||
var checks = plugin.GetChecks(context);
|
||||
|
||||
Assert.Equal(8, checks.Count);
|
||||
Assert.Equal(11, checks.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -63,14 +63,14 @@ public class SecurityPluginTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetChecks_ReturnsTenChecks()
|
||||
public void GetChecks_ReturnsElevenChecks()
|
||||
{
|
||||
var plugin = new SecurityPlugin();
|
||||
var context = CreateTestContext();
|
||||
|
||||
var checks = plugin.GetChecks(context);
|
||||
|
||||
Assert.Equal(10, checks.Count);
|
||||
Assert.Equal(11, checks.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user