114 lines
4.6 KiB
C#
114 lines
4.6 KiB
C#
using Sdt.Config;
|
|
using Sdt.Core;
|
|
using Sdt.Runner;
|
|
using Xunit;
|
|
|
|
namespace DevTool.Tests;
|
|
|
|
public sealed class ToolchainManagerServiceTests
|
|
{
|
|
[Fact]
|
|
public async Task ProbeConfiguredTools_IncludesToolchainAndWorkflowRequirements()
|
|
{
|
|
var config = new DevToolConfig
|
|
{
|
|
Toolchains = new ToolchainConfig
|
|
{
|
|
Python = new PythonToolchain(),
|
|
Node = new NodeToolchain { PackageManager = "npm" }
|
|
},
|
|
Workflows =
|
|
[
|
|
new WorkflowDefinition
|
|
{
|
|
Id = "build",
|
|
Label = "Build",
|
|
Steps =
|
|
[
|
|
new WorkflowStep
|
|
{
|
|
Id = "s1",
|
|
Label = "S1",
|
|
Requires = [new ToolRequirement { Tool = "dotnet" }]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
};
|
|
|
|
var service = new ToolchainManagerService(new AvailableProbe(), new NoOpInstaller());
|
|
var probes = await service.ProbeConfiguredToolsAsync(config, Directory.GetCurrentDirectory());
|
|
|
|
var tools = probes.Select(p => p.Tool).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
Assert.Contains("python", tools);
|
|
Assert.Contains("node", tools);
|
|
Assert.Contains("npm", tools);
|
|
Assert.Contains("dotnet", tools);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AutoFixMissingTools_AttemptsInstallAndVerifies()
|
|
{
|
|
var config = new DevToolConfig
|
|
{
|
|
Toolchains = new ToolchainConfig
|
|
{
|
|
Node = new NodeToolchain { PackageManager = "npm" }
|
|
}
|
|
};
|
|
|
|
var probe = new SequenceProbe();
|
|
var installer = new SuccessInstaller();
|
|
var service = new ToolchainManagerService(probe, installer);
|
|
|
|
var results = await service.AutoFixMissingToolsAsync(
|
|
config,
|
|
Directory.GetCurrentDirectory(),
|
|
(_, _) => Task.FromResult(true),
|
|
(_, _) => { });
|
|
|
|
Assert.Contains(results, r => r.Tool.Equals("npm", StringComparison.OrdinalIgnoreCase) && r.Success);
|
|
}
|
|
|
|
private sealed class AvailableProbe : IToolProbe
|
|
{
|
|
public Task<ProbeResult> ProbeAsync(string tool, string projectRoot, DevToolConfig? config = null, IReadOnlyDictionary<string, string>? envOverrides = null, CancellationToken cancellationToken = default)
|
|
=> Task.FromResult(new ProbeResult(tool, true, Version: "1.0.0"));
|
|
}
|
|
|
|
private sealed class SequenceProbe : IToolProbe
|
|
{
|
|
private readonly Dictionary<string, int> _count = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
public Task<ProbeResult> ProbeAsync(string tool, string projectRoot, DevToolConfig? config = null, IReadOnlyDictionary<string, string>? envOverrides = null, CancellationToken cancellationToken = default)
|
|
{
|
|
_count.TryGetValue(tool, out var c);
|
|
_count[tool] = c + 1;
|
|
if (tool.Equals("node", StringComparison.OrdinalIgnoreCase))
|
|
return Task.FromResult(new ProbeResult(tool, true, Version: "1.0.0"));
|
|
|
|
// npm: first probe missing, later available (after install).
|
|
var available = c > 0;
|
|
return Task.FromResult(new ProbeResult(tool, available, Version: available ? "1.0.0" : null));
|
|
}
|
|
}
|
|
|
|
private sealed class NoOpInstaller : IPrereqInstaller
|
|
{
|
|
public Task<InstallPlan> GetInstallPlanAsync(string tool, string projectRoot, DevToolConfig? config = null, CancellationToken cancellationToken = default)
|
|
=> Task.FromResult(new InstallPlan(tool, true, "noop", [new InstallCommand("echo", ["ok"])]));
|
|
|
|
public Task<RunResult> RunInstallAsync(InstallCommand command, string projectRoot, Action<string, bool> onOutput, IReadOnlyDictionary<string, string>? envOverrides = null, CancellationToken cancellationToken = default)
|
|
=> Task.FromResult(new RunResult(0, TimeSpan.Zero));
|
|
}
|
|
|
|
private sealed class SuccessInstaller : IPrereqInstaller
|
|
{
|
|
public Task<InstallPlan> GetInstallPlanAsync(string tool, string projectRoot, DevToolConfig? config = null, CancellationToken cancellationToken = default)
|
|
=> Task.FromResult(new InstallPlan(tool, true, "install", [new InstallCommand("echo", ["install"])]));
|
|
|
|
public Task<RunResult> RunInstallAsync(InstallCommand command, string projectRoot, Action<string, bool> onOutput, IReadOnlyDictionary<string, string>? envOverrides = null, CancellationToken cancellationToken = default)
|
|
=> Task.FromResult(new RunResult(0, TimeSpan.FromMilliseconds(5)));
|
|
}
|
|
}
|