- Implement ProofChainTestFixture for PostgreSQL-backed integration tests. - Create StellaOps.Integration.ProofChain project with necessary dependencies. - Add ReachabilityIntegrationTests to validate call graph extraction and reachability analysis. - Introduce ReachabilityTestFixture for managing corpus and fixture paths. - Establish StellaOps.Integration.Reachability project with required references. - Develop UnknownsWorkflowTests to cover the unknowns lifecycle: detection, ranking, escalation, and resolution. - Create StellaOps.Integration.Unknowns project with dependencies for unknowns workflow.
411 lines
13 KiB
C#
411 lines
13 KiB
C#
// -----------------------------------------------------------------------------
|
|
// WitnessCommandGroupTests.cs
|
|
// Sprint: SPRINT_3700_0005_0001_witness_ui_cli
|
|
// Tasks: TEST-002
|
|
// Description: Unit tests for witness CLI commands
|
|
// -----------------------------------------------------------------------------
|
|
|
|
using System.CommandLine;
|
|
using System.CommandLine.Parsing;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
using Moq;
|
|
using Xunit;
|
|
using StellaOps.Cli.Commands;
|
|
|
|
namespace StellaOps.Cli.Tests.Commands;
|
|
|
|
/// <summary>
|
|
/// Unit tests for witness CLI commands.
|
|
/// </summary>
|
|
public class WitnessCommandGroupTests
|
|
{
|
|
private readonly IServiceProvider _services;
|
|
private readonly Option<bool> _verboseOption;
|
|
private readonly CancellationToken _cancellationToken;
|
|
|
|
public WitnessCommandGroupTests()
|
|
{
|
|
var serviceCollection = new ServiceCollection();
|
|
serviceCollection.AddLogging();
|
|
_services = serviceCollection.BuildServiceProvider();
|
|
_verboseOption = new Option<bool>("--verbose", "-v");
|
|
_cancellationToken = CancellationToken.None;
|
|
}
|
|
|
|
#region Command Structure Tests
|
|
|
|
[Fact]
|
|
public void BuildWitnessCommand_CreatesWitnessCommandTree()
|
|
{
|
|
// Act
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
|
|
// Assert
|
|
Assert.Equal("witness", command.Name);
|
|
Assert.Equal("Reachability witness operations.", command.Description);
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildWitnessCommand_HasShowSubcommand()
|
|
{
|
|
// Act
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var showCommand = command.Subcommands.FirstOrDefault(c => c.Name == "show");
|
|
|
|
// Assert
|
|
Assert.NotNull(showCommand);
|
|
Assert.Equal("Display a witness with call path visualization.", showCommand.Description);
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildWitnessCommand_HasVerifySubcommand()
|
|
{
|
|
// Act
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var verifyCommand = command.Subcommands.FirstOrDefault(c => c.Name == "verify");
|
|
|
|
// Assert
|
|
Assert.NotNull(verifyCommand);
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildWitnessCommand_HasListSubcommand()
|
|
{
|
|
// Act
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var listCommand = command.Subcommands.FirstOrDefault(c => c.Name == "list");
|
|
|
|
// Assert
|
|
Assert.NotNull(listCommand);
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildWitnessCommand_HasExportSubcommand()
|
|
{
|
|
// Act
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var exportCommand = command.Subcommands.FirstOrDefault(c => c.Name == "export");
|
|
|
|
// Assert
|
|
Assert.NotNull(exportCommand);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Show Command Tests
|
|
|
|
[Fact]
|
|
public void ShowCommand_HasWitnessIdArgument()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var showCommand = command.Subcommands.First(c => c.Name == "show");
|
|
|
|
// Act
|
|
var witnessIdArg = showCommand.Arguments.FirstOrDefault(a => a.Name == "witness-id");
|
|
|
|
// Assert
|
|
Assert.NotNull(witnessIdArg);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShowCommand_HasFormatOption()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var showCommand = command.Subcommands.First(c => c.Name == "show");
|
|
|
|
// Act
|
|
var formatOption = showCommand.Options.FirstOrDefault(o =>
|
|
o.Aliases.Contains("-f") || o.Aliases.Contains("--format"));
|
|
|
|
// Assert
|
|
Assert.NotNull(formatOption);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShowCommand_HasNoColorOption()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var showCommand = command.Subcommands.First(c => c.Name == "show");
|
|
|
|
// Act
|
|
var noColorOption = showCommand.Options.FirstOrDefault(o =>
|
|
o.Name == "--no-color" || o.Aliases.Contains("--no-color"));
|
|
|
|
// Assert
|
|
Assert.NotNull(noColorOption);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShowCommand_HasPathOnlyOption()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var showCommand = command.Subcommands.First(c => c.Name == "show");
|
|
|
|
// Act
|
|
var pathOnlyOption = showCommand.Options.FirstOrDefault(o =>
|
|
o.Name == "--path-only" || o.Aliases.Contains("--path-only"));
|
|
|
|
// Assert
|
|
Assert.NotNull(pathOnlyOption);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("text")]
|
|
[InlineData("json")]
|
|
[InlineData("yaml")]
|
|
public void ShowCommand_FormatOption_AcceptsValidFormats(string format)
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var showCommand = command.Subcommands.First(c => c.Name == "show");
|
|
|
|
// Act
|
|
var parseResult = showCommand.Parse($"wit:abc123 --format {format}");
|
|
|
|
// Assert
|
|
Assert.Empty(parseResult.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShowCommand_FormatOption_RejectsInvalidFormat()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var showCommand = command.Subcommands.First(c => c.Name == "show");
|
|
|
|
// Act
|
|
var parseResult = showCommand.Parse("wit:abc123 --format invalid");
|
|
|
|
// Assert
|
|
Assert.NotEmpty(parseResult.Errors);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Verify Command Tests
|
|
|
|
[Fact]
|
|
public void VerifyCommand_HasWitnessIdArgument()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var verifyCommand = command.Subcommands.First(c => c.Name == "verify");
|
|
|
|
// Act
|
|
var witnessIdArg = verifyCommand.Arguments.FirstOrDefault(a => a.Name == "witness-id");
|
|
|
|
// Assert
|
|
Assert.NotNull(witnessIdArg);
|
|
}
|
|
|
|
[Fact]
|
|
public void VerifyCommand_HasPublicKeyOption()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var verifyCommand = command.Subcommands.First(c => c.Name == "verify");
|
|
|
|
// Act
|
|
var publicKeyOption = verifyCommand.Options.FirstOrDefault(o =>
|
|
o.Aliases.Contains("-k") || o.Aliases.Contains("--public-key"));
|
|
|
|
// Assert
|
|
Assert.NotNull(publicKeyOption);
|
|
}
|
|
|
|
[Fact]
|
|
public void VerifyCommand_HasOfflineOption()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var verifyCommand = command.Subcommands.First(c => c.Name == "verify");
|
|
|
|
// Act
|
|
var offlineOption = verifyCommand.Options.FirstOrDefault(o =>
|
|
o.Name == "--offline" || o.Aliases.Contains("--offline"));
|
|
|
|
// Assert
|
|
Assert.NotNull(offlineOption);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region List Command Tests
|
|
|
|
[Fact]
|
|
public void ListCommand_HasScanOption()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var listCommand = command.Subcommands.First(c => c.Name == "list");
|
|
|
|
// Act
|
|
var scanOption = listCommand.Options.FirstOrDefault(o =>
|
|
o.Aliases.Contains("--scan") || o.Aliases.Contains("-s"));
|
|
|
|
// Assert
|
|
Assert.NotNull(scanOption);
|
|
}
|
|
|
|
[Fact]
|
|
public void ListCommand_HasVulnOption()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var listCommand = command.Subcommands.First(c => c.Name == "list");
|
|
|
|
// Act
|
|
var vulnOption = listCommand.Options.FirstOrDefault(o =>
|
|
o.Aliases.Contains("--vuln") || o.Aliases.Contains("-v"));
|
|
|
|
// Assert
|
|
Assert.NotNull(vulnOption);
|
|
}
|
|
|
|
[Fact]
|
|
public void ListCommand_HasReachableOnlyOption()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var listCommand = command.Subcommands.First(c => c.Name == "list");
|
|
|
|
// Act
|
|
var reachableOption = listCommand.Options.FirstOrDefault(o =>
|
|
o.Name == "--reachable-only" || o.Aliases.Contains("--reachable-only"));
|
|
|
|
// Assert
|
|
Assert.NotNull(reachableOption);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Export Command Tests
|
|
|
|
[Fact]
|
|
public void ExportCommand_HasWitnessIdArgument()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var exportCommand = command.Subcommands.First(c => c.Name == "export");
|
|
|
|
// Act
|
|
var witnessIdArg = exportCommand.Arguments.FirstOrDefault(a => a.Name == "witness-id");
|
|
|
|
// Assert
|
|
Assert.NotNull(witnessIdArg);
|
|
}
|
|
|
|
[Fact]
|
|
public void ExportCommand_HasFormatOption()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var exportCommand = command.Subcommands.First(c => c.Name == "export");
|
|
|
|
// Act
|
|
var formatOption = exportCommand.Options.FirstOrDefault(o =>
|
|
o.Aliases.Contains("-f") || o.Aliases.Contains("--format"));
|
|
|
|
// Assert
|
|
Assert.NotNull(formatOption);
|
|
}
|
|
|
|
[Fact]
|
|
public void ExportCommand_HasOutputOption()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var exportCommand = command.Subcommands.First(c => c.Name == "export");
|
|
|
|
// Act
|
|
var outputOption = exportCommand.Options.FirstOrDefault(o =>
|
|
o.Aliases.Contains("-o") || o.Aliases.Contains("--output"));
|
|
|
|
// Assert
|
|
Assert.NotNull(outputOption);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("json")]
|
|
[InlineData("sarif")]
|
|
public void ExportCommand_FormatOption_AcceptsValidFormats(string format)
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
var exportCommand = command.Subcommands.First(c => c.Name == "export");
|
|
|
|
// Act
|
|
var parseResult = exportCommand.Parse($"wit:abc123 --format {format}");
|
|
|
|
// Assert
|
|
Assert.Empty(parseResult.Errors);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Integration Tests
|
|
|
|
[Fact]
|
|
public void WitnessCommand_CanParseShowCommand()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
|
|
// Act
|
|
var parseResult = command.Parse("show wit:sha256:abc123 --format json");
|
|
|
|
// Assert
|
|
Assert.Equal("show", parseResult.CommandResult.Command.Name);
|
|
Assert.Empty(parseResult.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
public void WitnessCommand_CanParseVerifyCommand()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
|
|
// Act
|
|
var parseResult = command.Parse("verify wit:sha256:abc123 --offline");
|
|
|
|
// Assert
|
|
Assert.Equal("verify", parseResult.CommandResult.Command.Name);
|
|
Assert.Empty(parseResult.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
public void WitnessCommand_CanParseListCommand()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
|
|
// Act
|
|
var parseResult = command.Parse("list --scan scan-12345 --reachable-only");
|
|
|
|
// Assert
|
|
Assert.Equal("list", parseResult.CommandResult.Command.Name);
|
|
Assert.Empty(parseResult.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
public void WitnessCommand_CanParseExportCommand()
|
|
{
|
|
// Arrange
|
|
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
|
|
|
// Act
|
|
var parseResult = command.Parse("export wit:sha256:abc123 --format sarif --output report.sarif");
|
|
|
|
// Assert
|
|
Assert.Equal("export", parseResult.CommandResult.Command.Name);
|
|
Assert.Empty(parseResult.Errors);
|
|
}
|
|
|
|
#endregion
|
|
}
|