Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -23,4 +23,17 @@ public sealed class CommandFactoryTests
|
||||
Assert.Contains(ruby.Subcommands, command => string.Equals(command.Name, "inspect", StringComparison.Ordinal));
|
||||
Assert.Contains(ruby.Subcommands, command => string.Equals(command.Name, "resolve", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_ExposesBunInspectAndResolveCommands()
|
||||
{
|
||||
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 bun = Assert.Single(root.Subcommands, command => string.Equals(command.Name, "bun", StringComparison.Ordinal));
|
||||
|
||||
Assert.Contains(bun.Subcommands, command => string.Equals(command.Name, "inspect", StringComparison.Ordinal));
|
||||
Assert.Contains(bun.Subcommands, command => string.Equals(command.Name, "resolve", StringComparison.Ordinal));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ using StellaOps.Cli.Configuration;
|
||||
using StellaOps.Cli.Services;
|
||||
using StellaOps.Cli.Services.Models;
|
||||
using StellaOps.Cli.Services.Models.AdvisoryAi;
|
||||
using StellaOps.Cli.Services.Models.Bun;
|
||||
using StellaOps.Cli.Services.Models.Ruby;
|
||||
using StellaOps.Cli.Telemetry;
|
||||
using StellaOps.Cli.Tests.Testing;
|
||||
@@ -641,6 +642,161 @@ public sealed class CommandHandlersTests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleBunInspectAsync_WritesJson()
|
||||
{
|
||||
var originalExit = Environment.ExitCode;
|
||||
using var fixture = new TempDirectory();
|
||||
CreateBunWorkspace(fixture.Path);
|
||||
var provider = BuildServiceProvider(new StubBackendClient(new JobTriggerResult(true, "ok", null, null)));
|
||||
|
||||
try
|
||||
{
|
||||
var output = await CaptureTestConsoleAsync(async _ =>
|
||||
{
|
||||
await CommandHandlers.HandleBunInspectAsync(
|
||||
provider,
|
||||
fixture.Path,
|
||||
"json",
|
||||
verbose: false,
|
||||
cancellationToken: CancellationToken.None);
|
||||
});
|
||||
|
||||
Assert.Equal(0, Environment.ExitCode);
|
||||
using var document = JsonDocument.Parse(output.PlainBuffer);
|
||||
var packages = document.RootElement.GetProperty("packages");
|
||||
Assert.NotEmpty(packages.EnumerateArray());
|
||||
|
||||
Assert.Contains(packages.EnumerateArray(), p =>
|
||||
string.Equals(p.GetProperty("name").GetString(), "lodash", StringComparison.OrdinalIgnoreCase));
|
||||
Assert.Contains(packages.EnumerateArray(), p =>
|
||||
string.Equals(p.GetProperty("name").GetString(), "express", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.ExitCode = originalExit;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleBunResolveAsync_RendersPackages()
|
||||
{
|
||||
var originalExit = Environment.ExitCode;
|
||||
var backend = new StubBackendClient(new JobTriggerResult(true, "ok", null, null))
|
||||
{
|
||||
BunInventory = CreateBunInventory(
|
||||
"scan-bun",
|
||||
new[]
|
||||
{
|
||||
CreateBunPackageItem("lodash", "4.17.21", isDev: false, isDirect: true),
|
||||
CreateBunPackageItem("express", "4.18.2", isDev: false, isDirect: true),
|
||||
CreateBunPackageItem("typescript", "5.3.3", isDev: true, isDirect: true)
|
||||
})
|
||||
};
|
||||
var provider = BuildServiceProvider(backend);
|
||||
|
||||
try
|
||||
{
|
||||
var output = await CaptureTestConsoleAsync(async _ =>
|
||||
{
|
||||
await CommandHandlers.HandleBunResolveAsync(
|
||||
provider,
|
||||
imageReference: null,
|
||||
scanId: "scan-bun",
|
||||
format: "table",
|
||||
verbose: false,
|
||||
cancellationToken: CancellationToken.None);
|
||||
});
|
||||
|
||||
Assert.Equal(0, Environment.ExitCode);
|
||||
Assert.Equal("scan-bun", backend.LastBunPackagesScanId);
|
||||
Assert.Contains("scan-bun", output.Combined, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("lodash", output.Combined, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("express", output.Combined, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.ExitCode = originalExit;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleBunResolveAsync_WritesJson()
|
||||
{
|
||||
var originalExit = Environment.ExitCode;
|
||||
const string identifier = "bun-scan-json";
|
||||
var backend = new StubBackendClient(new JobTriggerResult(true, "ok", null, null))
|
||||
{
|
||||
BunInventory = CreateBunInventory(
|
||||
identifier,
|
||||
new[]
|
||||
{
|
||||
CreateBunPackageItem("lodash", "4.17.21", isDev: false, isDirect: true)
|
||||
})
|
||||
};
|
||||
var provider = BuildServiceProvider(backend);
|
||||
|
||||
try
|
||||
{
|
||||
var output = await CaptureTestConsoleAsync(async _ =>
|
||||
{
|
||||
await CommandHandlers.HandleBunResolveAsync(
|
||||
provider,
|
||||
imageReference: identifier,
|
||||
scanId: null,
|
||||
format: "json",
|
||||
verbose: false,
|
||||
cancellationToken: CancellationToken.None);
|
||||
});
|
||||
|
||||
Assert.Equal(0, Environment.ExitCode);
|
||||
Assert.Equal(identifier, backend.LastBunPackagesScanId);
|
||||
|
||||
using var document = JsonDocument.Parse(output.PlainBuffer);
|
||||
Assert.Equal(identifier, document.RootElement.GetProperty("scanId").GetString());
|
||||
|
||||
var packages = document.RootElement.GetProperty("packages");
|
||||
Assert.Single(packages.EnumerateArray());
|
||||
|
||||
var package = packages.EnumerateArray().First();
|
||||
Assert.Equal("lodash", package.GetProperty("name").GetString());
|
||||
Assert.Equal("4.17.21", package.GetProperty("version").GetString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.ExitCode = originalExit;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleBunResolveAsync_NotifiesWhenInventoryMissing()
|
||||
{
|
||||
var originalExit = Environment.ExitCode;
|
||||
var backend = new StubBackendClient(new JobTriggerResult(true, "ok", null, null));
|
||||
var provider = BuildServiceProvider(backend);
|
||||
|
||||
try
|
||||
{
|
||||
var output = await CaptureTestConsoleAsync(async _ =>
|
||||
{
|
||||
await CommandHandlers.HandleBunResolveAsync(
|
||||
provider,
|
||||
imageReference: null,
|
||||
scanId: "scan-missing-bun",
|
||||
format: "table",
|
||||
verbose: false,
|
||||
cancellationToken: CancellationToken.None);
|
||||
});
|
||||
|
||||
Assert.Equal(0, Environment.ExitCode);
|
||||
Assert.Contains("not available", output.Combined, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.ExitCode = originalExit;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAdviseRunAsync_WritesOutputAndSetsExitCode()
|
||||
{
|
||||
@@ -4081,6 +4237,84 @@ spec:
|
||||
packages);
|
||||
}
|
||||
|
||||
private static void CreateBunWorkspace(string root)
|
||||
{
|
||||
var packageJson = """
|
||||
{
|
||||
"name": "test-bun-app",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"lodash": "4.17.21",
|
||||
"express": "4.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "5.3.3"
|
||||
}
|
||||
}
|
||||
""";
|
||||
File.WriteAllText(Path.Combine(root, "package.json"), packageJson);
|
||||
|
||||
var bunLock = """
|
||||
{
|
||||
"lockfileVersion": 0,
|
||||
"packages": {
|
||||
"lodash": ["lodash@4.17.21", { "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDE+k+xyz=" }],
|
||||
"express": ["express@4.18.2", { "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", "integrity": "sha512-expr+k+abc=" }],
|
||||
"typescript": ["typescript@5.3.3", { "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", "integrity": "sha512-ts+k+def=" }]
|
||||
},
|
||||
"workspaces": {}
|
||||
}
|
||||
""";
|
||||
File.WriteAllText(Path.Combine(root, "bun.lock"), bunLock);
|
||||
|
||||
var nodeModules = Path.Combine(root, "node_modules");
|
||||
Directory.CreateDirectory(nodeModules);
|
||||
|
||||
var lodashDir = Path.Combine(nodeModules, "lodash");
|
||||
Directory.CreateDirectory(lodashDir);
|
||||
File.WriteAllText(Path.Combine(lodashDir, "package.json"), """{"name":"lodash","version":"4.17.21"}""");
|
||||
|
||||
var expressDir = Path.Combine(nodeModules, "express");
|
||||
Directory.CreateDirectory(expressDir);
|
||||
File.WriteAllText(Path.Combine(expressDir, "package.json"), """{"name":"express","version":"4.18.2"}""");
|
||||
|
||||
var typescriptDir = Path.Combine(nodeModules, "typescript");
|
||||
Directory.CreateDirectory(typescriptDir);
|
||||
File.WriteAllText(Path.Combine(typescriptDir, "package.json"), """{"name":"typescript","version":"5.3.3"}""");
|
||||
}
|
||||
|
||||
private static BunPackageItem CreateBunPackageItem(
|
||||
string name,
|
||||
string? version = null,
|
||||
string? source = null,
|
||||
bool? isDev = null,
|
||||
bool? isDirect = null,
|
||||
IDictionary<string, string?>? metadata = null)
|
||||
{
|
||||
return new BunPackageItem(
|
||||
name,
|
||||
version,
|
||||
source ?? "registry",
|
||||
$"https://registry.npmjs.org/{name}/-/{name}-{version ?? "1.0.0"}.tgz",
|
||||
"sha512-abc123=",
|
||||
isDev,
|
||||
isDirect,
|
||||
IsPatched: null,
|
||||
CustomRegistry: null,
|
||||
metadata ?? new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static BunPackageInventory CreateBunInventory(
|
||||
string scanId,
|
||||
IReadOnlyList<BunPackageItem> packages,
|
||||
string? imageDigest = null)
|
||||
{
|
||||
return new BunPackageInventory(
|
||||
scanId,
|
||||
imageDigest ?? "sha256:bun-inventory",
|
||||
DateTimeOffset.UtcNow,
|
||||
packages);
|
||||
}
|
||||
|
||||
private static string ComputeSha256Base64(string path)
|
||||
{
|
||||
@@ -4165,6 +4399,9 @@ spec:
|
||||
public RubyPackageInventoryModel? RubyInventory { get; set; }
|
||||
public Exception? RubyInventoryException { get; set; }
|
||||
public string? LastRubyPackagesScanId { get; private set; }
|
||||
public BunPackageInventory? BunInventory { get; set; }
|
||||
public Exception? BunInventoryException { get; set; }
|
||||
public string? LastBunPackagesScanId { get; private set; }
|
||||
public List<(string ExportId, string DestinationPath, string? Algorithm, string? Digest)> ExportDownloads { get; } = new();
|
||||
public ExcititorOperationResult? ExcititorResult { get; set; } = new ExcititorOperationResult(true, "ok", null, null);
|
||||
public IReadOnlyList<ExcititorProviderSummary> ProviderSummaries { get; set; } = Array.Empty<ExcititorProviderSummary>();
|
||||
@@ -4415,6 +4652,17 @@ spec:
|
||||
return Task.FromResult(RubyInventory);
|
||||
}
|
||||
|
||||
public Task<BunPackageInventory?> GetBunPackagesAsync(string scanId, CancellationToken cancellationToken)
|
||||
{
|
||||
LastBunPackagesScanId = scanId;
|
||||
if (BunInventoryException is not null)
|
||||
{
|
||||
throw BunInventoryException;
|
||||
}
|
||||
|
||||
return Task.FromResult(BunInventory);
|
||||
}
|
||||
|
||||
public Task<AdvisoryPipelinePlanResponseModel> CreateAdvisoryPipelinePlanAsync(AdvisoryAiTaskType taskType, AdvisoryPipelinePlanRequestModel request, CancellationToken cancellationToken)
|
||||
{
|
||||
AdvisoryPlanRequests.Add((taskType, request));
|
||||
|
||||
Reference in New Issue
Block a user