save work

This commit is contained in:
StellaOps Bot
2025-12-19 09:40:41 +02:00
parent 2eafe98d44
commit 43882078a4
44 changed files with 3044 additions and 492 deletions

View File

@@ -232,6 +232,161 @@ public sealed class CommandHandlersTests
}
}
[Fact]
public async Task HandleGraphExplainAsync_SetsExitCode4WhenNoFilters()
{
var originalExit = Environment.ExitCode;
var backend = new StubBackendClient(new JobTriggerResult(true, "ok", null, null));
var provider = BuildServiceProvider(backend);
try
{
var output = await CaptureTestConsoleAsync(console => CommandHandlers.HandleGraphExplainAsync(
provider,
tenant: null,
graphId: "graph-123",
vulnerabilityId: null,
packagePurl: null,
includeCallPaths: false,
includeRuntimeHits: false,
includePredicates: false,
includeDsse: false,
includeCounterfactuals: false,
emitJson: false,
verbose: false,
cancellationToken: CancellationToken.None));
Assert.Equal(4, Environment.ExitCode);
Assert.Contains("--vuln-id", output.Combined, StringComparison.OrdinalIgnoreCase);
Assert.Null(backend.LastGraphExplainRequest);
}
finally
{
Environment.ExitCode = originalExit;
}
}
[Fact]
public async Task HandleGraphExplainAsync_CallsBackendAndRendersJson()
{
var originalExit = Environment.ExitCode;
var backend = new StubBackendClient(new JobTriggerResult(true, "ok", null, null))
{
GraphExplainResponse = new GraphExplainResult
{
GraphId = "graph-123",
GraphHash = "blake3:abc123",
VulnerabilityId = "CVE-2025-0001",
ReachabilityState = "reachable",
Confidence = "high"
}
};
var provider = BuildServiceProvider(backend);
try
{
var output = await CaptureTestConsoleAsync(console => CommandHandlers.HandleGraphExplainAsync(
provider,
tenant: "t-1",
graphId: "graph-123",
vulnerabilityId: "CVE-2025-0001",
packagePurl: null,
includeCallPaths: true,
includeRuntimeHits: false,
includePredicates: false,
includeDsse: true,
includeCounterfactuals: false,
emitJson: true,
verbose: false,
cancellationToken: CancellationToken.None));
Assert.Equal(0, Environment.ExitCode);
Assert.NotNull(backend.LastGraphExplainRequest);
Assert.Equal("graph-123", backend.LastGraphExplainRequest!.GraphId);
Assert.Equal("CVE-2025-0001", backend.LastGraphExplainRequest!.VulnerabilityId);
Assert.True(backend.LastGraphExplainRequest!.IncludeCallPaths);
Assert.True(backend.LastGraphExplainRequest!.IncludeDsseEnvelopes);
Assert.Equal("t-1", backend.LastGraphExplainRequest!.Tenant);
using var document = JsonDocument.Parse(output.SpectreBuffer.Trim());
var root = document.RootElement;
Assert.Equal("graph-123", root.GetProperty("graphId").GetString());
Assert.Equal("blake3:abc123", root.GetProperty("graphHash").GetString());
Assert.Equal("reachable", root.GetProperty("reachabilityState").GetString());
}
finally
{
Environment.ExitCode = originalExit;
}
}
[Fact]
public async Task HandleGraphVerifyAsync_EmitsJsonWithExpectedFields()
{
var originalExit = Environment.ExitCode;
var provider = BuildServiceProvider(new StubBackendClient(new JobTriggerResult(true, "ok", null, null)));
try
{
var output = await CaptureTestConsoleAsync(console => CommandHandlers.HandleGraphVerifyAsync(
provider,
tenant: null,
hash: "blake3:deadbeef",
includeBundles: true,
specificBundle: null,
verifyRekor: true,
casRoot: "C:\\offline-cas",
format: "json",
verbose: false,
cancellationToken: CancellationToken.None));
Assert.Equal(0, Environment.ExitCode);
using var document = JsonDocument.Parse(output.SpectreBuffer.Trim());
var root = document.RootElement;
Assert.Equal("blake3:deadbeef", root.GetProperty("hash").GetString());
Assert.Equal("VERIFIED", root.GetProperty("status").GetString());
Assert.True(root.GetProperty("offlineMode").GetBoolean());
Assert.True(root.GetProperty("rekorIncluded").GetBoolean());
Assert.Equal(2, root.GetProperty("bundlesVerified").GetInt32());
}
finally
{
Environment.ExitCode = originalExit;
}
}
[Fact]
public async Task HandleGraphBundlesAsync_EmitsJsonWithBundles()
{
var originalExit = Environment.ExitCode;
var provider = BuildServiceProvider(new StubBackendClient(new JobTriggerResult(true, "ok", null, null)));
try
{
var output = await CaptureTestConsoleAsync(console => CommandHandlers.HandleGraphBundlesAsync(
provider,
tenant: null,
graphHash: "blake3:deadbeef",
emitJson: true,
verbose: false,
cancellationToken: CancellationToken.None));
Assert.Equal(0, Environment.ExitCode);
using var document = JsonDocument.Parse(output.SpectreBuffer.Trim());
var root = document.RootElement;
Assert.Equal("blake3:deadbeef", root.GetProperty("graphHash").GetString());
Assert.Equal(2, root.GetProperty("bundles").GetArrayLength());
Assert.Contains(root.GetProperty("bundles").EnumerateArray(), bundle =>
string.Equals(bundle.GetProperty("bundleId").GetString(), "bundle:001", StringComparison.OrdinalIgnoreCase));
}
finally
{
Environment.ExitCode = originalExit;
}
}
[Fact]
public async Task HandleNodeLockValidateAsync_RendersDeclaredOnlyAndMissingLock()
{
@@ -4669,8 +4824,15 @@ spec:
public Task<ReachabilityExplainResult> ExplainReachabilityAsync(ReachabilityExplainRequest request, CancellationToken cancellationToken)
=> Task.FromResult(new ReachabilityExplainResult());
public GraphExplainRequest? LastGraphExplainRequest { get; private set; }
public GraphExplainResult GraphExplainResponse { get; set; } = new GraphExplainResult();
public Task<GraphExplainResult> ExplainGraphAsync(GraphExplainRequest request, CancellationToken cancellationToken)
=> Task.FromResult(new GraphExplainResult());
{
LastGraphExplainRequest = request;
return Task.FromResult(GraphExplainResponse);
}
public Task<ApiSpecListResponse> ListApiSpecsAsync(string? tenant, CancellationToken cancellationToken)
=> Task.FromResult(new ApiSpecListResponse());