too much for one commit
This commit is contained in:
305
tests/EntKube.Provisioning.Tests/Domain/RedisClusterTests.cs
Normal file
305
tests/EntKube.Provisioning.Tests/Domain/RedisClusterTests.cs
Normal file
@@ -0,0 +1,305 @@
|
||||
using EntKube.SharedKernel.Domain;
|
||||
using FluentAssertions;
|
||||
|
||||
namespace EntKube.Provisioning.Tests.Domain;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the RedisCluster aggregate root. This rich domain model tracks
|
||||
/// everything about a Redis instance managed by the OT-OPERATORS Redis Operator:
|
||||
/// replica count, storage, sentinel mode, persistence, and version lifecycle.
|
||||
///
|
||||
/// The OT-OPERATORS Redis Operator manages Redis, RedisCluster, RedisReplication,
|
||||
/// and RedisSentinel CRDs on Kubernetes. Each Redis instance can be standalone,
|
||||
/// replicated, or run with Sentinel for HA.
|
||||
/// </summary>
|
||||
public class RedisClusterTests
|
||||
{
|
||||
// ─── Creation ──────────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void Create_WithValidInputs_ReturnsClusterInProvisioningState()
|
||||
{
|
||||
// Arrange & Act — A platform admin provisions a new Redis instance
|
||||
// for shared caching on a specific Kubernetes cluster.
|
||||
|
||||
Guid environmentId = Guid.NewGuid();
|
||||
Guid clusterId = Guid.NewGuid();
|
||||
|
||||
Provisioning.Domain.RedisCluster redisCluster = Provisioning.Domain.RedisCluster.Create(
|
||||
environmentId: environmentId,
|
||||
clusterId: clusterId,
|
||||
name: "platform-redis",
|
||||
ns: "redis",
|
||||
redisVersion: "7.2.5",
|
||||
replicas: 3,
|
||||
storageSize: "10Gi",
|
||||
sentinelEnabled: true);
|
||||
|
||||
// Assert — The cluster starts in Provisioning state, waiting for the
|
||||
// reconciler to create the Redis CR.
|
||||
|
||||
redisCluster.Id.Should().NotBe(Guid.Empty);
|
||||
redisCluster.EnvironmentId.Should().Be(environmentId);
|
||||
redisCluster.ClusterId.Should().Be(clusterId);
|
||||
redisCluster.Name.Should().Be("platform-redis");
|
||||
redisCluster.Namespace.Should().Be("redis");
|
||||
redisCluster.RedisVersion.Should().Be("7.2.5");
|
||||
redisCluster.Replicas.Should().Be(3);
|
||||
redisCluster.StorageSize.Should().Be("10Gi");
|
||||
redisCluster.SentinelEnabled.Should().BeTrue();
|
||||
redisCluster.Status.Should().Be(Provisioning.Domain.RedisClusterStatus.Provisioning);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_WithEmptyName_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange & Act — The domain rejects invalid cluster names.
|
||||
|
||||
Action act = () => Provisioning.Domain.RedisCluster.Create(
|
||||
Guid.NewGuid(), Guid.NewGuid(), "", "ns", "7.2.5", 3, "10Gi", true);
|
||||
|
||||
// Assert
|
||||
|
||||
act.Should().Throw<ArgumentException>().WithParameterName("name");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_WithZeroReplicas_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange & Act — Must have at least 1 replica.
|
||||
|
||||
Action act = () => Provisioning.Domain.RedisCluster.Create(
|
||||
Guid.NewGuid(), Guid.NewGuid(), "cache", "ns", "7.2.5", 0, "10Gi", true);
|
||||
|
||||
// Assert
|
||||
|
||||
act.Should().Throw<ArgumentException>().WithParameterName("replicas");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_WithEmptyNamespace_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange & Act
|
||||
|
||||
Action act = () => Provisioning.Domain.RedisCluster.Create(
|
||||
Guid.NewGuid(), Guid.NewGuid(), "cache", "", "7.2.5", 3, "10Gi", true);
|
||||
|
||||
// Assert
|
||||
|
||||
act.Should().Throw<ArgumentException>().WithParameterName("ns");
|
||||
}
|
||||
|
||||
// ─── Adoption ──────────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void Adopt_FromExistingInstance_CreatesRunningCluster()
|
||||
{
|
||||
// Arrange & Act — When we adopt an existing Redis instance that's
|
||||
// already running on the K8s cluster, we create it in Running state.
|
||||
|
||||
Provisioning.Domain.RedisCluster redisCluster = Provisioning.Domain.RedisCluster.Adopt(
|
||||
environmentId: Guid.NewGuid(),
|
||||
clusterId: Guid.NewGuid(),
|
||||
name: "existing-redis",
|
||||
ns: "redis",
|
||||
redisVersion: "7.0.15",
|
||||
replicas: 3,
|
||||
storageSize: "5Gi",
|
||||
sentinelEnabled: true);
|
||||
|
||||
// Assert — Adopted clusters start in Running state.
|
||||
|
||||
redisCluster.Status.Should().Be(Provisioning.Domain.RedisClusterStatus.Running);
|
||||
redisCluster.Name.Should().Be("existing-redis");
|
||||
redisCluster.LastReconcileAt.Should().NotBeNull();
|
||||
}
|
||||
|
||||
// ─── Status transitions ────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void MarkRunning_TransitionsFromProvisioning()
|
||||
{
|
||||
// Arrange — A freshly created cluster in Provisioning state.
|
||||
|
||||
Provisioning.Domain.RedisCluster redisCluster = CreateTestCluster();
|
||||
|
||||
// Act — The reconciler confirmed the Redis instance is ready.
|
||||
|
||||
redisCluster.MarkRunning();
|
||||
|
||||
// Assert
|
||||
|
||||
redisCluster.Status.Should().Be(Provisioning.Domain.RedisClusterStatus.Running);
|
||||
redisCluster.LastReconcileAt.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MarkDegraded_SetsStatusToDegraded()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
Provisioning.Domain.RedisCluster redisCluster = CreateTestCluster();
|
||||
redisCluster.MarkRunning();
|
||||
|
||||
// Act — Reconciler detected an issue.
|
||||
|
||||
redisCluster.MarkDegraded("Replica pod not ready");
|
||||
|
||||
// Assert
|
||||
|
||||
redisCluster.Status.Should().Be(Provisioning.Domain.RedisClusterStatus.Degraded);
|
||||
redisCluster.StatusMessage.Should().Be("Replica pod not ready");
|
||||
}
|
||||
|
||||
// ─── Version upgrades ──────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void RequestUpgrade_WhenRunning_SetsUpgradingStatusAndTargetVersion()
|
||||
{
|
||||
// Arrange — A running cluster at version 7.0.15.
|
||||
|
||||
Provisioning.Domain.RedisCluster redisCluster = Provisioning.Domain.RedisCluster.Create(
|
||||
Guid.NewGuid(), Guid.NewGuid(), "cache", "ns", "7.0.15", 3, "10Gi", true);
|
||||
redisCluster.MarkRunning();
|
||||
|
||||
// Act — Platform admin requests upgrade to 7.2.5.
|
||||
|
||||
Result<string> result = redisCluster.RequestUpgrade("7.2.5");
|
||||
|
||||
// Assert — The cluster transitions to Upgrading state.
|
||||
|
||||
result.IsSuccess.Should().BeTrue();
|
||||
redisCluster.Status.Should().Be(Provisioning.Domain.RedisClusterStatus.Upgrading);
|
||||
redisCluster.TargetVersion.Should().Be("7.2.5");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RequestUpgrade_WhenNotRunning_ReturnsFailure()
|
||||
{
|
||||
// Arrange — A cluster still provisioning can't be upgraded.
|
||||
|
||||
Provisioning.Domain.RedisCluster redisCluster = CreateTestCluster();
|
||||
|
||||
// Act
|
||||
|
||||
Result<string> result = redisCluster.RequestUpgrade("7.2.5");
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsSuccess.Should().BeFalse();
|
||||
result.Error.Should().Contain("running");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RequestUpgrade_ToSameVersion_ReturnsFailure()
|
||||
{
|
||||
// Arrange — A cluster already at version 7.2.5.
|
||||
|
||||
Provisioning.Domain.RedisCluster redisCluster = Provisioning.Domain.RedisCluster.Create(
|
||||
Guid.NewGuid(), Guid.NewGuid(), "cache", "ns", "7.2.5", 3, "10Gi", true);
|
||||
redisCluster.MarkRunning();
|
||||
|
||||
// Act — Try to upgrade to the same version.
|
||||
|
||||
Result<string> result = redisCluster.RequestUpgrade("7.2.5");
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsSuccess.Should().BeFalse();
|
||||
result.Error.Should().Contain("same version");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompleteUpgrade_TransitionsToRunningWithNewVersion()
|
||||
{
|
||||
// Arrange — A cluster mid-upgrade.
|
||||
|
||||
Provisioning.Domain.RedisCluster redisCluster = Provisioning.Domain.RedisCluster.Create(
|
||||
Guid.NewGuid(), Guid.NewGuid(), "cache", "ns", "7.0.15", 3, "10Gi", true);
|
||||
redisCluster.MarkRunning();
|
||||
redisCluster.RequestUpgrade("7.2.5");
|
||||
|
||||
// Act — Reconciler confirmed upgrade completed.
|
||||
|
||||
redisCluster.CompleteUpgrade();
|
||||
|
||||
// Assert — Version updated, status back to Running.
|
||||
|
||||
redisCluster.RedisVersion.Should().Be("7.2.5");
|
||||
redisCluster.Status.Should().Be(Provisioning.Domain.RedisClusterStatus.Running);
|
||||
redisCluster.TargetVersion.Should().BeNull();
|
||||
}
|
||||
|
||||
// ─── Scaling ───────────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void ScaleReplicas_UpdatesReplicaCount()
|
||||
{
|
||||
// Arrange — A running 3-replica cluster.
|
||||
|
||||
Provisioning.Domain.RedisCluster redisCluster = CreateTestCluster();
|
||||
redisCluster.MarkRunning();
|
||||
|
||||
// Act — Scale up to 5 replicas for more read capacity.
|
||||
|
||||
Result<string> result = redisCluster.ScaleReplicas(5);
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsSuccess.Should().BeTrue();
|
||||
redisCluster.Replicas.Should().Be(5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScaleReplicas_ToZero_ReturnsFailure()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
Provisioning.Domain.RedisCluster redisCluster = CreateTestCluster();
|
||||
redisCluster.MarkRunning();
|
||||
|
||||
// Act
|
||||
|
||||
Result<string> result = redisCluster.ScaleReplicas(0);
|
||||
|
||||
// Assert
|
||||
|
||||
result.IsSuccess.Should().BeFalse();
|
||||
result.Error.Should().Contain("at least 1");
|
||||
}
|
||||
|
||||
// ─── Configuration ─────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void ConfigureSentinel_EnablesOrDisablesSentinel()
|
||||
{
|
||||
// Arrange — A cluster without sentinel.
|
||||
|
||||
Provisioning.Domain.RedisCluster redisCluster = Provisioning.Domain.RedisCluster.Create(
|
||||
Guid.NewGuid(), Guid.NewGuid(), "cache", "ns", "7.2.5", 3, "10Gi", false);
|
||||
redisCluster.MarkRunning();
|
||||
|
||||
// Act — Enable sentinel for HA.
|
||||
|
||||
redisCluster.ConfigureSentinel(true);
|
||||
|
||||
// Assert
|
||||
|
||||
redisCluster.SentinelEnabled.Should().BeTrue();
|
||||
}
|
||||
|
||||
// ─── Helper ────────────────────────────────────────────────────────────────
|
||||
|
||||
private static Provisioning.Domain.RedisCluster CreateTestCluster()
|
||||
{
|
||||
return Provisioning.Domain.RedisCluster.Create(
|
||||
Guid.NewGuid(),
|
||||
Guid.NewGuid(),
|
||||
"test-redis",
|
||||
"redis",
|
||||
"7.2.5",
|
||||
3,
|
||||
"10Gi",
|
||||
true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user