Refactor code structure for improved readability and maintainability; optimize performance in key functions.

This commit is contained in:
master
2025-12-22 19:06:31 +02:00
parent dfaa2079aa
commit 4602ccc3a3
1444 changed files with 109919 additions and 8058 deletions

View File

@@ -72,4 +72,16 @@ public sealed class CommandFactoryTests
Assert.Contains(bun.Subcommands, command => string.Equals(command.Name, "inspect", StringComparison.Ordinal));
Assert.Contains(bun.Subcommands, command => string.Equals(command.Name, "resolve", StringComparison.Ordinal));
}
[Fact]
public void Create_ExposesSbomUploadCommand()
{
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 sbom = Assert.Single(root.Subcommands, command => string.Equals(command.Name, "sbom", StringComparison.Ordinal));
Assert.Contains(sbom.Subcommands, command => string.Equals(command.Name, "upload", StringComparison.Ordinal));
}
}

View File

@@ -0,0 +1,157 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Spectre.Console;
using Spectre.Console.Testing;
using StellaOps.Cli.Commands;
using StellaOps.Cli.Services;
using StellaOps.Cli.Services.Models;
using Xunit;
namespace StellaOps.Cli.Tests.Commands;
public sealed class SbomUploadCommandHandlersTests
{
[Fact]
public async Task HandleSbomUploadAsync_ReturnsErrorOnInvalidValidation()
{
var tempPath = Path.Combine(Path.GetTempPath(), $"sbom-{Guid.NewGuid():N}.json");
await File.WriteAllTextAsync(tempPath, "{\"bomFormat\":\"CycloneDX\",\"specVersion\":\"1.6\",\"components\":[]}");
try
{
var response = new SbomUploadResponse
{
SbomId = "sbom-1",
ArtifactRef = "example.com/app:1.0",
ValidationResult = new SbomUploadValidationSummary
{
Valid = false,
Errors = new[] { "Invalid SBOM." }
}
};
var provider = BuildServiceProvider(new StubSbomClient(response));
var exitCode = await RunWithTestConsoleAsync(() =>
CommandHandlers.HandleSbomUploadAsync(
provider,
tempPath,
"example.com/app:1.0",
null,
null,
null,
null,
null,
json: false,
verbose: false,
cancellationToken: CancellationToken.None));
Assert.Equal(18, exitCode);
}
finally
{
File.Delete(tempPath);
}
}
[Fact]
public async Task HandleSbomUploadAsync_ReturnsZeroOnSuccess()
{
var tempPath = Path.Combine(Path.GetTempPath(), $"sbom-{Guid.NewGuid():N}.json");
await File.WriteAllTextAsync(tempPath, "{\"bomFormat\":\"CycloneDX\",\"specVersion\":\"1.6\",\"components\":[]}");
try
{
var response = new SbomUploadResponse
{
SbomId = "sbom-2",
ArtifactRef = "example.com/app:2.0",
Digest = "sha256:abc",
Format = "cyclonedx",
FormatVersion = "1.6",
AnalysisJobId = "job-1",
ValidationResult = new SbomUploadValidationSummary
{
Valid = true,
ComponentCount = 0,
QualityScore = 1.0
}
};
var provider = BuildServiceProvider(new StubSbomClient(response));
var exitCode = await RunWithTestConsoleAsync(() =>
CommandHandlers.HandleSbomUploadAsync(
provider,
tempPath,
"example.com/app:2.0",
null,
null,
null,
null,
null,
json: false,
verbose: false,
cancellationToken: CancellationToken.None));
Assert.Equal(0, exitCode);
}
finally
{
File.Delete(tempPath);
}
}
private static IServiceProvider BuildServiceProvider(ISbomClient client)
{
var services = new ServiceCollection();
services.AddSingleton(client);
services.AddSingleton<ILoggerFactory>(_ => LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.None)));
return services.BuildServiceProvider();
}
private static async Task<int> RunWithTestConsoleAsync(Func<Task<int>> action)
{
var original = AnsiConsole.Console;
var testConsole = new TestConsole();
try
{
AnsiConsole.Console = testConsole;
return await action().ConfigureAwait(false);
}
finally
{
AnsiConsole.Console = original;
}
}
private sealed class StubSbomClient : ISbomClient
{
private readonly SbomUploadResponse? _response;
public StubSbomClient(SbomUploadResponse? response)
{
_response = response;
}
public Task<SbomListResponse> ListAsync(SbomListRequest request, CancellationToken cancellationToken)
=> throw new NotSupportedException();
public Task<SbomDetailResponse?> GetAsync(string sbomId, string? tenant, bool includeComponents, bool includeVulnerabilities, bool includeLicenses, bool explain, CancellationToken cancellationToken)
=> throw new NotSupportedException();
public Task<SbomCompareResponse?> CompareAsync(SbomCompareRequest request, CancellationToken cancellationToken)
=> throw new NotSupportedException();
public Task<(Stream Content, SbomExportResult? Result)> ExportAsync(SbomExportRequest request, CancellationToken cancellationToken)
=> throw new NotSupportedException();
public Task<SbomUploadResponse?> UploadAsync(SbomUploadRequest request, CancellationToken cancellationToken)
=> Task.FromResult(_response);
public Task<ParityMatrixResponse> GetParityMatrixAsync(string? tenant, CancellationToken cancellationToken)
=> throw new NotSupportedException();
}
}

View File

@@ -0,0 +1,85 @@
// -----------------------------------------------------------------------------
// Sprint5100_CommandTests.cs
// Sprint: SPRINT_5100_0002_0002 / SPRINT_5100_0002_0003
// Description: CLI command tree tests for replay and delta commands
// -----------------------------------------------------------------------------
using System.CommandLine;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
using StellaOps.Cli.Commands;
namespace StellaOps.Cli.Tests.Commands;
public class Sprint5100_CommandTests
{
private readonly IServiceProvider _services;
private readonly Option<bool> _verboseOption;
private readonly CancellationToken _cancellationToken;
public Sprint5100_CommandTests()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance));
_services = serviceCollection.BuildServiceProvider();
_verboseOption = new Option<bool>("--verbose", "-v") { Description = "Verbose output" };
_cancellationToken = CancellationToken.None;
}
[Fact]
public void ReplayCommand_CreatesCommandTree()
{
var command = ReplayCommandGroup.BuildReplayCommand(_verboseOption, _cancellationToken);
Assert.Equal("replay", command.Name);
Assert.Contains("Replay scans", command.Description);
Assert.NotNull(command.Subcommands.FirstOrDefault(c => c.Name == "verify"));
Assert.NotNull(command.Subcommands.FirstOrDefault(c => c.Name == "diff"));
Assert.NotNull(command.Subcommands.FirstOrDefault(c => c.Name == "batch"));
}
[Fact]
public void ReplayCommand_ParsesWithManifest()
{
var command = ReplayCommandGroup.BuildReplayCommand(_verboseOption, _cancellationToken);
var root = new RootCommand { command };
var result = root.Parse("replay --manifest run-manifest.json");
Assert.Empty(result.Errors);
}
[Fact]
public void DeltaCommand_CreatesCommandTree()
{
var command = DeltaCommandGroup.BuildDeltaCommand(_verboseOption, _cancellationToken);
Assert.Equal("delta", command.Name);
Assert.NotNull(command.Subcommands.FirstOrDefault(c => c.Name == "compute"));
Assert.NotNull(command.Subcommands.FirstOrDefault(c => c.Name == "check"));
Assert.NotNull(command.Subcommands.FirstOrDefault(c => c.Name == "attach"));
}
[Fact]
public void DeltaCompute_ParsesRequiredOptions()
{
var command = DeltaCommandGroup.BuildDeltaCommand(_verboseOption, _cancellationToken);
var root = new RootCommand { command };
var result = root.Parse("delta compute --base base.json --head head.json");
Assert.Empty(result.Errors);
}
[Fact]
public void DeltaCheck_RequiresDeltaOption()
{
var command = DeltaCommandGroup.BuildDeltaCommand(_verboseOption, _cancellationToken);
var root = new RootCommand { command };
var result = root.Parse("delta check");
Assert.NotEmpty(result.Errors);
}
}

View File

@@ -0,0 +1,28 @@
using System.CommandLine;
using System.Linq;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StellaOps.Cli.Commands;
using StellaOps.Cli.Configuration;
namespace StellaOps.Cli.Tests.Commands;
public sealed class VerifyImageCommandTests
{
[Fact]
public void Create_ExposesVerifyImageCommand()
{
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 verify = Assert.Single(root.Subcommands, command => string.Equals(command.Name, "verify", StringComparison.Ordinal));
var image = Assert.Single(verify.Subcommands, command => string.Equals(command.Name, "image", StringComparison.Ordinal));
Assert.Contains(image.Options, option => option.HasAlias("--require"));
Assert.Contains(image.Options, option => option.HasAlias("--trust-policy"));
Assert.Contains(image.Options, option => option.HasAlias("--output"));
Assert.Contains(image.Options, option => option.HasAlias("--strict"));
}
}

View File

@@ -0,0 +1,146 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Spectre.Console;
using Spectre.Console.Testing;
using StellaOps.Cli.Commands;
using StellaOps.Cli.Configuration;
using StellaOps.Cli.Services;
using StellaOps.Cli.Services.Models;
namespace StellaOps.Cli.Tests.Commands;
public sealed class VerifyImageHandlerTests
{
[Fact]
public void ParseImageReference_WithDigest_Parses()
{
var (registry, repository, digest) = CommandHandlers.ParseImageReference("gcr.io/myproject/myapp@sha256:abc123");
Assert.Equal("gcr.io", registry);
Assert.Equal("myproject/myapp", repository);
Assert.Equal("sha256:abc123", digest);
}
[Fact]
public async Task HandleVerifyImageAsync_ValidResult_ReturnsZero()
{
var result = new ImageVerificationResult
{
ImageReference = "registry.example.com/app@sha256:deadbeef",
ImageDigest = "sha256:deadbeef",
VerifiedAt = DateTimeOffset.UtcNow,
IsValid = true
};
var provider = BuildServices(new StubVerifier(result));
var originalExit = Environment.ExitCode;
try
{
await CaptureConsoleAsync(async _ =>
{
var exitCode = await CommandHandlers.HandleVerifyImageAsync(
provider,
"registry.example.com/app@sha256:deadbeef",
new[] { "sbom" },
trustPolicy: null,
output: "json",
strict: false,
verbose: false,
cancellationToken: CancellationToken.None);
Assert.Equal(0, exitCode);
});
Assert.Equal(0, Environment.ExitCode);
}
finally
{
Environment.ExitCode = originalExit;
}
}
[Fact]
public async Task HandleVerifyImageAsync_InvalidResult_ReturnsOne()
{
var result = new ImageVerificationResult
{
ImageReference = "registry.example.com/app@sha256:deadbeef",
ImageDigest = "sha256:deadbeef",
VerifiedAt = DateTimeOffset.UtcNow,
IsValid = false
};
var provider = BuildServices(new StubVerifier(result));
var originalExit = Environment.ExitCode;
try
{
await CaptureConsoleAsync(async _ =>
{
var exitCode = await CommandHandlers.HandleVerifyImageAsync(
provider,
"registry.example.com/app@sha256:deadbeef",
new[] { "sbom" },
trustPolicy: null,
output: "json",
strict: true,
verbose: false,
cancellationToken: CancellationToken.None);
Assert.Equal(1, exitCode);
});
Assert.Equal(1, Environment.ExitCode);
}
finally
{
Environment.ExitCode = originalExit;
}
}
private static ServiceProvider BuildServices(IImageAttestationVerifier verifier)
{
var services = new ServiceCollection();
services.AddLogging(builder => builder.SetMinimumLevel(LogLevel.None));
services.AddSingleton(new StellaOpsCliOptions());
services.AddSingleton(verifier);
return services.BuildServiceProvider();
}
private static async Task CaptureConsoleAsync(Func<TestConsole, Task> action)
{
var testConsole = new TestConsole();
var originalConsole = AnsiConsole.Console;
var originalOut = Console.Out;
using var writer = new StringWriter();
try
{
AnsiConsole.Console = testConsole;
Console.SetOut(writer);
await action(testConsole).ConfigureAwait(false);
}
finally
{
AnsiConsole.Console = originalConsole;
Console.SetOut(originalOut);
}
}
private sealed class StubVerifier : IImageAttestationVerifier
{
private readonly ImageVerificationResult _result;
public StubVerifier(ImageVerificationResult result)
{
_result = result;
}
public Task<ImageVerificationResult> VerifyAsync(ImageVerificationRequest request, CancellationToken cancellationToken = default)
=> Task.FromResult(_result);
}
}