doctor enhancements, setup, enhancements, ui functionality and design consolidation and , test projects fixes , product advisory attestation/rekor and delta verfications enhancements
This commit is contained in:
@@ -0,0 +1,504 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// ScoreGateCommandTests.cs
|
||||
// Sprint: SPRINT_20260118_030_LIB_verdict_rekor_gate_api
|
||||
// Task: TASK-030-008 - CLI Gate Command
|
||||
// Description: Unit tests for score-based gate CLI commands
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.CommandLine;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Cli.Commands;
|
||||
using StellaOps.Cli.Configuration;
|
||||
using StellaOps.TestKit;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Cli.Tests.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for score-based gate CLI commands.
|
||||
/// </summary>
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
public class ScoreGateCommandTests
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly StellaOpsCliOptions _options;
|
||||
private readonly Option<bool> _verboseOption;
|
||||
|
||||
public ScoreGateCommandTests()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
|
||||
_services = serviceCollection.BuildServiceProvider();
|
||||
|
||||
_options = new StellaOpsCliOptions
|
||||
{
|
||||
PolicyGateway = new StellaOpsCliPolicyGatewayOptions
|
||||
{
|
||||
BaseUrl = "http://localhost:5080"
|
||||
}
|
||||
};
|
||||
|
||||
_verboseOption = new Option<bool>("--verbose", "-v") { Description = "Enable verbose output" };
|
||||
}
|
||||
|
||||
#region Score Command Structure Tests
|
||||
|
||||
[Fact]
|
||||
public void BuildScoreCommand_CreatesScoreCommandTree()
|
||||
{
|
||||
// Act
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("score", command.Name);
|
||||
Assert.Contains("Score-based", command.Description);
|
||||
Assert.Contains("EWS", command.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildScoreCommand_HasEvaluateSubcommand()
|
||||
{
|
||||
// Act
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var evaluateCommand = command.Subcommands.FirstOrDefault(c => c.Name == "evaluate");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(evaluateCommand);
|
||||
Assert.Contains("single finding", evaluateCommand.Description, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildScoreCommand_HasBatchSubcommand()
|
||||
{
|
||||
// Act
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var batchCommand = command.Subcommands.FirstOrDefault(c => c.Name == "batch");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(batchCommand);
|
||||
Assert.Contains("multiple findings", batchCommand.Description, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Evaluate Command Tests
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCommand_HasFindingIdOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var evaluateCommand = command.Subcommands.First(c => c.Name == "evaluate");
|
||||
|
||||
// Act
|
||||
var findingIdOption = evaluateCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--finding-id") || o.Aliases.Contains("-f"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(findingIdOption);
|
||||
Assert.Equal(1, findingIdOption.Arity.MinimumNumberOfValues); // Required
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCommand_HasCvssOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var evaluateCommand = command.Subcommands.First(c => c.Name == "evaluate");
|
||||
|
||||
// Act
|
||||
var cvssOption = evaluateCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--cvss"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(cvssOption);
|
||||
Assert.Contains("0-10", cvssOption.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCommand_HasEpssOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var evaluateCommand = command.Subcommands.First(c => c.Name == "evaluate");
|
||||
|
||||
// Act
|
||||
var epssOption = evaluateCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--epss"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(epssOption);
|
||||
Assert.Contains("0-1", epssOption.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCommand_HasReachabilityOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var evaluateCommand = command.Subcommands.First(c => c.Name == "evaluate");
|
||||
|
||||
// Act
|
||||
var reachabilityOption = evaluateCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--reachability") || o.Aliases.Contains("-r"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(reachabilityOption);
|
||||
Assert.Contains("none", reachabilityOption.Description);
|
||||
Assert.Contains("package", reachabilityOption.Description);
|
||||
Assert.Contains("function", reachabilityOption.Description);
|
||||
Assert.Contains("caller", reachabilityOption.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCommand_HasExploitMaturityOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var evaluateCommand = command.Subcommands.First(c => c.Name == "evaluate");
|
||||
|
||||
// Act
|
||||
var exploitOption = evaluateCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--exploit-maturity") || o.Aliases.Contains("-e"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(exploitOption);
|
||||
Assert.Contains("poc", exploitOption.Description);
|
||||
Assert.Contains("functional", exploitOption.Description);
|
||||
Assert.Contains("high", exploitOption.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCommand_HasPatchProofOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var evaluateCommand = command.Subcommands.First(c => c.Name == "evaluate");
|
||||
|
||||
// Act
|
||||
var patchProofOption = evaluateCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--patch-proof"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(patchProofOption);
|
||||
Assert.Contains("0-1", patchProofOption.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCommand_HasVexStatusOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var evaluateCommand = command.Subcommands.First(c => c.Name == "evaluate");
|
||||
|
||||
// Act
|
||||
var vexStatusOption = evaluateCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--vex-status"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(vexStatusOption);
|
||||
Assert.Contains("affected", vexStatusOption.Description);
|
||||
Assert.Contains("not_affected", vexStatusOption.Description);
|
||||
Assert.Contains("fixed", vexStatusOption.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCommand_HasPolicyProfileOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var evaluateCommand = command.Subcommands.First(c => c.Name == "evaluate");
|
||||
|
||||
// Act
|
||||
var policyOption = evaluateCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--policy") || o.Aliases.Contains("-p"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(policyOption);
|
||||
Assert.Contains("advisory", policyOption.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCommand_HasAnchorOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var evaluateCommand = command.Subcommands.First(c => c.Name == "evaluate");
|
||||
|
||||
// Act
|
||||
var anchorOption = evaluateCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--anchor"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(anchorOption);
|
||||
Assert.Contains("Rekor", anchorOption.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCommand_HasOutputOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var evaluateCommand = command.Subcommands.First(c => c.Name == "evaluate");
|
||||
|
||||
// Act
|
||||
var outputOption = evaluateCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--output") || o.Aliases.Contains("-o"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(outputOption);
|
||||
Assert.Contains("table", outputOption.Description, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("json", outputOption.Description, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("ci", outputOption.Description, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCommand_HasBreakdownOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var evaluateCommand = command.Subcommands.First(c => c.Name == "evaluate");
|
||||
|
||||
// Act
|
||||
var breakdownOption = evaluateCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--breakdown"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(breakdownOption);
|
||||
Assert.Contains("breakdown", breakdownOption.Description, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Batch Command Tests
|
||||
|
||||
[Fact]
|
||||
public void BatchCommand_HasInputOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var batchCommand = command.Subcommands.First(c => c.Name == "batch");
|
||||
|
||||
// Act
|
||||
var inputOption = batchCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--input") || o.Aliases.Contains("-i"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(inputOption);
|
||||
Assert.Contains("JSON", inputOption.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BatchCommand_HasSarifOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var batchCommand = command.Subcommands.First(c => c.Name == "batch");
|
||||
|
||||
// Act
|
||||
var sarifOption = batchCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--sarif"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(sarifOption);
|
||||
Assert.Contains("SARIF", sarifOption.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BatchCommand_HasFailFastOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var batchCommand = command.Subcommands.First(c => c.Name == "batch");
|
||||
|
||||
// Act
|
||||
var failFastOption = batchCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--fail-fast"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(failFastOption);
|
||||
Assert.Contains("Stop", failFastOption.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BatchCommand_HasParallelismOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var batchCommand = command.Subcommands.First(c => c.Name == "batch");
|
||||
|
||||
// Act
|
||||
var parallelismOption = batchCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--parallelism"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(parallelismOption);
|
||||
Assert.Contains("parallelism", parallelismOption.Description, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BatchCommand_HasIncludeVerdictsOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var batchCommand = command.Subcommands.First(c => c.Name == "batch");
|
||||
|
||||
// Act
|
||||
var includeVerdictsOption = batchCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--include-verdicts"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(includeVerdictsOption);
|
||||
Assert.Contains("verdict", includeVerdictsOption.Description, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BatchCommand_HasOutputOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
var batchCommand = command.Subcommands.First(c => c.Name == "batch");
|
||||
|
||||
// Act
|
||||
var outputOption = batchCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--output") || o.Aliases.Contains("-o"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(outputOption);
|
||||
Assert.Contains("table", outputOption.Description, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("json", outputOption.Description, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("ci", outputOption.Description, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Integration with Gate Command Tests
|
||||
|
||||
[Fact]
|
||||
public void ScoreCommand_ShouldBeAddableToGateCommand()
|
||||
{
|
||||
// Arrange
|
||||
var gateCommand = new Command("gate", "CI/CD release gate operations");
|
||||
var scoreCommand = ScoreGateCommandGroup.BuildScoreCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
|
||||
// Act
|
||||
gateCommand.Add(scoreCommand);
|
||||
|
||||
// Assert
|
||||
Assert.Contains(gateCommand.Subcommands, c => c.Name == "score");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GateCommand_IncludesScoreSubcommand()
|
||||
{
|
||||
// Act
|
||||
var gateCommand = GateCommandGroup.BuildGateCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
Assert.Contains(gateCommand.Subcommands, c => c.Name == "score");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GateScoreEvaluate_FullCommandPath()
|
||||
{
|
||||
// Arrange
|
||||
var gateCommand = GateCommandGroup.BuildGateCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
|
||||
// Act
|
||||
var scoreCommand = gateCommand.Subcommands.First(c => c.Name == "score");
|
||||
var evaluateCommand = scoreCommand.Subcommands.First(c => c.Name == "evaluate");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(evaluateCommand);
|
||||
Assert.Equal("evaluate", evaluateCommand.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GateScoreBatch_FullCommandPath()
|
||||
{
|
||||
// Arrange
|
||||
var gateCommand = GateCommandGroup.BuildGateCommand(
|
||||
_services, _options, _verboseOption, CancellationToken.None);
|
||||
|
||||
// Act
|
||||
var scoreCommand = gateCommand.Subcommands.First(c => c.Name == "score");
|
||||
var batchCommand = scoreCommand.Subcommands.First(c => c.Name == "batch");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(batchCommand);
|
||||
Assert.Equal("batch", batchCommand.Name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Exit Codes Tests
|
||||
|
||||
[Fact]
|
||||
public void ScoreGateExitCodes_PassIsZero()
|
||||
{
|
||||
Assert.Equal(0, ScoreGateExitCodes.Pass);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScoreGateExitCodes_WarnIsOne()
|
||||
{
|
||||
Assert.Equal(1, ScoreGateExitCodes.Warn);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScoreGateExitCodes_BlockIsTwo()
|
||||
{
|
||||
Assert.Equal(2, ScoreGateExitCodes.Block);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScoreGateExitCodes_InputErrorIsTen()
|
||||
{
|
||||
Assert.Equal(10, ScoreGateExitCodes.InputError);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScoreGateExitCodes_NetworkErrorIsEleven()
|
||||
{
|
||||
Assert.Equal(11, ScoreGateExitCodes.NetworkError);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScoreGateExitCodes_PolicyErrorIsTwelve()
|
||||
{
|
||||
Assert.Equal(12, ScoreGateExitCodes.PolicyError);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScoreGateExitCodes_UnknownErrorIsNinetyNine()
|
||||
{
|
||||
Assert.Equal(99, ScoreGateExitCodes.UnknownError);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user