tests fixes and sprints work

This commit is contained in:
master
2026-01-22 19:08:46 +02:00
parent c32fff8f86
commit 726d70dc7f
881 changed files with 134434 additions and 6228 deletions

View File

@@ -0,0 +1,285 @@
// -----------------------------------------------------------------------------
// AnalyticsCommandTests.cs
// Sprint: SPRINT_20260120_032_Cli_sbom_analytics_cli
// Description: Unit tests for analytics sbom-lake CLI commands.
// -----------------------------------------------------------------------------
using System;
using System.CommandLine;
using System.Globalization;
using System.IO;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using StellaOps.Cli.Commands;
using StellaOps.Cli.Services;
using StellaOps.Cli.Services.Models;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Cli.Tests.Commands;
[Trait("Category", TestCategories.Unit)]
public sealed class AnalyticsCommandTests
{
[Fact]
public async Task SuppliersJsonOutput_IncludesItems()
{
var client = new Mock<IBackendOperationsClient>();
client
.Setup(c => c.GetAnalyticsSuppliersAsync(
It.IsAny<int?>(),
It.IsAny<string?>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(BuildSuppliersResponse());
var services = new ServiceCollection()
.AddSingleton(client.Object)
.BuildServiceProvider();
var root = BuildRoot(services);
var writer = new StringWriter(CultureInfo.InvariantCulture);
var originalOut = Console.Out;
int exitCode;
try
{
Console.SetOut(writer);
exitCode = await root.Parse("analytics sbom-lake suppliers --format json").InvokeAsync();
}
finally
{
Console.SetOut(originalOut);
}
Assert.Equal(0, exitCode);
using var doc = JsonDocument.Parse(writer.ToString());
var items = doc.RootElement.GetProperty("items");
Assert.Equal(2, items.GetArrayLength());
Assert.Equal("Acme Co", items[0].GetProperty("supplier").GetString());
Assert.Equal(2, doc.RootElement.GetProperty("count").GetInt32());
}
[Fact]
public async Task SuppliersCsvOutput_MatchesFixture()
{
var client = new Mock<IBackendOperationsClient>();
client
.Setup(c => c.GetAnalyticsSuppliersAsync(
It.IsAny<int?>(),
It.IsAny<string?>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(BuildSuppliersResponse());
var services = new ServiceCollection()
.AddSingleton(client.Object)
.BuildServiceProvider();
var root = BuildRoot(services);
var writer = new StringWriter(CultureInfo.InvariantCulture);
var originalOut = Console.Out;
int exitCode;
try
{
Console.SetOut(writer);
exitCode = await root.Parse("analytics sbom-lake suppliers --environment prod --format csv").InvokeAsync();
}
finally
{
Console.SetOut(originalOut);
}
Assert.Equal(0, exitCode);
var expected = await File.ReadAllTextAsync(ResolveFixturePath("suppliers.csv"), CancellationToken.None);
Assert.Equal(expected.TrimEnd(), writer.ToString().TrimEnd());
}
[Fact]
public async Task Suppliers_InvalidLimit_ReturnsError()
{
var services = new ServiceCollection().BuildServiceProvider();
var root = BuildRoot(services);
var writer = new StringWriter(CultureInfo.InvariantCulture);
var originalOut = Console.Out;
int exitCode;
try
{
Console.SetOut(writer);
exitCode = await root.Parse("analytics sbom-lake suppliers --limit 0 --format json").InvokeAsync();
}
finally
{
Console.SetOut(originalOut);
}
Assert.Equal(1, exitCode);
using var doc = JsonDocument.Parse(writer.ToString());
Assert.Equal("error", doc.RootElement.GetProperty("status").GetString());
}
[Fact]
public async Task TrendsCsvOutput_MatchesFixture()
{
var client = new Mock<IBackendOperationsClient>();
client
.Setup(c => c.GetAnalyticsVulnerabilityTrendsAsync(
It.IsAny<string?>(),
It.IsAny<int?>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(BuildVulnerabilityTrendsResponse());
client
.Setup(c => c.GetAnalyticsComponentTrendsAsync(
It.IsAny<string?>(),
It.IsAny<int?>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(BuildComponentTrendsResponse());
var services = new ServiceCollection()
.AddSingleton(client.Object)
.BuildServiceProvider();
var root = BuildRoot(services);
var writer = new StringWriter(CultureInfo.InvariantCulture);
var originalOut = Console.Out;
int exitCode;
try
{
Console.SetOut(writer);
exitCode = await root.Parse("analytics sbom-lake trends --series all --days 14 --format csv").InvokeAsync();
}
finally
{
Console.SetOut(originalOut);
}
Assert.Equal(0, exitCode);
var expected = await File.ReadAllTextAsync(ResolveFixturePath("trends_all.csv"), CancellationToken.None);
Assert.Equal(expected.TrimEnd(), writer.ToString().TrimEnd());
}
private static RootCommand BuildRoot(IServiceProvider services)
{
var root = new RootCommand();
root.Add(AnalyticsCommandGroup.BuildAnalyticsCommand(
services,
new Option<bool>("--verbose", new[] { "-v" }),
CancellationToken.None));
return root;
}
private static AnalyticsListResponse<AnalyticsSupplierConcentration> BuildSuppliersResponse()
{
var items = new[]
{
new AnalyticsSupplierConcentration(
"Acme Co",
15,
12,
3,
2,
5,
new[] { "prod", "stage" }),
new AnalyticsSupplierConcentration(
"Omega Labs",
5,
3,
1,
0,
1,
new[] { "dev" })
};
return new AnalyticsListResponse<AnalyticsSupplierConcentration>(
"tenant-001",
"actor-001",
new DateTimeOffset(2026, 1, 20, 0, 0, 0, TimeSpan.Zero),
true,
300,
items,
items.Length);
}
private static AnalyticsListResponse<AnalyticsVulnerabilityTrendPoint> BuildVulnerabilityTrendsResponse()
{
var items = new[]
{
new AnalyticsVulnerabilityTrendPoint(
new DateTimeOffset(2026, 1, 18, 0, 0, 0, TimeSpan.Zero),
"prod",
42,
10,
5,
27,
2),
new AnalyticsVulnerabilityTrendPoint(
new DateTimeOffset(2026, 1, 19, 0, 0, 0, TimeSpan.Zero),
"stage",
35,
7,
4,
24,
1)
};
return new AnalyticsListResponse<AnalyticsVulnerabilityTrendPoint>(
"tenant-001",
"actor-001",
new DateTimeOffset(2026, 1, 20, 0, 0, 0, TimeSpan.Zero),
false,
0,
items,
items.Length);
}
private static AnalyticsListResponse<AnalyticsComponentTrendPoint> BuildComponentTrendsResponse()
{
var items = new[]
{
new AnalyticsComponentTrendPoint(
new DateTimeOffset(2026, 1, 18, 0, 0, 0, TimeSpan.Zero),
"prod",
1200,
80),
new AnalyticsComponentTrendPoint(
new DateTimeOffset(2026, 1, 19, 0, 0, 0, TimeSpan.Zero),
"stage",
950,
65)
};
return new AnalyticsListResponse<AnalyticsComponentTrendPoint>(
"tenant-001",
"actor-001",
new DateTimeOffset(2026, 1, 20, 0, 0, 0, TimeSpan.Zero),
false,
0,
items,
items.Length);
}
private static string ResolveFixturePath(string fileName)
{
var relative = Path.Combine(
"src",
"Cli",
"__Tests",
"StellaOps.Cli.Tests",
"Fixtures",
"Analytics",
fileName);
var baseDirectory = new DirectoryInfo(AppContext.BaseDirectory);
for (var directory = baseDirectory; directory is not null; directory = directory.Parent)
{
var candidate = Path.Combine(directory.FullName, relative);
if (File.Exists(candidate))
{
return candidate;
}
}
return Path.Combine("Fixtures", "Analytics", fileName);
}
}

View File

@@ -108,4 +108,16 @@ public sealed class CommandFactoryTests
var evidence = Assert.Single(root.Subcommands, command => string.Equals(command.Name, "evidence", StringComparison.Ordinal));
Assert.Contains(evidence.Subcommands, command => string.Equals(command.Name, "store", StringComparison.Ordinal));
}
[Fact]
public void Create_ExposesAnalyticsCommands()
{
using var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.None));
var services = new ServiceCollection().BuildServiceProvider();
var root = CommandFactory.Create(services, new StellaOpsCliOptions(), CancellationToken.None, loggerFactory);
var analytics = Assert.Single(root.Subcommands, command => string.Equals(command.Name, "analytics", StringComparison.Ordinal));
var sbomLake = Assert.Single(analytics.Subcommands, command => string.Equals(command.Name, "sbom-lake", StringComparison.Ordinal));
Assert.Contains(sbomLake.Subcommands, command => string.Equals(command.Name, "suppliers", StringComparison.Ordinal));
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
@@ -4925,6 +4925,39 @@ spec:
public Task<string?> GetScanSarifAsync(string scanId, bool includeHardening, bool includeReachability, string? minSeverity, CancellationToken cancellationToken)
=> Task.FromResult<string?>(null);
public Task<AnalyticsListResponse<AnalyticsSupplierConcentration>> GetAnalyticsSuppliersAsync(int? limit, string? environment, CancellationToken cancellationToken)
=> Task.FromResult(new AnalyticsListResponse<AnalyticsSupplierConcentration>(Array.Empty<AnalyticsSupplierConcentration>()));
public Task<AnalyticsListResponse<AnalyticsLicenseDistribution>> GetAnalyticsLicensesAsync(string? environment, CancellationToken cancellationToken)
=> Task.FromResult(new AnalyticsListResponse<AnalyticsLicenseDistribution>(Array.Empty<AnalyticsLicenseDistribution>()));
public Task<AnalyticsListResponse<AnalyticsVulnerabilityExposure>> GetAnalyticsVulnerabilitiesAsync(string? environment, string? minSeverity, CancellationToken cancellationToken)
=> Task.FromResult(new AnalyticsListResponse<AnalyticsVulnerabilityExposure>(Array.Empty<AnalyticsVulnerabilityExposure>()));
public Task<AnalyticsListResponse<AnalyticsFixableBacklogItem>> GetAnalyticsBacklogAsync(string? environment, CancellationToken cancellationToken)
=> Task.FromResult(new AnalyticsListResponse<AnalyticsFixableBacklogItem>(Array.Empty<AnalyticsFixableBacklogItem>()));
public Task<AnalyticsListResponse<AnalyticsAttestationCoverage>> GetAnalyticsAttestationCoverageAsync(string? environment, CancellationToken cancellationToken)
=> Task.FromResult(new AnalyticsListResponse<AnalyticsAttestationCoverage>(Array.Empty<AnalyticsAttestationCoverage>()));
public Task<AnalyticsListResponse<AnalyticsVulnerabilityTrendPoint>> GetAnalyticsVulnerabilityTrendsAsync(string? environment, int? days, CancellationToken cancellationToken)
=> Task.FromResult(new AnalyticsListResponse<AnalyticsVulnerabilityTrendPoint>(Array.Empty<AnalyticsVulnerabilityTrendPoint>()));
public Task<AnalyticsListResponse<AnalyticsComponentTrendPoint>> GetAnalyticsComponentTrendsAsync(string? environment, int? days, CancellationToken cancellationToken)
=> Task.FromResult(new AnalyticsListResponse<AnalyticsComponentTrendPoint>(Array.Empty<AnalyticsComponentTrendPoint>()));
public Task<WitnessListResponse> ListWitnessesAsync(WitnessListRequest request, CancellationToken cancellationToken)
=> Task.FromResult(new WitnessListResponse());
public Task<WitnessDetailResponse?> GetWitnessAsync(string witnessId, CancellationToken cancellationToken)
=> Task.FromResult<WitnessDetailResponse?>(null);
public Task<WitnessVerifyResponse> VerifyWitnessAsync(string witnessId, CancellationToken cancellationToken)
=> Task.FromResult(new WitnessVerifyResponse());
public Task<Stream> DownloadWitnessAsync(string witnessId, WitnessExportFormat format, CancellationToken cancellationToken)
=> Task.FromResult<Stream>(new MemoryStream(Encoding.UTF8.GetBytes("{}")));
}
private sealed class StubExecutor : IScannerExecutor
@@ -5145,3 +5178,4 @@ spec:
}
}
}

View File

@@ -0,0 +1,338 @@
// -----------------------------------------------------------------------------
// GroundTruthCommandTests.cs
// Sprint: SPRINT_20260121_035_BinaryIndex_golden_corpus_connectors_cli
// Task: GCC-005 - CLI commands for ground-truth corpus management
// Description: Unit tests for groundtruth CLI command parsing
// -----------------------------------------------------------------------------
using System.CommandLine;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Cli.Commands;
using Xunit;
namespace StellaOps.Cli.Tests.Commands;
public sealed class GroundTruthCommandTests
{
private readonly IServiceProvider _services;
private readonly Option<bool> _verboseOption;
private readonly CancellationToken _cancellationToken;
private readonly Command _groundTruthCommand;
public GroundTruthCommandTests()
{
_services = new ServiceCollection().BuildServiceProvider();
_verboseOption = new Option<bool>("--verbose", new[] { "-v" })
{
Description = "Enable verbose output"
};
_cancellationToken = CancellationToken.None;
_groundTruthCommand = GroundTruthCommandGroup.BuildGroundTruthCommand(
_services,
_verboseOption,
_cancellationToken);
}
#region Command Structure Tests
[Fact]
public void BuildGroundTruthCommand_CreatesCommandWithCorrectName()
{
// Assert
_groundTruthCommand.Name.Should().Be("groundtruth");
}
[Fact]
public void BuildGroundTruthCommand_HasDescription()
{
// Assert
_groundTruthCommand.Description.Should().NotBeNullOrEmpty();
_groundTruthCommand.Description.Should().Contain("corpus");
}
[Fact]
public void BuildGroundTruthCommand_HasFourSubcommands()
{
// Assert
_groundTruthCommand.Subcommands.Should().HaveCount(4);
}
[Fact]
public void BuildGroundTruthCommand_HasSourcesSubcommand()
{
// Act
var sourcesCommand = _groundTruthCommand.Subcommands
.FirstOrDefault(c => c.Name == "sources");
// Assert
sourcesCommand.Should().NotBeNull();
sourcesCommand!.Description.Should().Contain("source");
}
[Fact]
public void BuildGroundTruthCommand_HasSymbolsSubcommand()
{
// Act
var symbolsCommand = _groundTruthCommand.Subcommands
.FirstOrDefault(c => c.Name == "symbols");
// Assert
symbolsCommand.Should().NotBeNull();
symbolsCommand!.Description.Should().Contain("symbol");
}
[Fact]
public void BuildGroundTruthCommand_HasPairsSubcommand()
{
// Act
var pairsCommand = _groundTruthCommand.Subcommands
.FirstOrDefault(c => c.Name == "pairs");
// Assert
pairsCommand.Should().NotBeNull();
pairsCommand!.Description.Should().Contain("pair");
}
[Fact]
public void BuildGroundTruthCommand_HasValidateSubcommand()
{
// Act
var validateCommand = _groundTruthCommand.Subcommands
.FirstOrDefault(c => c.Name == "validate");
// Assert
validateCommand.Should().NotBeNull();
validateCommand!.Description.Should().Contain("validation");
}
#endregion
#region Sources Subcommand Tests
[Fact]
public void Sources_HasFourSubcommands()
{
// Act
var sourcesCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "sources");
// Assert
sourcesCommand.Subcommands.Should().HaveCount(4);
}
[Fact]
public void Sources_HasListCommand()
{
// Act
var sourcesCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "sources");
var listCommand = sourcesCommand.Subcommands.FirstOrDefault(c => c.Name == "list");
// Assert
listCommand.Should().NotBeNull();
}
[Fact]
public void Sources_HasEnableCommand()
{
// Act
var sourcesCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "sources");
var enableCommand = sourcesCommand.Subcommands.FirstOrDefault(c => c.Name == "enable");
// Assert
enableCommand.Should().NotBeNull();
}
[Fact]
public void Sources_HasDisableCommand()
{
// Act
var sourcesCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "sources");
var disableCommand = sourcesCommand.Subcommands.FirstOrDefault(c => c.Name == "disable");
// Assert
disableCommand.Should().NotBeNull();
}
[Fact]
public void Sources_HasSyncCommand()
{
// Act
var sourcesCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "sources");
var syncCommand = sourcesCommand.Subcommands.FirstOrDefault(c => c.Name == "sync");
// Assert
syncCommand.Should().NotBeNull();
}
[Fact]
public void Sources_Enable_HasSourceArgument()
{
// Arrange
var sourcesCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "sources");
var enableCommand = sourcesCommand.Subcommands.First(c => c.Name == "enable");
// Assert
enableCommand.Arguments.Should().NotBeEmpty();
}
#endregion
#region Symbols Subcommand Tests
[Fact]
public void Symbols_HasTwoSubcommands()
{
// Act
var symbolsCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "symbols");
// Assert
symbolsCommand.Subcommands.Should().HaveCount(2);
}
[Fact]
public void Symbols_HasLookupCommand()
{
// Act
var symbolsCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "symbols");
var lookupCommand = symbolsCommand.Subcommands.FirstOrDefault(c => c.Name == "lookup");
// Assert
lookupCommand.Should().NotBeNull();
}
[Fact]
public void Symbols_HasSearchCommand()
{
// Act
var symbolsCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "symbols");
var searchCommand = symbolsCommand.Subcommands.FirstOrDefault(c => c.Name == "search");
// Assert
searchCommand.Should().NotBeNull();
}
#endregion
#region Pairs Subcommand Tests
[Fact]
public void Pairs_HasThreeSubcommands()
{
// Act
var pairsCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "pairs");
// Assert
pairsCommand.Subcommands.Should().HaveCount(3);
}
[Fact]
public void Pairs_HasCreateCommand()
{
// Act
var pairsCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "pairs");
var createCommand = pairsCommand.Subcommands.FirstOrDefault(c => c.Name == "create");
// Assert
createCommand.Should().NotBeNull();
}
[Fact]
public void Pairs_HasListCommand()
{
// Act
var pairsCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "pairs");
var listCommand = pairsCommand.Subcommands.FirstOrDefault(c => c.Name == "list");
// Assert
listCommand.Should().NotBeNull();
}
[Fact]
public void Pairs_HasDeleteCommand()
{
// Act
var pairsCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "pairs");
var deleteCommand = pairsCommand.Subcommands.FirstOrDefault(c => c.Name == "delete");
// Assert
deleteCommand.Should().NotBeNull();
}
[Fact]
public void Pairs_Delete_HasArgument()
{
// Arrange
var pairsCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "pairs");
var deleteCommand = pairsCommand.Subcommands.First(c => c.Name == "delete");
// Assert
deleteCommand.Arguments.Should().NotBeEmpty();
}
#endregion
#region Validate Subcommand Tests
[Fact]
public void Validate_HasThreeSubcommands()
{
// Act
var validateCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "validate");
// Assert
validateCommand.Subcommands.Should().HaveCount(3);
}
[Fact]
public void Validate_HasRunCommand()
{
// Act
var validateCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "validate");
var runCommand = validateCommand.Subcommands.FirstOrDefault(c => c.Name == "run");
// Assert
runCommand.Should().NotBeNull();
}
[Fact]
public void Validate_HasMetricsCommand()
{
// Act
var validateCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "validate");
var metricsCommand = validateCommand.Subcommands.FirstOrDefault(c => c.Name == "metrics");
// Assert
metricsCommand.Should().NotBeNull();
}
[Fact]
public void Validate_HasExportCommand()
{
// Act
var validateCommand = _groundTruthCommand.Subcommands.First(c => c.Name == "validate");
var exportCommand = validateCommand.Subcommands.FirstOrDefault(c => c.Name == "export");
// Assert
exportCommand.Should().NotBeNull();
}
#endregion
#region Output Format Tests
[Fact]
public void OutputFormat_Enum_HasTableValue()
{
// Assert
Enum.IsDefined(typeof(GroundTruthOutputFormat), "Table").Should().BeTrue();
}
[Fact]
public void OutputFormat_Enum_HasJsonValue()
{
// Assert
Enum.IsDefined(typeof(GroundTruthOutputFormat), "Json").Should().BeTrue();
}
#endregion
}

View File

@@ -59,7 +59,7 @@ public sealed class ProveCommandTests : IDisposable
command.Description.Should().Contain("replay proof");
}
[Fact(Skip = "System.CommandLine 2.0 API change - options lookup behavior changed")]
[Fact]
public void BuildProveCommand_HasRequiredImageOption()
{
// Arrange
@@ -69,13 +69,13 @@ public sealed class ProveCommandTests : IDisposable
// Act
var command = ProveCommandGroup.BuildProveCommand(services, verboseOption, CancellationToken.None);
// Assert - search by alias since Name includes the dashes
var imageOption = command.Options.FirstOrDefault(o => o.Aliases.Contains("--image"));
imageOption.Should().NotBeNull();
imageOption!.Required.Should().BeTrue();
// Assert - check that image option exists (by name containing "image")
var imageOption = command.Options.FirstOrDefault(o => o.Name.Contains("image", StringComparison.OrdinalIgnoreCase));
imageOption.Should().NotBeNull("prove command should have an image option");
imageOption!.Required.Should().BeTrue("image option should be required");
}
[Fact(Skip = "System.CommandLine 2.0 API change - options lookup behavior changed")]
[Fact]
public void BuildProveCommand_HasOptionalAtOption()
{
// Arrange
@@ -86,12 +86,12 @@ public sealed class ProveCommandTests : IDisposable
var command = ProveCommandGroup.BuildProveCommand(services, verboseOption, CancellationToken.None);
// Assert
var atOption = command.Options.FirstOrDefault(o => o.Aliases.Contains("--at"));
atOption.Should().NotBeNull();
atOption!.Required.Should().BeFalse();
var atOption = command.Options.FirstOrDefault(o => o.Name.Contains("at", StringComparison.OrdinalIgnoreCase) && o.Name.Length <= 4);
atOption.Should().NotBeNull("prove command should have an at option");
atOption!.Required.Should().BeFalse("at option should be optional");
}
[Fact(Skip = "System.CommandLine 2.0 API change - options lookup behavior changed")]
[Fact]
public void BuildProveCommand_HasOptionalSnapshotOption()
{
// Arrange
@@ -102,12 +102,12 @@ public sealed class ProveCommandTests : IDisposable
var command = ProveCommandGroup.BuildProveCommand(services, verboseOption, CancellationToken.None);
// Assert
var snapshotOption = command.Options.FirstOrDefault(o => o.Aliases.Contains("--snapshot"));
snapshotOption.Should().NotBeNull();
snapshotOption!.Required.Should().BeFalse();
var snapshotOption = command.Options.FirstOrDefault(o => o.Name.Contains("snapshot", StringComparison.OrdinalIgnoreCase));
snapshotOption.Should().NotBeNull("prove command should have a snapshot option");
snapshotOption!.Required.Should().BeFalse("snapshot option should be optional");
}
[Fact(Skip = "System.CommandLine 2.0 API change - options lookup behavior changed")]
[Fact]
public void BuildProveCommand_HasOptionalBundleOption()
{
// Arrange
@@ -118,12 +118,12 @@ public sealed class ProveCommandTests : IDisposable
var command = ProveCommandGroup.BuildProveCommand(services, verboseOption, CancellationToken.None);
// Assert
var bundleOption = command.Options.FirstOrDefault(o => o.Aliases.Contains("--bundle"));
bundleOption.Should().NotBeNull();
bundleOption!.Required.Should().BeFalse();
var bundleOption = command.Options.FirstOrDefault(o => o.Name.Contains("bundle", StringComparison.OrdinalIgnoreCase));
bundleOption.Should().NotBeNull("prove command should have a bundle option");
bundleOption!.Required.Should().BeFalse("bundle option should be optional");
}
[Fact(Skip = "System.CommandLine 2.0 API change - options lookup behavior changed")]
[Fact]
public void BuildProveCommand_HasOutputOptionWithValidValues()
{
// Arrange
@@ -134,8 +134,8 @@ public sealed class ProveCommandTests : IDisposable
var command = ProveCommandGroup.BuildProveCommand(services, verboseOption, CancellationToken.None);
// Assert
var outputOption = command.Options.FirstOrDefault(o => o.Aliases.Contains("--output"));
outputOption.Should().NotBeNull();
var outputOption = command.Options.FirstOrDefault(o => o.Name.Contains("output", StringComparison.OrdinalIgnoreCase));
outputOption.Should().NotBeNull("prove command should have an output option");
}
#endregion

View File

@@ -1,4 +1,4 @@
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// ScanWorkersOptionTests.cs
// Sprint: SPRINT_20260117_005_CLI_scanning_detection (SCD-005)
// Description: Unit tests for scan run --workers option
@@ -33,3 +33,4 @@ public sealed class ScanWorkersOptionTests
Assert.Equal(4, result.GetValueForOption(workersOption!));
}
}