Add unit tests for KubernetesCluster, Tenant, ServiceInstance, and RegisterClusterHandler
Some checks failed
Build EntKube / build (push) Failing after 27s
Package Helm Chart / lint (push) Failing after 36s
Package Helm Chart / package (push) Has been skipped

- Implement tests for KubernetesCluster including registration, connectivity status, and error handling.
- Create tests for Tenant creation, member management, and status changes.
- Add tests for ServiceInstance provisioning and state management.
- Introduce RegisterClusterHandler tests to validate registration requests and error scenarios.
- Set up project files for new test projects with necessary dependencies.
This commit is contained in:
Nils Blomgren
2026-05-05 11:44:36 +02:00
parent 461fa36a46
commit a96dd33039
203 changed files with 2876 additions and 553 deletions

View File

@@ -0,0 +1,90 @@
using EntKube.Clusters.Domain;
using FluentAssertions;
namespace EntKube.Clusters.Tests.Domain;
public class KubernetesClusterTests
{
[Fact]
public void Register_WithValidInputs_CreatesClusterInPendingState()
{
// Arrange & Act — A tenant admin registers a new cluster with its name and API URL.
KubernetesCluster cluster = KubernetesCluster.Register(
"production-eu",
"https://k8s.example.com:6443",
"secret-ref-123");
// Assert — The cluster should be created with a unique ID and start in Pending state
// because we haven't verified connectivity yet.
cluster.Id.Should().NotBe(Guid.Empty);
cluster.Name.Should().Be("production-eu");
cluster.ApiServerUrl.Should().Be("https://k8s.example.com:6443");
cluster.KubeConfigSecret.Should().Be("secret-ref-123");
cluster.Status.Should().Be(ClusterStatus.Pending);
cluster.RegisteredAt.Should().BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromSeconds(5));
}
[Fact]
public void Register_WithEmptyName_ThrowsArgumentException()
{
// Arrange & Act — Attempting to register a cluster without a name should fail
// because every cluster needs a human-readable identifier.
Action act = () => KubernetesCluster.Register("", "https://k8s.example.com", null);
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("name");
}
[Fact]
public void Register_WithEmptyApiServerUrl_ThrowsArgumentException()
{
// Arrange & Act — Without an API URL, we can't communicate with the cluster.
Action act = () => KubernetesCluster.Register("my-cluster", "", null);
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("apiServerUrl");
}
[Fact]
public void MarkConnected_SetsStatusToConnected()
{
// Arrange — A freshly registered cluster in Pending state.
KubernetesCluster cluster = KubernetesCluster.Register("test", "https://api.test", null);
// Act — The health check confirms the cluster is reachable.
cluster.MarkConnected();
// Assert
cluster.Status.Should().Be(ClusterStatus.Connected);
cluster.LastHealthCheckAt.Should().NotBeNull();
}
[Fact]
public void MarkUnreachable_SetsStatusToUnreachable()
{
// Arrange
KubernetesCluster cluster = KubernetesCluster.Register("test", "https://api.test", null);
cluster.MarkConnected();
// Act — The health check fails.
cluster.MarkUnreachable();
// Assert
cluster.Status.Should().Be(ClusterStatus.Unreachable);
cluster.LastHealthCheckAt.Should().NotBeNull();
}
}

View File

@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="FluentAssertions" Version="8.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\EntKube.Clusters\EntKube.Clusters.csproj" />
<ProjectReference Include="..\..\src\EntKube.SharedKernel\EntKube.SharedKernel.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,74 @@
using EntKube.Clusters.Domain;
using EntKube.Clusters.Features.RegisterCluster;
using EntKube.Clusters.Infrastructure;
using EntKube.SharedKernel.Domain;
using FluentAssertions;
namespace EntKube.Clusters.Tests.Features;
public class RegisterClusterHandlerTests
{
private readonly RegisterClusterHandler handler;
private readonly InMemoryClusterRepository repository;
public RegisterClusterHandlerTests()
{
repository = new InMemoryClusterRepository();
handler = new RegisterClusterHandler(repository);
}
[Fact]
public async Task HandleAsync_WithValidRequest_ReturnsSuccessWithClusterId()
{
// Arrange — A valid registration request with all required fields.
RegisterClusterRequest request = new("production-eu", "https://k8s.example.com:6443", "secret-ref");
// Act
Result<Guid> result = await handler.HandleAsync(request);
// Assert — The handler should succeed and the cluster should be persisted.
result.IsSuccess.Should().BeTrue();
result.Value.Should().NotBe(Guid.Empty);
KubernetesCluster? persisted = await repository.GetByIdAsync(result.Value!);
persisted.Should().NotBeNull();
persisted!.Name.Should().Be("production-eu");
}
[Fact]
public async Task HandleAsync_WithEmptyName_ReturnsFailure()
{
// Arrange — Missing cluster name.
RegisterClusterRequest request = new("", "https://k8s.example.com:6443", null);
// Act
Result<Guid> result = await handler.HandleAsync(request);
// Assert
result.IsFailure.Should().BeTrue();
result.Error.Should().Contain("name");
}
[Fact]
public async Task HandleAsync_WithEmptyApiUrl_ReturnsFailure()
{
// Arrange — Missing API server URL.
RegisterClusterRequest request = new("my-cluster", "", null);
// Act
Result<Guid> result = await handler.HandleAsync(request);
// Assert
result.IsFailure.Should().BeTrue();
result.Error.Should().Contain("API server URL");
}
}