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,197 @@
using EntKube.Clusters.Domain;
using EntKube.Clusters.Features.AdoptCluster;
using EntKube.Clusters.Features.AdoptCluster.ConfigureComponent;
using EntKube.Clusters.Infrastructure;
using EntKube.SharedKernel.Domain;
using FluentAssertions;
using Moq;
namespace EntKube.Clusters.Tests.Features;
public class ConfigureComponentHandlerTests
{
private readonly InMemoryClusterRepository repository;
private readonly Mock<IComponentInstaller> monitoringInstaller;
private readonly Mock<IComponentInstaller> kyvernoInstaller;
private readonly ConfigureComponentHandler handler;
public ConfigureComponentHandlerTests()
{
repository = new InMemoryClusterRepository();
monitoringInstaller = new Mock<IComponentInstaller>();
monitoringInstaller.Setup(i => i.ComponentName).Returns("monitoring");
kyvernoInstaller = new Mock<IComponentInstaller>();
kyvernoInstaller.Setup(i => i.ComponentName).Returns("kyverno");
List<IComponentInstaller> installers = new() { monitoringInstaller.Object, kyvernoInstaller.Object };
handler = new ConfigureComponentHandler(repository, installers);
}
[Fact]
public async Task HandleAsync_WithValidComponent_CallsConfigureAsync()
{
// Arrange — A connected cluster and a request to change Prometheus retention.
KubernetesCluster cluster = KubernetesCluster.Register(
"prod-cluster", "https://k8s.example.com:6443", "kubeconfig", Guid.NewGuid(), "ctx", Guid.NewGuid());
cluster.MarkConnected();
await repository.AddAsync(cluster);
monitoringInstaller
.Setup(i => i.ConfigureAsync(cluster, It.IsAny<ComponentConfiguration>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new InstallResult(true, "monitoring", "Reconfigured", new List<string> { "retention=90d" }));
ConfigureComponentRequest request = new("monitoring", Values: new Dictionary<string, string>
{
["retention"] = "90d"
});
// Act
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
// Assert
result.IsSuccess.Should().BeTrue();
result.Value!.Success.Should().BeTrue();
result.Value.Message.Should().Be("Reconfigured");
monitoringInstaller.Verify(
i => i.ConfigureAsync(cluster, It.Is<ComponentConfiguration>(c => c.Values["retention"] == "90d"), It.IsAny<CancellationToken>()),
Times.Once);
}
[Fact]
public async Task HandleAsync_WithNonExistentCluster_ReturnsFailure()
{
// Arrange
ConfigureComponentRequest request = new("monitoring", Values: new Dictionary<string, string>
{
["retention"] = "90d"
});
// 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_WithDisconnectedCluster_ReturnsFailure()
{
// Arrange — Cluster that isn't connected can't be configured.
KubernetesCluster cluster = KubernetesCluster.Register(
"offline-cluster", "https://k8s.example.com:6443", "kubeconfig", Guid.NewGuid(), "ctx", Guid.NewGuid());
await repository.AddAsync(cluster);
ConfigureComponentRequest request = new("monitoring", Values: new Dictionary<string, string>
{
["retention"] = "90d"
});
// 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_WithUnknownComponent_ReturnsFailure()
{
// Arrange — Request an installer that doesn't exist.
KubernetesCluster cluster = KubernetesCluster.Register(
"prod-cluster", "https://k8s.example.com:6443", "kubeconfig", Guid.NewGuid(), "ctx", Guid.NewGuid());
cluster.MarkConnected();
await repository.AddAsync(cluster);
ConfigureComponentRequest request = new("nonexistent-thing", Values: new Dictionary<string, string>
{
["foo"] = "bar"
});
// Act
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
// Assert
result.IsFailure.Should().BeTrue();
result.Error.Should().Contain("No installer found");
result.Error.Should().Contain("monitoring");
result.Error.Should().Contain("kyverno");
}
[Fact]
public async Task HandleAsync_WhenConfigureFails_ReturnsFailure()
{
// Arrange — The installer reports a failure.
KubernetesCluster cluster = KubernetesCluster.Register(
"broken-cluster", "https://k8s.example.com:6443", "kubeconfig", Guid.NewGuid(), "ctx", Guid.NewGuid());
cluster.MarkConnected();
await repository.AddAsync(cluster);
kyvernoInstaller
.Setup(i => i.ConfigureAsync(cluster, It.IsAny<ComponentConfiguration>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new InstallResult(false, "kyverno", "Helm upgrade failed: timeout", new List<string> { "Error: timeout" }));
ConfigureComponentRequest request = new("kyverno", Values: new Dictionary<string, string>
{
["admissionReplicas"] = "4"
});
// Act
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
// Assert
result.IsFailure.Should().BeTrue();
result.Error.Should().Contain("Helm upgrade failed");
}
[Fact]
public async Task HandleAsync_PassesNamespaceToConfiguration()
{
// Arrange — Verify the namespace from the request reaches the installer.
KubernetesCluster cluster = KubernetesCluster.Register(
"prod-cluster", "https://k8s.example.com:6443", "kubeconfig", Guid.NewGuid(), "ctx", Guid.NewGuid());
cluster.MarkConnected();
await repository.AddAsync(cluster);
monitoringInstaller
.Setup(i => i.ConfigureAsync(cluster, It.IsAny<ComponentConfiguration>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new InstallResult(true, "monitoring", "Done", new List<string>()));
ConfigureComponentRequest request = new("monitoring", Namespace: "custom-monitoring", Values: new Dictionary<string, string>
{
["retention"] = "7d"
});
// Act
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
// Assert
result.IsSuccess.Should().BeTrue();
monitoringInstaller.Verify(
i => i.ConfigureAsync(cluster, It.Is<ComponentConfiguration>(c => c.Namespace == "custom-monitoring"), It.IsAny<CancellationToken>()),
Times.Once);
}
}