audit work, doctors work
This commit is contained in:
@@ -0,0 +1,471 @@
|
||||
// <copyright file="DoctorCommandGroupTests.cs" company="Stella Operations">
|
||||
// Copyright (c) Stella Operations. Licensed under AGPL-3.0-or-later.
|
||||
// </copyright>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// DoctorCommandGroupTests.cs
|
||||
// Sprint: SPRINT_20260112_001_006_CLI_doctor_command
|
||||
// Task: CLI-DOC-001 - Unit tests for stella doctor command
|
||||
// Description: Tests for the doctor command structure and options.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.CommandLine;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Cli.Commands;
|
||||
using StellaOps.Doctor.DependencyInjection;
|
||||
using StellaOps.Doctor.Engine;
|
||||
using StellaOps.Doctor.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Cli.Tests.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for DoctorCommandGroup and related functionality.
|
||||
/// </summary>
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class DoctorCommandGroupTests
|
||||
{
|
||||
#region Command Structure Tests
|
||||
|
||||
[Fact]
|
||||
public void BuildDoctorCommand_ReturnsCommandWithCorrectName()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
command.Name.Should().Be("doctor");
|
||||
command.Description.Should().Contain("diagnostic");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildDoctorCommand_HasRunSubcommand()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var runCommand = command.Subcommands.FirstOrDefault(c => c.Name == "run");
|
||||
runCommand.Should().NotBeNull();
|
||||
runCommand!.Description.Should().Contain("Execute");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildDoctorCommand_HasListSubcommand()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var listCommand = command.Subcommands.FirstOrDefault(c => c.Name == "list");
|
||||
listCommand.Should().NotBeNull();
|
||||
listCommand!.Description.Should().Contain("List");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Run Subcommand Options Tests
|
||||
|
||||
[Fact]
|
||||
public void RunCommand_HasFormatOption()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
var runCommand = command.Subcommands.First(c => c.Name == "run");
|
||||
|
||||
// Assert
|
||||
var formatOption = runCommand.Options.FirstOrDefault(o =>
|
||||
o.Name == "format" || o.Aliases.Contains("--format") || o.Aliases.Contains("-f"));
|
||||
formatOption.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunCommand_HasModeOption()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
var runCommand = command.Subcommands.First(c => c.Name == "run");
|
||||
|
||||
// Assert
|
||||
var modeOption = runCommand.Options.FirstOrDefault(o =>
|
||||
o.Name == "mode" || o.Aliases.Contains("--mode") || o.Aliases.Contains("-m"));
|
||||
modeOption.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunCommand_HasCategoryOption()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
var runCommand = command.Subcommands.First(c => c.Name == "run");
|
||||
|
||||
// Assert
|
||||
var categoryOption = runCommand.Options.FirstOrDefault(o =>
|
||||
o.Name == "category" || o.Aliases.Contains("--category") || o.Aliases.Contains("-c"));
|
||||
categoryOption.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunCommand_HasTagOption()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
var runCommand = command.Subcommands.First(c => c.Name == "run");
|
||||
|
||||
// Assert
|
||||
var tagOption = runCommand.Options.FirstOrDefault(o =>
|
||||
o.Name == "tag" || o.Aliases.Contains("--tag") || o.Aliases.Contains("-t"));
|
||||
tagOption.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunCommand_HasCheckOption()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
var runCommand = command.Subcommands.First(c => c.Name == "run");
|
||||
|
||||
// Assert - System.CommandLine stores option name without leading dashes
|
||||
var checkOption = runCommand.Options.FirstOrDefault(o =>
|
||||
o.Name == "--check" || o.Name == "check" || o.Aliases.Any(a => a == "--check"));
|
||||
checkOption.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunCommand_HasParallelOption()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
var runCommand = command.Subcommands.First(c => c.Name == "run");
|
||||
|
||||
// Assert
|
||||
var parallelOption = runCommand.Options.FirstOrDefault(o =>
|
||||
o.Name == "parallel" || o.Aliases.Contains("--parallel") || o.Aliases.Contains("-p"));
|
||||
parallelOption.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunCommand_HasTimeoutOption()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
var runCommand = command.Subcommands.First(c => c.Name == "run");
|
||||
|
||||
// Assert - System.CommandLine stores option name without leading dashes
|
||||
var timeoutOption = runCommand.Options.FirstOrDefault(o =>
|
||||
o.Name == "timeout" || o.Name == "--timeout" || o.Aliases.Contains("--timeout"));
|
||||
timeoutOption.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunCommand_HasOutputOption()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
var runCommand = command.Subcommands.First(c => c.Name == "run");
|
||||
|
||||
// Assert
|
||||
var outputOption = runCommand.Options.FirstOrDefault(o =>
|
||||
o.Name == "output" || o.Aliases.Contains("--output") || o.Aliases.Contains("-o"));
|
||||
outputOption.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunCommand_HasFailOnWarnOption()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
var runCommand = command.Subcommands.First(c => c.Name == "run");
|
||||
|
||||
// Assert - System.CommandLine stores option name without leading dashes
|
||||
var failOnWarnOption = runCommand.Options.FirstOrDefault(o =>
|
||||
o.Name == "fail-on-warn" || o.Name == "--fail-on-warn" || o.Aliases.Contains("--fail-on-warn"));
|
||||
failOnWarnOption.Should().NotBeNull();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region List Subcommand Options Tests
|
||||
|
||||
[Fact]
|
||||
public void ListCommand_HasCategoryOption()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
var listCommand = command.Subcommands.First(c => c.Name == "list");
|
||||
|
||||
// Assert
|
||||
var categoryOption = listCommand.Options.FirstOrDefault(o =>
|
||||
o.Name == "category" || o.Aliases.Contains("--category") || o.Aliases.Contains("-c"));
|
||||
categoryOption.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListCommand_HasTagOption()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
var listCommand = command.Subcommands.First(c => c.Name == "list");
|
||||
|
||||
// Assert
|
||||
var tagOption = listCommand.Options.FirstOrDefault(o =>
|
||||
o.Name == "tag" || o.Aliases.Contains("--tag") || o.Aliases.Contains("-t"));
|
||||
tagOption.Should().NotBeNull();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Exit Codes Tests
|
||||
|
||||
[Fact]
|
||||
public void CliExitCodes_SuccessIsZero()
|
||||
{
|
||||
CliExitCodes.Success.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CliExitCodes_DoctorFailedIs100()
|
||||
{
|
||||
CliExitCodes.DoctorFailed.Should().Be(100);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CliExitCodes_DoctorWarningIs101()
|
||||
{
|
||||
CliExitCodes.DoctorWarning.Should().Be(101);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CliExitCodes_DoctorCodesAreUnique()
|
||||
{
|
||||
var codes = new[]
|
||||
{
|
||||
CliExitCodes.DoctorFailed,
|
||||
CliExitCodes.DoctorWarning
|
||||
};
|
||||
|
||||
codes.Should().OnlyHaveUniqueItems();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CliExitCodes_DoctorCodesDoNotOverlapWithGeneralCodes()
|
||||
{
|
||||
var generalCodes = new[]
|
||||
{
|
||||
CliExitCodes.Success,
|
||||
CliExitCodes.InputFileNotFound,
|
||||
CliExitCodes.MissingRequiredOption,
|
||||
CliExitCodes.ServiceNotConfigured,
|
||||
CliExitCodes.SigningFailed,
|
||||
CliExitCodes.VerificationFailed,
|
||||
CliExitCodes.PolicyViolation,
|
||||
CliExitCodes.FileNotFound,
|
||||
CliExitCodes.GeneralError,
|
||||
CliExitCodes.NotImplemented,
|
||||
CliExitCodes.UnexpectedError
|
||||
};
|
||||
|
||||
var doctorCodes = new[]
|
||||
{
|
||||
CliExitCodes.DoctorFailed,
|
||||
CliExitCodes.DoctorWarning
|
||||
};
|
||||
|
||||
generalCodes.Should().NotIntersectWith(doctorCodes);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DoctorRunOptions Tests
|
||||
|
||||
[Fact]
|
||||
public void DoctorRunOptions_DefaultModeIsNormal()
|
||||
{
|
||||
var options = new DoctorRunOptions();
|
||||
|
||||
options.Mode.Should().Be(DoctorRunMode.Normal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoctorRunOptions_DefaultParallelismIsFour()
|
||||
{
|
||||
var options = new DoctorRunOptions();
|
||||
|
||||
options.Parallelism.Should().Be(4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoctorRunOptions_DefaultTimeoutIsThirtySeconds()
|
||||
{
|
||||
var options = new DoctorRunOptions();
|
||||
|
||||
options.Timeout.Should().Be(TimeSpan.FromSeconds(30));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoctorRunOptions_DefaultCategoriesIsNull()
|
||||
{
|
||||
var options = new DoctorRunOptions();
|
||||
|
||||
options.Categories.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoctorRunOptions_DefaultTagsIsNull()
|
||||
{
|
||||
var options = new DoctorRunOptions();
|
||||
|
||||
options.Tags.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoctorRunOptions_DefaultCheckIdsIsNull()
|
||||
{
|
||||
var options = new DoctorRunOptions();
|
||||
|
||||
options.CheckIds.Should().BeNull();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DoctorSeverity Tests
|
||||
|
||||
[Fact]
|
||||
public void DoctorSeverity_PassIsZero()
|
||||
{
|
||||
((int)DoctorSeverity.Pass).Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoctorSeverity_InfoIsOne()
|
||||
{
|
||||
((int)DoctorSeverity.Info).Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoctorSeverity_WarnIsTwo()
|
||||
{
|
||||
((int)DoctorSeverity.Warn).Should().Be(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoctorSeverity_FailIsThree()
|
||||
{
|
||||
((int)DoctorSeverity.Fail).Should().Be(3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoctorSeverity_SkipIsFour()
|
||||
{
|
||||
((int)DoctorSeverity.Skip).Should().Be(4);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DoctorRunMode Tests
|
||||
|
||||
[Fact]
|
||||
public void DoctorRunMode_QuickIsZero()
|
||||
{
|
||||
((int)DoctorRunMode.Quick).Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoctorRunMode_NormalIsOne()
|
||||
{
|
||||
((int)DoctorRunMode.Normal).Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoctorRunMode_FullIsTwo()
|
||||
{
|
||||
((int)DoctorRunMode.Full).Should().Be(2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
private static IServiceProvider CreateTestServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
// Add configuration
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>())
|
||||
.Build();
|
||||
services.AddSingleton<IConfiguration>(configuration);
|
||||
|
||||
// Add time provider
|
||||
services.AddSingleton(TimeProvider.System);
|
||||
|
||||
// Add logging
|
||||
services.AddLogging();
|
||||
|
||||
// Add doctor services
|
||||
services.AddDoctorEngine();
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../StellaOps.Cli/StellaOps.Cli.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Doctor/StellaOps.Doctor.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Cli.Plugins.Aoc/StellaOps.Cli.Plugins.Aoc.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Cli.Plugins.NonCore/StellaOps.Cli.Plugins.NonCore.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Cli.Plugins.Symbols/StellaOps.Cli.Plugins.Symbols.csproj" />
|
||||
|
||||
Reference in New Issue
Block a user