using EntKube.Clusters.Features.Certificates; using FluentAssertions; namespace EntKube.Clusters.Tests.Features; /// /// Tests for the Certificate Authority feature. Since the handler connects to /// a real K8s cluster, these tests focus on the domain models, request validation, /// and operation result patterns. Integration tests with a real cluster would go /// in a separate test suite. /// public class CertificateAuthorityTests { // ─── CaOperationResult ──────────────────────────────────────────────── [Fact] public void CaOperationResult_Ok_HasSuccessTrue() { // When we create a successful result, it should carry the message // and have Success = true. CaOperationResult result = CaOperationResult.Ok("CA created.", new List { "Step 1" }); result.Success.Should().BeTrue(); result.Message.Should().Be("CA created."); result.Actions.Should().ContainSingle().Which.Should().Be("Step 1"); } [Fact] public void CaOperationResult_Failure_HasSuccessFalse() { // A failure result should carry the error message with Success = false. CaOperationResult result = CaOperationResult.Failure("Something went wrong."); result.Success.Should().BeFalse(); result.Message.Should().Be("Something went wrong."); result.Actions.Should().BeEmpty(); } [Fact] public void CaOperationResult_Ok_WithNoActions_DefaultsToEmptyList() { // When no actions are provided, the list should default to empty. CaOperationResult result = CaOperationResult.Ok("Done."); result.Actions.Should().NotBeNull(); result.Actions.Should().BeEmpty(); } // ─── CertificateAuthorityInfo ───────────────────────────────────────── [Fact] public void CertificateAuthorityInfo_InternalCA_HasCorrectType() { // An internal CA should be classified as CaType.Internal and have // no domain restrictions. CertificateAuthorityInfo ca = new( Name: "platform-internal-ca", Type: CaType.Internal, SecretName: "platform-internal-ca-secret", Domains: new List(), IsExternal: false, InTrustBundle: true, Status: "Ready", NotBefore: DateTimeOffset.UtcNow.AddYears(-1), NotAfter: DateTimeOffset.UtcNow.AddYears(9), Organization: "EntKube Platform"); ca.Type.Should().Be(CaType.Internal); ca.Domains.Should().BeEmpty(); ca.IsExternal.Should().BeFalse(); ca.InTrustBundle.Should().BeTrue(); } [Fact] public void CertificateAuthorityInfo_DomainCA_HasDomains() { // A domain CA should carry the list of domains it's scoped to. CertificateAuthorityInfo ca = new( Name: "corp-ca", Type: CaType.Domain, SecretName: "corp-ca-secret", Domains: new List { "*.internal.corp.com", "*.svc.local" }, IsExternal: false, InTrustBundle: true, Status: "Ready", NotBefore: DateTimeOffset.UtcNow, NotAfter: DateTimeOffset.UtcNow.AddYears(5), Organization: "Corp Inc"); ca.Type.Should().Be(CaType.Domain); ca.Domains.Should().HaveCount(2); ca.Domains.Should().Contain("*.internal.corp.com"); } [Fact] public void CertificateAuthorityInfo_ExternalCA_FlagIsSet() { // An imported external CA should have IsExternal = true. CertificateAuthorityInfo ca = new( Name: "digicert-ca", Type: CaType.Domain, SecretName: "digicert-ca-secret", Domains: new List { "*.example.com" }, IsExternal: true, InTrustBundle: true, Status: "Ready", NotBefore: null, NotAfter: null, Organization: "DigiCert"); ca.IsExternal.Should().BeTrue(); } [Fact] public void CertificateAuthorityInfo_LetsEncrypt_HasAcmeType() { // A Let's Encrypt issuer is always external and publicly trusted. CertificateAuthorityInfo ca = new( Name: "letsencrypt-prod", Type: CaType.LetsEncrypt, SecretName: string.Empty, Domains: new List(), IsExternal: true, InTrustBundle: true, Status: "Ready", NotBefore: null, NotAfter: null, Organization: "Let's Encrypt"); ca.Type.Should().Be(CaType.LetsEncrypt); ca.IsExternal.Should().BeTrue(); } // ─── Request models ────────────────────────────────────────────────── [Fact] public void CreateInternalCARequest_DefaultValues_AreReasonable() { // The default values for an internal CA should be sensible defaults. CreateInternalCARequest request = new("my-ca"); request.Name.Should().Be("my-ca"); request.Organization.Should().Be("EntKube Platform"); request.DurationDays.Should().Be(3650); request.BundleName.Should().BeNull(); } [Fact] public void CreateDomainCARequest_WithDomains_CarriesThem() { // A domain CA request should carry the domains it covers. CreateDomainCARequest request = new( Name: "corp-ca", Domains: new List { "*.corp.com", "*.internal.corp.com" }); request.Domains.Should().HaveCount(2); request.TlsCert.Should().BeNull(); request.TlsKey.Should().BeNull(); } [Fact] public void CreateDomainCARequest_WithImportedCert_HasTlsFields() { // An imported domain CA should carry the base64-encoded cert and key. CreateDomainCARequest request = new( Name: "imported-ca", Domains: new List { "*.example.com" }, TlsCert: "base64cert", TlsKey: "base64key"); request.TlsCert.Should().Be("base64cert"); request.TlsKey.Should().Be("base64key"); } // ─── CaType enum ───────────────────────────────────────────────────── [Fact] public void CaType_HasExpectedValues() { // The enum should have exactly three values. Enum.GetValues().Should().HaveCount(3); Enum.GetValues().Should().Contain(CaType.Internal); Enum.GetValues().Should().Contain(CaType.Domain); Enum.GetValues().Should().Contain(CaType.LetsEncrypt); } }