213 lines
7.8 KiB
C#
213 lines
7.8 KiB
C#
using EntKube.Clusters.Domain;
|
|
using EntKube.Clusters.Features.AdoptCluster;
|
|
using EntKube.Clusters.Features.AdoptCluster.UninstallComponent;
|
|
using EntKube.Clusters.Infrastructure;
|
|
using EntKube.SharedKernel.Domain;
|
|
using FluentAssertions;
|
|
using Moq;
|
|
|
|
namespace EntKube.Clusters.Tests.Features;
|
|
|
|
public class UninstallComponentHandlerTests
|
|
{
|
|
private readonly InMemoryClusterRepository repository;
|
|
private readonly Mock<IComponentInstaller> kyvernoInstaller;
|
|
private readonly Mock<IComponentInstaller> monitoringInstaller;
|
|
private readonly UninstallComponentHandler handler;
|
|
|
|
public UninstallComponentHandlerTests()
|
|
{
|
|
repository = new InMemoryClusterRepository();
|
|
kyvernoInstaller = new Mock<IComponentInstaller>();
|
|
monitoringInstaller = new Mock<IComponentInstaller>();
|
|
|
|
kyvernoInstaller.Setup(i => i.ComponentName).Returns("kyverno");
|
|
monitoringInstaller.Setup(i => i.ComponentName).Returns("monitoring");
|
|
|
|
List<IComponentInstaller> installers = new() { kyvernoInstaller.Object, monitoringInstaller.Object };
|
|
handler = new UninstallComponentHandler(repository, installers, Microsoft.Extensions.Logging.Abstractions.NullLogger<UninstallComponentHandler>.Instance);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HandleAsync_ClusterNotFound_ReturnsFailure()
|
|
{
|
|
// Arrange — request an uninstall on a cluster that doesn't exist.
|
|
|
|
UninstallComponentRequest request = new("kyverno");
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await handler.HandleAsync(Guid.NewGuid(), request);
|
|
|
|
// Assert
|
|
|
|
result.IsFailure.Should().BeTrue();
|
|
result.Error.Should().Contain("not found");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HandleAsync_ClusterNotConnected_ReturnsFailure()
|
|
{
|
|
// Arrange — Cluster is Pending, can't uninstall from it.
|
|
|
|
KubernetesCluster cluster = KubernetesCluster.Register(
|
|
"pending-cluster", "https://k8s.dev:6443", "kubeconfig-data", Guid.NewGuid(), "ctx", Guid.NewGuid());
|
|
await repository.AddAsync(cluster);
|
|
|
|
UninstallComponentRequest request = new("kyverno");
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
|
|
|
|
// Assert
|
|
|
|
result.IsFailure.Should().BeTrue();
|
|
result.Error.Should().Contain("connected");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HandleAsync_UnknownComponent_ReturnsFailureWithAvailable()
|
|
{
|
|
// Arrange — Requesting an uninstall for a component that has no installer.
|
|
|
|
KubernetesCluster cluster = KubernetesCluster.Register(
|
|
"test-cluster", "https://k8s.test:6443", "kubeconfig-data", Guid.NewGuid(), "ctx", Guid.NewGuid());
|
|
cluster.MarkConnected();
|
|
await repository.AddAsync(cluster);
|
|
|
|
UninstallComponentRequest request = new("nonexistent-thing");
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
|
|
|
|
// Assert — Should list available installers.
|
|
|
|
result.IsFailure.Should().BeTrue();
|
|
result.Error.Should().Contain("kyverno");
|
|
result.Error.Should().Contain("monitoring");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HandleAsync_ValidComponent_DelegatesToInstallerUninstall()
|
|
{
|
|
// Arrange — Uninstall Kyverno from a connected cluster.
|
|
|
|
KubernetesCluster cluster = KubernetesCluster.Register(
|
|
"prod-cluster", "https://k8s.prod:6443", "kubeconfig-data", Guid.NewGuid(), "prod-ctx", Guid.NewGuid());
|
|
cluster.MarkConnected();
|
|
await repository.AddAsync(cluster);
|
|
|
|
kyvernoInstaller.Setup(i => i.UninstallAsync(cluster, It.IsAny<ComponentInstallOptions>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new InstallResult(
|
|
Success: true,
|
|
ComponentName: "kyverno",
|
|
Message: "Kyverno uninstalled successfully",
|
|
Actions: new List<string> { "Helm uninstall kyverno" }));
|
|
|
|
UninstallComponentRequest request = new("kyverno");
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
|
|
|
|
// Assert — Should succeed and return the uninstall result.
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
result.Value!.Success.Should().BeTrue();
|
|
result.Value.ComponentName.Should().Be("kyverno");
|
|
result.Value.Actions.Should().Contain("Helm uninstall kyverno");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HandleAsync_InstallerFails_ReturnsFailure()
|
|
{
|
|
// Arrange — The uninstall encounters an error.
|
|
|
|
KubernetesCluster cluster = KubernetesCluster.Register(
|
|
"flaky-cluster", "https://k8s.flaky:6443", "kubeconfig-data", Guid.NewGuid(), "ctx", Guid.NewGuid());
|
|
cluster.MarkConnected();
|
|
await repository.AddAsync(cluster);
|
|
|
|
kyvernoInstaller.Setup(i => i.UninstallAsync(cluster, It.IsAny<ComponentInstallOptions>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new InstallResult(
|
|
Success: false,
|
|
ComponentName: "kyverno",
|
|
Message: "Helm uninstall failed: resources still in use",
|
|
Actions: new List<string> { "Error: resources still in use" }));
|
|
|
|
UninstallComponentRequest request = new("kyverno");
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
|
|
|
|
// Assert
|
|
|
|
result.IsFailure.Should().BeTrue();
|
|
result.Error.Should().Contain("resources still in use");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HandleAsync_PassesOptionsToInstaller()
|
|
{
|
|
// Arrange — Uninstall monitoring with a custom namespace.
|
|
|
|
KubernetesCluster cluster = KubernetesCluster.Register(
|
|
"config-cluster", "https://k8s.cfg:6443", "kubeconfig-data", Guid.NewGuid(), "ctx", Guid.NewGuid());
|
|
cluster.MarkConnected();
|
|
await repository.AddAsync(cluster);
|
|
|
|
ComponentInstallOptions? capturedOptions = null;
|
|
|
|
monitoringInstaller.Setup(i => i.UninstallAsync(cluster, It.IsAny<ComponentInstallOptions>(), It.IsAny<CancellationToken>()))
|
|
.Callback<KubernetesCluster, ComponentInstallOptions, CancellationToken>((_, opts, _) => capturedOptions = opts)
|
|
.ReturnsAsync(new InstallResult(
|
|
Success: true,
|
|
ComponentName: "monitoring",
|
|
Message: "Monitoring uninstalled",
|
|
Actions: new List<string>()));
|
|
|
|
UninstallComponentRequest request = new("monitoring", Namespace: "custom-monitoring");
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
|
|
|
|
// Assert — The handler should forward the namespace to the installer.
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
capturedOptions.Should().NotBeNull();
|
|
capturedOptions!.Namespace.Should().Be("custom-monitoring");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HandleAsync_ComponentNameIsCaseInsensitive()
|
|
{
|
|
// Arrange — Use mixed case for the component name.
|
|
|
|
KubernetesCluster cluster = KubernetesCluster.Register(
|
|
"case-cluster", "https://k8s.case:6443", "kubeconfig-data", Guid.NewGuid(), "ctx", Guid.NewGuid());
|
|
cluster.MarkConnected();
|
|
await repository.AddAsync(cluster);
|
|
|
|
kyvernoInstaller.Setup(i => i.UninstallAsync(cluster, It.IsAny<ComponentInstallOptions>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new InstallResult(
|
|
Success: true,
|
|
ComponentName: "kyverno",
|
|
Message: "Uninstalled",
|
|
Actions: new List<string>()));
|
|
|
|
UninstallComponentRequest request = new("Kyverno");
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
|
|
|
|
// Assert
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
}
|
|
}
|