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 monitoringInstaller; private readonly Mock kyvernoInstaller; private readonly ConfigureComponentHandler handler; public ConfigureComponentHandlerTests() { repository = new InMemoryClusterRepository(); monitoringInstaller = new Mock(); monitoringInstaller.Setup(i => i.ComponentName).Returns("monitoring"); kyvernoInstaller = new Mock(); kyvernoInstaller.Setup(i => i.ComponentName).Returns("kyverno"); List 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(), It.IsAny())) .ReturnsAsync(new InstallResult(true, "monitoring", "Reconfigured", new List { "retention=90d" })); ConfigureComponentRequest request = new("monitoring", Values: new Dictionary { ["retention"] = "90d" }); // Act Result 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(c => c.Values["retention"] == "90d"), It.IsAny()), Times.Once); } [Fact] public async Task HandleAsync_WithNonExistentCluster_ReturnsFailure() { // Arrange ConfigureComponentRequest request = new("monitoring", Values: new Dictionary { ["retention"] = "90d" }); // Act Result 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 { ["retention"] = "90d" }); // Act Result 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 { ["foo"] = "bar" }); // Act Result 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(), It.IsAny())) .ReturnsAsync(new InstallResult(false, "kyverno", "Helm upgrade failed: timeout", new List { "Error: timeout" })); ConfigureComponentRequest request = new("kyverno", Values: new Dictionary { ["admissionReplicas"] = "4" }); // Act Result 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(), It.IsAny())) .ReturnsAsync(new InstallResult(true, "monitoring", "Done", new List())); ConfigureComponentRequest request = new("monitoring", Namespace: "custom-monitoring", Values: new Dictionary { ["retention"] = "7d" }); // Act Result result = await handler.HandleAsync(cluster.Id, request); // Assert result.IsSuccess.Should().BeTrue(); monitoringInstaller.Verify( i => i.ConfigureAsync(cluster, It.Is(c => c.Namespace == "custom-monitoring"), It.IsAny()), Times.Once); } }