Files
Entkube/tests/EntKube.Provisioning.Tests/Domain/PrometheusStackTests.cs
2026-05-13 14:01:32 +02:00

276 lines
9.2 KiB
C#

using EntKube.Provisioning.Domain;
using FluentAssertions;
namespace EntKube.Provisioning.Tests.Domain;
public class PrometheusStackTests
{
private readonly Guid clusterId = Guid.NewGuid();
private readonly Guid environmentId = Guid.NewGuid();
// ─── Creation ───────────────────────────────────────────────────────
[Fact]
public void Create_WithValidInputs_CreatesStackInPendingState()
{
// Arrange & Act — An operator provisions a new Prometheus stack on a cluster.
// Prometheus is per-cluster: each cluster gets its own metrics collector.
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
// Assert — The stack should be in a Pending state, with sensible defaults
// for Prometheus retention, replicas, and Alertmanager.
stack.Id.Should().NotBe(Guid.Empty);
stack.ClusterId.Should().Be(clusterId);
stack.EnvironmentId.Should().Be(environmentId);
stack.Name.Should().Be("prod-prometheus");
stack.Namespace.Should().Be("monitoring");
stack.Status.Should().Be(PrometheusStackStatus.Pending);
stack.CreatedAt.Should().BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromSeconds(5));
// Prometheus defaults
stack.Prometheus.Should().NotBeNull();
stack.Prometheus.Retention.Should().Be("30d");
stack.Prometheus.Replicas.Should().Be(2);
stack.Prometheus.StorageSize.Should().Be("50Gi");
// Alertmanager defaults
stack.Alertmanager.Should().NotBeNull();
stack.Alertmanager.Enabled.Should().BeTrue();
stack.Alertmanager.Replicas.Should().Be(2);
}
[Fact]
public void Create_WithEmptyName_ThrowsArgumentException()
{
// Arrange & Act — A Prometheus stack must have a name.
Action act = () => PrometheusStack.Create(clusterId, environmentId, "", "monitoring");
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("name");
}
[Fact]
public void Create_WithEmptyNamespace_ThrowsArgumentException()
{
// Arrange & Act — A Prometheus stack must have a namespace.
Action act = () => PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "");
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("ns");
}
[Fact]
public void Create_WithEmptyClusterId_ThrowsArgumentException()
{
// Arrange & Act — A Prometheus stack must belong to a cluster.
Action act = () => PrometheusStack.Create(Guid.Empty, environmentId, "prod-prometheus", "monitoring");
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("clusterId");
}
[Fact]
public void Create_WithEmptyEnvironmentId_ThrowsArgumentException()
{
// Arrange & Act — A Prometheus stack must belong to an environment.
Action act = () => PrometheusStack.Create(clusterId, Guid.Empty, "prod-prometheus", "monitoring");
// Assert
act.Should().Throw<ArgumentException>()
.WithParameterName("environmentId");
}
// ─── Prometheus Configuration ─────────────────────────────────────
[Fact]
public void ConfigurePrometheus_UpdatesRetentionAndReplicas()
{
// Arrange — Start with a Prometheus stack using defaults.
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
// Act — The operator increases retention to 90 days and scales to 3 replicas.
stack.ConfigurePrometheus(retention: "90d", replicas: 3, storageSize: "100Gi", retentionSize: "90GB");
// Assert — Prometheus settings should reflect the new configuration.
stack.Prometheus.Retention.Should().Be("90d");
stack.Prometheus.Replicas.Should().Be(3);
stack.Prometheus.StorageSize.Should().Be("100Gi");
stack.Prometheus.RetentionSize.Should().Be("90GB");
}
[Fact]
public void ConfigurePrometheus_WithInvalidReplicas_ThrowsArgumentException()
{
// Arrange
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
// Act & Assert — Zero replicas doesn't make sense for Prometheus.
Action act = () => stack.ConfigurePrometheus(replicas: 0);
act.Should().Throw<ArgumentException>()
.WithParameterName("replicas");
}
// ─── Alertmanager Configuration ─────────────────────────────────
[Fact]
public void ConfigureAlertmanager_UpdatesReplicasAndEnablement()
{
// Arrange
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
// Act — The operator disables Alertmanager for a dev cluster.
stack.ConfigureAlertmanager(enabled: false, replicas: 1);
// Assert
stack.Alertmanager.Enabled.Should().BeFalse();
stack.Alertmanager.Replicas.Should().Be(1);
}
// ─── Ingress (Prometheus + Alertmanager) ────────────────────────
[Fact]
public void ConfigureIngress_SetsPrometheusAndAlertmanagerRouting()
{
// Arrange — Publish Prometheus and Alertmanager via Gateway API.
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
// Act
IngressConfiguration ingress = new(
Enabled: true,
Provider: IngressProvider.GatewayApi,
PrometheusHostname: "prometheus.prod01.example.com",
AlertmanagerHostname: "alertmanager.prod01.example.com",
TlsEnabled: true,
TlsCertificateMode: TlsCertificateMode.Manual,
TlsSecretName: "monitoring-tls");
stack.ConfigureIngress(ingress);
// Assert
stack.Ingress.Should().NotBeNull();
stack.Ingress!.Enabled.Should().BeTrue();
stack.Ingress.Provider.Should().Be(IngressProvider.GatewayApi);
stack.Ingress.PrometheusHostname.Should().Be("prometheus.prod01.example.com");
stack.Ingress.AlertmanagerHostname.Should().Be("alertmanager.prod01.example.com");
stack.Ingress.TlsEnabled.Should().BeTrue();
stack.Ingress.TlsCertificateMode.Should().Be(TlsCertificateMode.Manual);
stack.Ingress.TlsSecretName.Should().Be("monitoring-tls");
}
[Fact]
public void ConfigureIngress_WithCertManager_DoesNotRequireSecretName()
{
// Arrange
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
// Act — cert-manager with Let's Encrypt handles certificates automatically.
IngressConfiguration ingress = new(
Enabled: true,
Provider: IngressProvider.GatewayApi,
PrometheusHostname: "prometheus.example.com",
AlertmanagerHostname: "alertmanager.example.com",
TlsEnabled: true,
TlsCertificateMode: TlsCertificateMode.CertManager,
TlsSecretName: null,
ClusterIssuer: "letsencrypt-prod");
stack.ConfigureIngress(ingress);
// Assert
stack.Ingress!.TlsCertificateMode.Should().Be(TlsCertificateMode.CertManager);
stack.Ingress.TlsSecretName.Should().BeNull();
stack.Ingress.ClusterIssuer.Should().Be("letsencrypt-prod");
}
[Fact]
public void DisableIngress_ClearsIngressConfig()
{
// Arrange
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
IngressConfiguration ingress = new(
Enabled: true,
Provider: IngressProvider.GatewayApi,
PrometheusHostname: "prometheus.example.com",
AlertmanagerHostname: null,
TlsEnabled: true,
TlsCertificateMode: TlsCertificateMode.Manual,
TlsSecretName: "tls-secret");
stack.ConfigureIngress(ingress);
// Act
stack.DisableIngress();
// Assert
stack.Ingress.Should().BeNull();
}
// ─── Lifecycle ──────────────────────────────────────────────────
[Fact]
public void MarkRunning_SetsStatusToRunning()
{
// Arrange
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
// Act
stack.MarkRunning();
// Assert
stack.Status.Should().Be(PrometheusStackStatus.Running);
}
[Fact]
public void MarkDegraded_SetsStatusToDegraded()
{
// Arrange
PrometheusStack stack = PrometheusStack.Create(clusterId, environmentId, "prod-prometheus", "monitoring");
// Act
stack.MarkDegraded();
// Assert
stack.Status.Should().Be(PrometheusStackStatus.Degraded);
}
}