too much for one commit
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
using EntKube.Clusters.Domain;
|
||||
using EntKube.Clusters.Features.AdoptCluster;
|
||||
using EntKube.Clusters.Infrastructure;
|
||||
using EntKube.SharedKernel.Domain;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
|
||||
namespace EntKube.Clusters.Tests.Features;
|
||||
|
||||
public class AdoptClusterHandlerTests
|
||||
{
|
||||
private readonly InMemoryClusterRepository repository;
|
||||
private readonly Mock<IAdoptionCheck> kyvernoCheck;
|
||||
private readonly Mock<IAdoptionCheck> securityPoliciesCheck;
|
||||
private readonly AdoptClusterHandler handler;
|
||||
|
||||
public AdoptClusterHandlerTests()
|
||||
{
|
||||
repository = new InMemoryClusterRepository();
|
||||
kyvernoCheck = new Mock<IAdoptionCheck>();
|
||||
securityPoliciesCheck = new Mock<IAdoptionCheck>();
|
||||
|
||||
kyvernoCheck.Setup(c => c.ComponentName).Returns("kyverno");
|
||||
securityPoliciesCheck.Setup(c => c.ComponentName).Returns("security-policies");
|
||||
|
||||
List<IAdoptionCheck> checks = new() { kyvernoCheck.Object, securityPoliciesCheck.Object };
|
||||
handler = new AdoptClusterHandler(repository, checks, NullLogger<AdoptClusterHandler>.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAsync_ClusterNotFound_ReturnsFailure()
|
||||
{
|
||||
// Arrange — Attempt to adopt a cluster that doesn't exist.
|
||||
|
||||
Guid unknownId = Guid.NewGuid();
|
||||
|
||||
// Act
|
||||
|
||||
Result<AdoptionReport> result = await handler.HandleAsync(unknownId);
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsFailure.Should().BeTrue();
|
||||
result.Error.Should().Contain("not found");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAsync_ClusterNotConnected_ReturnsFailure()
|
||||
{
|
||||
// Arrange — The cluster exists but is still Pending (not yet reachable).
|
||||
|
||||
KubernetesCluster cluster = KubernetesCluster.Register(
|
||||
"dev-cluster", "https://k8s.dev:6443", "kubeconfig-data", Guid.NewGuid(), "dev-ctx", Guid.NewGuid());
|
||||
await repository.AddAsync(cluster);
|
||||
|
||||
// Act
|
||||
|
||||
Result<AdoptionReport> result = await handler.HandleAsync(cluster.Id);
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsFailure.Should().BeTrue();
|
||||
result.Error.Should().Contain("connected");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAsync_AllComponentsInstalled_ReturnsReadyReport()
|
||||
{
|
||||
// Arrange — A connected cluster where all components report Installed.
|
||||
|
||||
KubernetesCluster cluster = KubernetesCluster.Register(
|
||||
"prod-cluster", "https://k8s.prod:6443", "kubeconfig-data", Guid.NewGuid(), "prod-ctx", Guid.NewGuid());
|
||||
cluster.MarkConnected();
|
||||
await repository.AddAsync(cluster);
|
||||
|
||||
kyvernoCheck.Setup(c => c.CheckAsync(cluster, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ComponentCheckResult("kyverno", ComponentStatus.Installed,
|
||||
new List<string> { "Pod running: kyverno-admission-controller-0" },
|
||||
new List<string>()));
|
||||
|
||||
securityPoliciesCheck.Setup(c => c.CheckAsync(cluster, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ComponentCheckResult("security-policies", ComponentStatus.Installed,
|
||||
new List<string> { "Policy present: deny-loadbalancer-services" },
|
||||
new List<string>()));
|
||||
|
||||
// Act
|
||||
|
||||
Result<AdoptionReport> result = await handler.HandleAsync(cluster.Id);
|
||||
|
||||
// Assert — All components installed means Ready.
|
||||
|
||||
result.IsSuccess.Should().BeTrue();
|
||||
result.Value!.Status.Should().Be(AdoptionReadiness.Ready);
|
||||
result.Value.Components.Should().HaveCount(2);
|
||||
result.Value.Components.Should().AllSatisfy(c => c.Status.Should().Be(ComponentStatus.Installed));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAsync_OneComponentNotInstalled_ReturnsNotReady()
|
||||
{
|
||||
// Arrange — Kyverno is missing entirely while policies exist (impossible
|
||||
// in practice, but tests the aggregation logic).
|
||||
|
||||
KubernetesCluster cluster = KubernetesCluster.Register(
|
||||
"bare-cluster", "https://k8s.bare:6443", "kubeconfig-data", Guid.NewGuid(), "bare-ctx", Guid.NewGuid());
|
||||
cluster.MarkConnected();
|
||||
await repository.AddAsync(cluster);
|
||||
|
||||
kyvernoCheck.Setup(c => c.CheckAsync(cluster, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ComponentCheckResult("kyverno", ComponentStatus.NotInstalled,
|
||||
new List<string>(),
|
||||
new List<string> { "No Kyverno pods found" }));
|
||||
|
||||
securityPoliciesCheck.Setup(c => c.CheckAsync(cluster, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ComponentCheckResult("security-policies", ComponentStatus.NotInstalled,
|
||||
new List<string>(),
|
||||
new List<string> { "restrict-image-registries", "deny-loadbalancer-services" }));
|
||||
|
||||
// Act
|
||||
|
||||
Result<AdoptionReport> result = await handler.HandleAsync(cluster.Id);
|
||||
|
||||
// Assert — Any component NotInstalled means the cluster is NotReady.
|
||||
|
||||
result.IsSuccess.Should().BeTrue();
|
||||
result.Value!.Status.Should().Be(AdoptionReadiness.NotReady);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAsync_OneComponentDegraded_ReturnsPartiallyReady()
|
||||
{
|
||||
// Arrange — Kyverno is installed but security policies are only partially deployed.
|
||||
|
||||
KubernetesCluster cluster = KubernetesCluster.Register(
|
||||
"partial-cluster", "https://k8s.partial:6443", "kubeconfig-data", Guid.NewGuid(), "partial-ctx", Guid.NewGuid());
|
||||
cluster.MarkConnected();
|
||||
await repository.AddAsync(cluster);
|
||||
|
||||
kyvernoCheck.Setup(c => c.CheckAsync(cluster, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ComponentCheckResult("kyverno", ComponentStatus.Installed,
|
||||
new List<string> { "Pod running: kyverno-admission-controller-0" },
|
||||
new List<string>()));
|
||||
|
||||
securityPoliciesCheck.Setup(c => c.CheckAsync(cluster, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ComponentCheckResult("security-policies", ComponentStatus.Degraded,
|
||||
new List<string> { "Policy present: deny-loadbalancer-services" },
|
||||
new List<string> { "restrict-image-registries", "require-seccomp-runtime-default" }));
|
||||
|
||||
// Act
|
||||
|
||||
Result<AdoptionReport> result = await handler.HandleAsync(cluster.Id);
|
||||
|
||||
// Assert — Degraded components mean PartiallyReady.
|
||||
|
||||
result.IsSuccess.Should().BeTrue();
|
||||
result.Value!.Status.Should().Be(AdoptionReadiness.PartiallyReady);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAsync_RunsAllChecksInParallel()
|
||||
{
|
||||
// Arrange — Verify that both checks are called for a connected cluster.
|
||||
|
||||
KubernetesCluster cluster = KubernetesCluster.Register(
|
||||
"test-cluster", "https://k8s.test:6443", "kubeconfig-data", Guid.NewGuid(), "test-ctx", Guid.NewGuid());
|
||||
cluster.MarkConnected();
|
||||
await repository.AddAsync(cluster);
|
||||
|
||||
kyvernoCheck.Setup(c => c.CheckAsync(cluster, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ComponentCheckResult("kyverno", ComponentStatus.Installed, new List<string>(), new List<string>()));
|
||||
|
||||
securityPoliciesCheck.Setup(c => c.CheckAsync(cluster, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ComponentCheckResult("security-policies", ComponentStatus.Installed, new List<string>(), new List<string>()));
|
||||
|
||||
// Act
|
||||
|
||||
await handler.HandleAsync(cluster.Id);
|
||||
|
||||
// Assert — Both checks were invoked.
|
||||
|
||||
kyvernoCheck.Verify(c => c.CheckAsync(cluster, It.IsAny<CancellationToken>()), Times.Once);
|
||||
securityPoliciesCheck.Verify(c => c.CheckAsync(cluster, It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user