using EntKube.Clusters.Features.AdoptCluster.Components; using FluentAssertions; using k8s.Models; namespace EntKube.Clusters.Tests.Features; /// /// Tests for the shared CNPG database provisioner that builds Job and Secret /// manifests for creating databases on a CNPG cluster. These are pure manifest /// builders — no Kubernetes API calls — so they're fully unit-testable. /// public class CnpgDatabaseProvisionerTests { [Fact] public void GetPrimaryHost_ReturnsCorrectFqdn() { // The CNPG primary service follows the naming convention // {clusterName}-rw.{namespace}.svc.cluster.local. string host = CnpgDatabaseProvisioner.GetPrimaryHost("shared-pg", "cnpg-system"); host.Should().Be("shared-pg-rw.cnpg-system.svc.cluster.local"); } [Fact] public void BuildCreateDatabaseJob_TargetsPrimaryService() { // The Job should connect to the CNPG cluster's read-write primary // service to execute the CREATE ROLE and CREATE DATABASE commands. V1Job job = CnpgDatabaseProvisioner.BuildCreateDatabaseJob( "shared-pg", "cnpg-system", "gitea", "gitea", "secret123"); // The psql command should target {clusterName}-rw. string args = job.Spec.Template.Spec.Containers[0].Args[0]; args.Should().Contain("psql -h shared-pg-rw"); } [Fact] public void BuildCreateDatabaseJob_UsesSuperuserSecret() { // The Job needs the superuser password from the CNPG cluster's // automatically created {clusterName}-superuser secret. V1Job job = CnpgDatabaseProvisioner.BuildCreateDatabaseJob( "shared-pg", "cnpg-system", "gitea", "gitea", "secret123"); V1EnvVar passwordEnv = job.Spec.Template.Spec.Containers[0].Env[0]; passwordEnv.Name.Should().Be("POSTGRES_PASSWORD"); passwordEnv.ValueFrom!.SecretKeyRef!.Name.Should().Be("shared-pg-superuser"); passwordEnv.ValueFrom.SecretKeyRef.Key.Should().Be("password"); } [Fact] public void BuildCreateDatabaseJob_SetsCorrectNamespace() { // The Job must run in the same namespace as the CNPG cluster // so it can resolve the primary service by short name. V1Job job = CnpgDatabaseProvisioner.BuildCreateDatabaseJob( "shared-pg", "cnpg-system", "harbor", "harbor", "pass"); job.Metadata.NamespaceProperty.Should().Be("cnpg-system"); } [Fact] public void BuildCreateDatabaseJob_HasManagementLabels() { // The Job should be labeled for EntKube management and discoverability. V1Job job = CnpgDatabaseProvisioner.BuildCreateDatabaseJob( "shared-pg", "cnpg-system", "keycloak", "keycloak", "pass"); job.Metadata.Labels["app.kubernetes.io/managed-by"].Should().Be("entkube"); job.Metadata.Labels["entkube.io/operation"].Should().Be("create-database"); } [Fact] public void BuildCreateDatabaseJob_UsesIdempotentSql() { // The SQL script should use IF NOT EXISTS patterns so re-running // the Job on an already-provisioned database doesn't fail. V1Job job = CnpgDatabaseProvisioner.BuildCreateDatabaseJob( "shared-pg", "cnpg-system", "gitea", "gitea", "secret123"); string args = job.Spec.Template.Spec.Containers[0].Args[0]; args.Should().Contain("WHERE NOT EXISTS (SELECT FROM pg_roles"); args.Should().Contain("WHERE NOT EXISTS (SELECT FROM pg_database"); } [Fact] public void BuildCredentialsSecret_ContainsAllConnectionDetails() { // The credentials secret should contain host, database, username, // and password so the consuming service can connect. V1Secret secret = CnpgDatabaseProvisioner.BuildCredentialsSecret( "gitea-db-credentials", "gitea", "shared-pg-rw.cnpg-system.svc.cluster.local", "gitea", "gitea", "secret123"); secret.StringData["host"].Should().Be("shared-pg-rw.cnpg-system.svc.cluster.local"); secret.StringData["database"].Should().Be("gitea"); secret.StringData["username"].Should().Be("gitea"); secret.StringData["password"].Should().Be("secret123"); } [Fact] public void BuildCredentialsSecret_HasManagementLabels() { // The secret should be labeled for EntKube management. V1Secret secret = CnpgDatabaseProvisioner.BuildCredentialsSecret( "harbor-db-credentials", "harbor", "shared-pg-rw.cnpg-system.svc.cluster.local", "harbor", "harbor", "pass"); secret.Metadata.Labels["app.kubernetes.io/managed-by"].Should().Be("entkube"); secret.Metadata.Labels["entkube.io/purpose"].Should().Be("database-credentials"); } [Fact] public void BuildCredentialsSecret_SetsCorrectNamespace() { // The secret must be created in the target service's namespace, // not the CNPG cluster's namespace. V1Secret secret = CnpgDatabaseProvisioner.BuildCredentialsSecret( "keycloak-db-credentials", "keycloak", "shared-pg-rw.cnpg-system.svc.cluster.local", "keycloak", "keycloak", "pass"); secret.Metadata.NamespaceProperty.Should().Be("keycloak"); } }