// ----------------------------------------------------------------------------- // 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 _verboseOption; public VexVerifyCommandTests() { var services = new ServiceCollection(); services.AddSingleton(NullLoggerFactory.Instance); _services = services.BuildServiceProvider(); _options = new StellaOpsCliOptions(); _verboseOption = new Option("--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().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()); } }