157 lines
5.1 KiB
C#
157 lines
5.1 KiB
C#
// -----------------------------------------------------------------------------
|
|
// VexVerifyCommandTests.cs
|
|
// Sprint: SPRINT_20260117_009_CLI_vex_processing (VPR-001)
|
|
// Task: VPR-001 - Add stella vex verify command
|
|
// Description: Unit tests for VEX verify command
|
|
// -----------------------------------------------------------------------------
|
|
|
|
using System.CommandLine;
|
|
using System.Text.Json;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using StellaOps.Cli.Configuration;
|
|
using StellaOps.Cli.Plugins.Vex;
|
|
using StellaOps.TestKit;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.Cli.Tests.Commands;
|
|
|
|
public sealed class VexVerifyCommandTests
|
|
{
|
|
private readonly IServiceProvider _services;
|
|
private readonly StellaOpsCliOptions _options;
|
|
private readonly Option<bool> _verboseOption;
|
|
|
|
public VexVerifyCommandTests()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddSingleton(NullLoggerFactory.Instance);
|
|
_services = services.BuildServiceProvider();
|
|
_options = new StellaOpsCliOptions();
|
|
_verboseOption = new Option<bool>("--verbose");
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public void VexCommand_RegistersVerifySubcommand()
|
|
{
|
|
// Arrange
|
|
var root = new RootCommand();
|
|
var module = new VexCliCommandModule();
|
|
|
|
// Act
|
|
module.RegisterCommands(root, _services, _options, _verboseOption, CancellationToken.None);
|
|
var vexCommand = root.Children.OfType<Command>().FirstOrDefault(c => c.Name == "vex");
|
|
var verifyCommand = vexCommand?.Subcommands.FirstOrDefault(c => c.Name == "verify");
|
|
|
|
// Assert
|
|
Assert.NotNull(vexCommand);
|
|
Assert.NotNull(verifyCommand);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task VexVerify_ValidOpenVex_ReturnsSuccessJson()
|
|
{
|
|
// Arrange
|
|
var root = new RootCommand();
|
|
var module = new VexCliCommandModule();
|
|
module.RegisterCommands(root, _services, _options, _verboseOption, CancellationToken.None);
|
|
|
|
var tempDir = Path.Combine(Path.GetTempPath(), "stellaops-vex-tests", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(tempDir);
|
|
var vexPath = Path.Combine(tempDir, "valid.openvex.json");
|
|
|
|
var vexJson = """
|
|
{
|
|
"@context": "https://openvex.dev/ns",
|
|
"@id": "https://stellaops.dev/vex/example-1",
|
|
"author": "stellaops",
|
|
"timestamp": "2026-01-16T00:00:00Z",
|
|
"statements": [
|
|
{
|
|
"vulnerability": { "name": "CVE-2025-0001" },
|
|
"status": "not_affected",
|
|
"products": ["pkg:oci/example@sha256:abc"]
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
|
|
await File.WriteAllTextAsync(vexPath, vexJson);
|
|
|
|
var writer = new StringWriter();
|
|
var originalOut = Console.Out;
|
|
int exitCode;
|
|
try
|
|
{
|
|
Console.SetOut(writer);
|
|
exitCode = await root.Parse($"vex verify \"{vexPath}\" --format json").InvokeAsync();
|
|
}
|
|
finally
|
|
{
|
|
Console.SetOut(originalOut);
|
|
}
|
|
|
|
// Assert
|
|
Assert.Equal(0, exitCode);
|
|
|
|
using var doc = JsonDocument.Parse(writer.ToString());
|
|
var rootElement = doc.RootElement;
|
|
Assert.True(rootElement.GetProperty("valid").GetBoolean());
|
|
Assert.Equal("OpenVEX", rootElement.GetProperty("detectedFormat").GetString());
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task VexVerify_StrictModeWithoutSignature_Fails()
|
|
{
|
|
// Arrange
|
|
var root = new RootCommand();
|
|
var module = new VexCliCommandModule();
|
|
module.RegisterCommands(root, _services, _options, _verboseOption, CancellationToken.None);
|
|
|
|
var tempDir = Path.Combine(Path.GetTempPath(), "stellaops-vex-tests", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(tempDir);
|
|
var vexPath = Path.Combine(tempDir, "valid.openvex.json");
|
|
|
|
var vexJson = """
|
|
{
|
|
"@context": "https://openvex.dev/ns",
|
|
"@id": "https://stellaops.dev/vex/example-2",
|
|
"author": "stellaops",
|
|
"timestamp": "2026-01-16T00:00:00Z",
|
|
"statements": [
|
|
{
|
|
"vulnerability": { "name": "CVE-2025-0002" },
|
|
"status": "not_affected",
|
|
"products": ["pkg:oci/example@sha256:def"]
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
|
|
await File.WriteAllTextAsync(vexPath, vexJson);
|
|
|
|
var writer = new StringWriter();
|
|
var originalOut = Console.Out;
|
|
int exitCode;
|
|
try
|
|
{
|
|
Console.SetOut(writer);
|
|
exitCode = await root.Parse($"vex verify \"{vexPath}\" --strict --format json").InvokeAsync();
|
|
}
|
|
finally
|
|
{
|
|
Console.SetOut(originalOut);
|
|
}
|
|
|
|
// Assert
|
|
Assert.Equal(1, exitCode);
|
|
|
|
using var doc = JsonDocument.Parse(writer.ToString());
|
|
var rootElement = doc.RootElement;
|
|
Assert.False(rootElement.GetProperty("valid").GetBoolean());
|
|
}
|
|
}
|