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,183 @@
using EntKube.Clusters.Domain;
using EntKube.Clusters.Features.ClusterSettings;
using EntKube.Clusters.Infrastructure;
using EntKube.SharedKernel.Domain;
using FluentAssertions;
using Moq;
namespace EntKube.Clusters.Tests.Features;
/// <summary>
/// Cluster settings let operators view and edit configuration that lives on the
/// live cluster — like the list of allowed container registries in the Kyverno
/// restrict-image-registries policy. These tests verify the handler logic without
/// touching a real cluster. The actual Kubernetes interaction is behind an
/// IClusterSettingsReader abstraction so we can mock it in tests.
/// </summary>
public class ClusterSettingsTests
{
private readonly InMemoryClusterRepository repository;
private readonly Mock<IClusterSettingsReader> settingsReader;
private readonly Mock<IClusterSettingsWriter> settingsWriter;
private readonly GetClusterSettingsHandler getHandler;
private readonly UpdateClusterSettingsHandler updateHandler;
public ClusterSettingsTests()
{
repository = new InMemoryClusterRepository();
settingsReader = new Mock<IClusterSettingsReader>();
settingsWriter = new Mock<IClusterSettingsWriter>();
getHandler = new GetClusterSettingsHandler(repository, settingsReader.Object);
updateHandler = new UpdateClusterSettingsHandler(repository, settingsWriter.Object);
}
// ─── GET ───────────────────────────────────────────────────────────────
[Fact]
public async Task GetSettings_WithConnectedCluster_ReturnsSettings()
{
// Arrange — a connected cluster with some registries configured.
KubernetesCluster cluster = KubernetesCluster.Register(
"prod", "https://k8s.example.com:6443", "kubeconfig", Guid.NewGuid(), "ctx", Guid.NewGuid());
cluster.MarkConnected();
await repository.AddAsync(cluster);
List<string> registries = new() { "docker.io", "ghcr.io", "registry.k8s.io" };
settingsReader
.Setup(r => r.ReadAllowedRegistriesAsync(cluster, It.IsAny<CancellationToken>()))
.ReturnsAsync(registries);
// Act
Result<ClusterSettingsDto> result = await getHandler.HandleAsync(cluster.Id);
// Assert — the returned settings should include the registries from the live cluster.
result.IsSuccess.Should().BeTrue();
result.Value!.AllowedRegistries.Should().BeEquivalentTo(registries);
}
[Fact]
public async Task GetSettings_WithNonExistentCluster_ReturnsFailure()
{
// Act
Result<ClusterSettingsDto> result = await getHandler.HandleAsync(Guid.NewGuid());
// Assert
result.IsFailure.Should().BeTrue();
result.Error.Should().Contain("not found");
}
[Fact]
public async Task GetSettings_WithDisconnectedCluster_ReturnsFailure()
{
// Arrange — a cluster that hasn't been connected yet.
KubernetesCluster cluster = KubernetesCluster.Register(
"pending", "https://k8s.example.com:6443", "kubeconfig", Guid.NewGuid(), "ctx", Guid.NewGuid());
await repository.AddAsync(cluster);
// Act
Result<ClusterSettingsDto> result = await getHandler.HandleAsync(cluster.Id);
// Assert
result.IsFailure.Should().BeTrue();
result.Error.Should().Contain("connected");
}
// ─── UPDATE ────────────────────────────────────────────────────────────
[Fact]
public async Task UpdateSettings_WithValidRegistries_CallsWriter()
{
// Arrange — a connected cluster.
KubernetesCluster cluster = KubernetesCluster.Register(
"prod", "https://k8s.example.com:6443", "kubeconfig", Guid.NewGuid(), "ctx", Guid.NewGuid());
cluster.MarkConnected();
await repository.AddAsync(cluster);
List<string> newRegistries = new() { "docker.io", "ghcr.io", "myregistry.com" };
settingsWriter
.Setup(w => w.UpdateAllowedRegistriesAsync(cluster, newRegistries, It.IsAny<CancellationToken>()))
.ReturnsAsync(Result.Success());
UpdateClusterSettingsRequest request = new(AllowedRegistries: newRegistries);
// Act
Result result = await updateHandler.HandleAsync(cluster.Id, request);
// Assert
result.IsSuccess.Should().BeTrue();
settingsWriter.Verify(
w => w.UpdateAllowedRegistriesAsync(cluster, newRegistries, It.IsAny<CancellationToken>()),
Times.Once);
}
[Fact]
public async Task UpdateSettings_WithEmptyRegistries_ReturnsFailure()
{
// Arrange
KubernetesCluster cluster = KubernetesCluster.Register(
"prod", "https://k8s.example.com:6443", "kubeconfig", Guid.NewGuid(), "ctx", Guid.NewGuid());
cluster.MarkConnected();
await repository.AddAsync(cluster);
UpdateClusterSettingsRequest request = new(AllowedRegistries: new List<string>());
// Act
Result result = await updateHandler.HandleAsync(cluster.Id, request);
// Assert — can't have zero allowed registries, that would block all pods.
result.IsFailure.Should().BeTrue();
result.Error.Should().Contain("at least one");
}
[Fact]
public async Task UpdateSettings_WithNonExistentCluster_ReturnsFailure()
{
// Arrange
UpdateClusterSettingsRequest request = new(AllowedRegistries: new List<string> { "docker.io" });
// Act
Result result = await updateHandler.HandleAsync(Guid.NewGuid(), request);
// Assert
result.IsFailure.Should().BeTrue();
result.Error.Should().Contain("not found");
}
[Fact]
public async Task UpdateSettings_WithDisconnectedCluster_ReturnsFailure()
{
// Arrange
KubernetesCluster cluster = KubernetesCluster.Register(
"pending", "https://k8s.example.com:6443", "kubeconfig", Guid.NewGuid(), "ctx", Guid.NewGuid());
await repository.AddAsync(cluster);
UpdateClusterSettingsRequest request = new(AllowedRegistries: new List<string> { "docker.io" });
// Act
Result result = await updateHandler.HandleAsync(cluster.Id, request);
// Assert
result.IsFailure.Should().BeTrue();
result.Error.Should().Contain("connected");
}
}