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,181 @@
using EntKube.Clusters.Domain;
using EntKube.Clusters.Features.AdoptCluster;
using EntKube.Clusters.Features.AdoptCluster.DeployComponent;
using EntKube.Clusters.Infrastructure;
using EntKube.SharedKernel.Domain;
using FluentAssertions;
using Moq;
namespace EntKube.Clusters.Tests.Features;
public class DeployComponentHandlerTests
{
private readonly InMemoryClusterRepository repository;
private readonly Mock<IComponentInstaller> kyvernoInstaller;
private readonly Mock<IComponentInstaller> policiesInstaller;
private readonly DeployComponentHandler handler;
public DeployComponentHandlerTests()
{
repository = new InMemoryClusterRepository();
kyvernoInstaller = new Mock<IComponentInstaller>();
policiesInstaller = new Mock<IComponentInstaller>();
kyvernoInstaller.Setup(i => i.ComponentName).Returns("kyverno");
policiesInstaller.Setup(i => i.ComponentName).Returns("security-policies");
List<IComponentInstaller> installers = new() { kyvernoInstaller.Object, policiesInstaller.Object };
handler = new DeployComponentHandler(repository, installers, Microsoft.Extensions.Logging.Abstractions.NullLogger<DeployComponentHandler>.Instance);
}
[Fact]
public async Task HandleAsync_ClusterNotFound_ReturnsFailure()
{
// Arrange
DeployComponentRequest 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 deploy to it.
KubernetesCluster cluster = KubernetesCluster.Register(
"pending-cluster", "https://k8s.dev:6443", "kubeconfig-data", Guid.NewGuid(), "ctx", Guid.NewGuid());
await repository.AddAsync(cluster);
DeployComponentRequest 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 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);
DeployComponentRequest 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("security-policies");
}
[Fact]
public async Task HandleAsync_ValidComponent_DelegatesToInstaller()
{
// Arrange — Deploy Kyverno on 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.InstallAsync(cluster, It.IsAny<ComponentInstallOptions>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new InstallResult(
Success: true,
ComponentName: "kyverno",
Message: "Kyverno 3.3.4 installed successfully",
Actions: new List<string> { "Helm install/upgrade kyverno v3.3.4" }));
DeployComponentRequest request = new("kyverno", Version: "3.3.4");
// Act
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
// Assert — Should succeed and return installer output.
result.IsSuccess.Should().BeTrue();
result.Value!.Success.Should().BeTrue();
result.Value.ComponentName.Should().Be("kyverno");
result.Value.Actions.Should().Contain("Helm install/upgrade kyverno v3.3.4");
}
[Fact]
public async Task HandleAsync_InstallerFails_ReturnsFailure()
{
// Arrange — The installer encounters an error (e.g., helm timeout).
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.InstallAsync(cluster, It.IsAny<ComponentInstallOptions>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new InstallResult(
Success: false,
ComponentName: "kyverno",
Message: "Helm failed (exit 1): timed out waiting for condition",
Actions: new List<string> { "Error: timed out" }));
DeployComponentRequest request = new("kyverno");
// Act
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
// Assert
result.IsFailure.Should().BeTrue();
result.Error.Should().Contain("timed out");
}
[Fact]
public async Task HandleAsync_PassesOptionsToInstaller()
{
// Arrange — Deploy security policies with custom parameters.
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;
policiesInstaller.Setup(i => i.InstallAsync(cluster, It.IsAny<ComponentInstallOptions>(), It.IsAny<CancellationToken>()))
.Callback<KubernetesCluster, ComponentInstallOptions, CancellationToken>((_, opts, _) => capturedOptions = opts)
.ReturnsAsync(new InstallResult(true, "security-policies", "Done", new List<string>()));
DeployComponentRequest request = new(
"security-policies",
Parameters: new Dictionary<string, string> { ["validationAction"] = "Enforce" });
// Act
await handler.HandleAsync(cluster.Id, request);
// Assert — The installer received the custom parameters.
capturedOptions.Should().NotBeNull();
capturedOptions!.Parameters.Should().ContainKey("validationAction");
capturedOptions.Parameters!["validationAction"].Should().Be("Enforce");
}
}