too much for one commit

This commit is contained in:
Nils Blomgren
2026-05-13 14:01:32 +02:00
parent a96dd33039
commit 328d494530
394 changed files with 98104 additions and 78 deletions

View File

@@ -0,0 +1,212 @@
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();
}
}