547 lines
23 KiB
C#
547 lines
23 KiB
C#
using EntKube.Clusters.Domain;
|
|
using EntKube.Clusters.Features.AdoptCluster;
|
|
using EntKube.Clusters.Features.AdoptCluster.ConfigureComponent;
|
|
using EntKube.Clusters.Features.AdoptCluster.DeployComponent;
|
|
using EntKube.Clusters.Infrastructure;
|
|
using EntKube.SharedKernel.Domain;
|
|
using FluentAssertions;
|
|
using Moq;
|
|
|
|
namespace EntKube.Clusters.Tests.Features;
|
|
|
|
/// <summary>
|
|
/// Tests for the Keycloak identity provider component. Keycloak provides:
|
|
///
|
|
/// 1. **Multi-realm identity management** — each tenant or environment gets its own realm
|
|
/// 2. **BankID identity provider** — Swedish BankID as a login option per realm
|
|
/// 3. **Custom theming** — per-realm logos, colors, and backgrounds
|
|
/// 4. **Federation** — SAML, OIDC, and social providers per realm
|
|
///
|
|
/// Keycloak is deployed via the Bitnami-free official Keycloak Operator or Helm chart.
|
|
/// Realms, providers, and themes are configured post-install via the Keycloak Admin API.
|
|
/// </summary>
|
|
public class KeycloakTests
|
|
{
|
|
private readonly InMemoryClusterRepository repository;
|
|
private readonly Mock<IComponentInstaller> keycloakInstaller;
|
|
|
|
public KeycloakTests()
|
|
{
|
|
repository = new InMemoryClusterRepository();
|
|
|
|
keycloakInstaller = new Mock<IComponentInstaller>();
|
|
keycloakInstaller.Setup(i => i.ComponentName).Returns("keycloak");
|
|
}
|
|
|
|
// ─── Keycloak Installation ────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task Keycloak_Install_DeploysWithOperatorAndDefaultRealm()
|
|
{
|
|
// Arrange — Deploy Keycloak with its operator and a default master realm.
|
|
// The operator manages Keycloak instances via CRDs (Keycloak, KeycloakRealm).
|
|
|
|
KubernetesCluster cluster = CreateConnectedCluster();
|
|
await repository.AddAsync(cluster);
|
|
|
|
keycloakInstaller
|
|
.Setup(i => i.InstallAsync(cluster, It.IsAny<ComponentInstallOptions>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new InstallResult(
|
|
Success: true,
|
|
ComponentName: "keycloak",
|
|
Message: "Keycloak 26.0 installed with operator",
|
|
Actions: new List<string>
|
|
{
|
|
"Ensured namespace 'keycloak'",
|
|
"Helm install keycloak-operator v26.0.0",
|
|
"Created Keycloak CR 'platform-keycloak'",
|
|
"Keycloak available at auth.internal.corp.com",
|
|
"BankID provider plugin deployed"
|
|
}));
|
|
|
|
DeployComponentHandler handler = new(repository, new List<IComponentInstaller>
|
|
{
|
|
keycloakInstaller.Object
|
|
}, Microsoft.Extensions.Logging.Abstractions.NullLogger<DeployComponentHandler>.Instance);
|
|
|
|
DeployComponentRequest request = new("keycloak", Version: "26.0.0",
|
|
Parameters: new Dictionary<string, string>
|
|
{
|
|
["keycloakDomain"] = "auth.internal.corp.com",
|
|
["adminPassword"] = "admin123",
|
|
["storageClass"] = "longhorn",
|
|
["databaseType"] = "postgres",
|
|
["installBankIdPlugin"] = "true"
|
|
});
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
|
|
|
|
// Assert — Keycloak operator and instance deployed.
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
result.Value!.Actions.Should().Contain(a => a.Contains("keycloak-operator"));
|
|
result.Value.Actions.Should().Contain(a => a.Contains("Keycloak CR"));
|
|
result.Value.Actions.Should().Contain(a => a.Contains("BankID"));
|
|
|
|
keycloakInstaller.Verify(i => i.InstallAsync(
|
|
cluster,
|
|
It.Is<ComponentInstallOptions>(o =>
|
|
o.Parameters!["keycloakDomain"] == "auth.internal.corp.com" &&
|
|
o.Parameters["installBankIdPlugin"] == "true"),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Keycloak_Install_WithoutDatabase_ReportsFailure()
|
|
{
|
|
// Arrange — Keycloak requires a database (internal or CloudNativePG).
|
|
// If no database is available, the install should fail gracefully.
|
|
|
|
KubernetesCluster cluster = CreateConnectedCluster();
|
|
await repository.AddAsync(cluster);
|
|
|
|
keycloakInstaller
|
|
.Setup(i => i.InstallAsync(cluster, It.IsAny<ComponentInstallOptions>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new InstallResult(
|
|
Success: false,
|
|
ComponentName: "keycloak",
|
|
Message: "Keycloak requires a PostgreSQL database (CloudNativePG or external)",
|
|
Actions: new List<string> { "Prerequisite check failed: no database available" }));
|
|
|
|
DeployComponentHandler handler = new(repository, new List<IComponentInstaller>
|
|
{
|
|
keycloakInstaller.Object
|
|
}, Microsoft.Extensions.Logging.Abstractions.NullLogger<DeployComponentHandler>.Instance);
|
|
|
|
DeployComponentRequest request = new("keycloak",
|
|
Parameters: new Dictionary<string, string>
|
|
{
|
|
["keycloakDomain"] = "auth.internal.corp.com"
|
|
});
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await handler.HandleAsync(cluster.Id, request);
|
|
|
|
// Assert
|
|
|
|
result.IsFailure.Should().BeTrue();
|
|
result.Error.Should().Contain("database");
|
|
}
|
|
|
|
// ─── Realm Management ─────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task Keycloak_Configure_CreateRealm_WithCustomTheme()
|
|
{
|
|
// Arrange — Create a new realm with a custom name, logo, background
|
|
// image, and brand colors. The realm is fully isolated and can have
|
|
// its own identity providers, users, and theme.
|
|
|
|
KubernetesCluster cluster = CreateConnectedCluster();
|
|
await repository.AddAsync(cluster);
|
|
|
|
keycloakInstaller
|
|
.Setup(i => i.ConfigureAsync(cluster, It.IsAny<ComponentConfiguration>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new InstallResult(
|
|
Success: true,
|
|
ComponentName: "keycloak",
|
|
Message: "Realm 'tenant-alpha' created with custom theme",
|
|
Actions: new List<string>
|
|
{
|
|
"Created realm 'tenant-alpha'",
|
|
"Set realm display name to 'Alpha Corp'",
|
|
"Deployed custom theme 'alpha-theme'",
|
|
"Theme: logo uploaded, primary color #1A73E8, background set",
|
|
"Realm login page configured with custom branding"
|
|
}));
|
|
|
|
List<IComponentInstaller> installers = new() { keycloakInstaller.Object };
|
|
ConfigureComponentHandler configHandler = new(repository, installers);
|
|
|
|
ConfigureComponentRequest configRequest = new(
|
|
ComponentName: "keycloak",
|
|
Values: new Dictionary<string, string>
|
|
{
|
|
["createRealm"] = "tenant-alpha",
|
|
["realmDisplayName"] = "Alpha Corp",
|
|
["themeLogo"] = "https://cdn.alpha.com/logo.png",
|
|
["themeBackground"] = "https://cdn.alpha.com/bg.jpg",
|
|
["themePrimaryColor"] = "#1A73E8",
|
|
["themeSecondaryColor"] = "#FFFFFF",
|
|
["themeLoginTitle"] = "Welcome to Alpha Corp"
|
|
});
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await configHandler.HandleAsync(cluster.Id, configRequest);
|
|
|
|
// Assert — Realm created with full theming.
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
result.Value!.Actions.Should().Contain(a => a.Contains("realm 'tenant-alpha'"));
|
|
result.Value.Actions.Should().Contain(a => a.Contains("Alpha Corp"));
|
|
result.Value.Actions.Should().Contain(a => a.Contains("theme"));
|
|
result.Value.Actions.Should().Contain(a => a.Contains("#1A73E8"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Keycloak_Configure_AddBankIdProvider_ToRealm()
|
|
{
|
|
// Arrange — Add a Swedish BankID identity provider to a specific realm.
|
|
// BankID requires a service certificate (P12) and API endpoint configuration.
|
|
|
|
KubernetesCluster cluster = CreateConnectedCluster();
|
|
await repository.AddAsync(cluster);
|
|
|
|
keycloakInstaller
|
|
.Setup(i => i.ConfigureAsync(cluster, It.IsAny<ComponentConfiguration>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new InstallResult(
|
|
Success: true,
|
|
ComponentName: "keycloak",
|
|
Message: "BankID provider added to realm 'tenant-alpha'",
|
|
Actions: new List<string>
|
|
{
|
|
"Configured BankID identity provider in realm 'tenant-alpha'",
|
|
"Stored BankID service certificate in Keycloak keystore",
|
|
"BankID endpoint: https://appapi2.bankid.com/rp/v6.0",
|
|
"BankID provider enabled as login option"
|
|
}));
|
|
|
|
List<IComponentInstaller> installers = new() { keycloakInstaller.Object };
|
|
ConfigureComponentHandler configHandler = new(repository, installers);
|
|
|
|
ConfigureComponentRequest configRequest = new(
|
|
ComponentName: "keycloak",
|
|
Values: new Dictionary<string, string>
|
|
{
|
|
["realmName"] = "tenant-alpha",
|
|
["addProvider"] = "bankid",
|
|
["bankIdEnvironment"] = "production",
|
|
["bankIdCertificateP12"] = "MIIEvgIBADANBg...", // base64 P12
|
|
["bankIdCertificatePassword"] = "cert-password",
|
|
["bankIdDisplayName"] = "Logga in med BankID"
|
|
});
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await configHandler.HandleAsync(cluster.Id, configRequest);
|
|
|
|
// Assert — BankID configured as identity provider.
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
result.Value!.Actions.Should().Contain(a => a.Contains("BankID") && a.Contains("tenant-alpha"));
|
|
result.Value.Actions.Should().Contain(a => a.Contains("certificate"));
|
|
result.Value.Actions.Should().Contain(a => a.Contains("appapi2.bankid.com"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Keycloak_Configure_AddOIDCProvider_ToRealm()
|
|
{
|
|
// Arrange — Add a generic OIDC identity provider (e.g., Azure AD, Google)
|
|
// to a realm, independently from other realms.
|
|
|
|
KubernetesCluster cluster = CreateConnectedCluster();
|
|
await repository.AddAsync(cluster);
|
|
|
|
keycloakInstaller
|
|
.Setup(i => i.ConfigureAsync(cluster, It.IsAny<ComponentConfiguration>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new InstallResult(
|
|
Success: true,
|
|
ComponentName: "keycloak",
|
|
Message: "OIDC provider 'azure-ad' added to realm 'tenant-beta'",
|
|
Actions: new List<string>
|
|
{
|
|
"Configured OIDC identity provider 'azure-ad' in realm 'tenant-beta'",
|
|
"Discovery URL: https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration",
|
|
"Client ID configured",
|
|
"Provider enabled as login option"
|
|
}));
|
|
|
|
List<IComponentInstaller> installers = new() { keycloakInstaller.Object };
|
|
ConfigureComponentHandler configHandler = new(repository, installers);
|
|
|
|
ConfigureComponentRequest configRequest = new(
|
|
ComponentName: "keycloak",
|
|
Values: new Dictionary<string, string>
|
|
{
|
|
["realmName"] = "tenant-beta",
|
|
["addProvider"] = "oidc",
|
|
["providerAlias"] = "azure-ad",
|
|
["providerDisplayName"] = "Sign in with Microsoft",
|
|
["oidcDiscoveryUrl"] = "https://login.microsoftonline.com/abc123/.well-known/openid-configuration",
|
|
["oidcClientId"] = "client-id-here",
|
|
["oidcClientSecret"] = "client-secret-here"
|
|
});
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await configHandler.HandleAsync(cluster.Id, configRequest);
|
|
|
|
// Assert — OIDC provider configured independently in tenant-beta.
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
result.Value!.Actions.Should().Contain(a => a.Contains("azure-ad") && a.Contains("tenant-beta"));
|
|
result.Value.Actions.Should().Contain(a => a.Contains("Discovery URL"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Keycloak_Configure_AddSAMLProvider_ToRealm()
|
|
{
|
|
// Arrange — Add a SAML identity provider to a realm. Useful for
|
|
// enterprise customers with ADFS or other SAML-based IdPs.
|
|
|
|
KubernetesCluster cluster = CreateConnectedCluster();
|
|
await repository.AddAsync(cluster);
|
|
|
|
keycloakInstaller
|
|
.Setup(i => i.ConfigureAsync(cluster, It.IsAny<ComponentConfiguration>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new InstallResult(
|
|
Success: true,
|
|
ComponentName: "keycloak",
|
|
Message: "SAML provider 'corp-adfs' added to realm 'tenant-gamma'",
|
|
Actions: new List<string>
|
|
{
|
|
"Configured SAML identity provider 'corp-adfs' in realm 'tenant-gamma'",
|
|
"SAML entity ID: https://adfs.corp.com/adfs/services/trust",
|
|
"SSO URL configured",
|
|
"Provider enabled as login option"
|
|
}));
|
|
|
|
List<IComponentInstaller> installers = new() { keycloakInstaller.Object };
|
|
ConfigureComponentHandler configHandler = new(repository, installers);
|
|
|
|
ConfigureComponentRequest configRequest = new(
|
|
ComponentName: "keycloak",
|
|
Values: new Dictionary<string, string>
|
|
{
|
|
["realmName"] = "tenant-gamma",
|
|
["addProvider"] = "saml",
|
|
["providerAlias"] = "corp-adfs",
|
|
["providerDisplayName"] = "Enterprise SSO",
|
|
["samlEntityId"] = "https://adfs.corp.com/adfs/services/trust",
|
|
["samlSsoUrl"] = "https://adfs.corp.com/adfs/ls/",
|
|
["samlCertificate"] = "MIIDXTCCAkWgAwIBAgI..."
|
|
});
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await configHandler.HandleAsync(cluster.Id, configRequest);
|
|
|
|
// Assert — SAML provider configured.
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
result.Value!.Actions.Should().Contain(a => a.Contains("SAML") && a.Contains("tenant-gamma"));
|
|
result.Value.Actions.Should().Contain(a => a.Contains("entity ID"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Keycloak_Configure_UpdateRealmTheme()
|
|
{
|
|
// Arrange — Update the theme of an existing realm. This changes logo,
|
|
// colors, and background without recreating the realm.
|
|
|
|
KubernetesCluster cluster = CreateConnectedCluster();
|
|
await repository.AddAsync(cluster);
|
|
|
|
keycloakInstaller
|
|
.Setup(i => i.ConfigureAsync(cluster, It.IsAny<ComponentConfiguration>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new InstallResult(
|
|
Success: true,
|
|
ComponentName: "keycloak",
|
|
Message: "Theme updated for realm 'tenant-alpha'",
|
|
Actions: new List<string>
|
|
{
|
|
"Updated theme for realm 'tenant-alpha'",
|
|
"Logo updated to new URL",
|
|
"Primary color changed to #FF5722",
|
|
"Background image updated"
|
|
}));
|
|
|
|
List<IComponentInstaller> installers = new() { keycloakInstaller.Object };
|
|
ConfigureComponentHandler configHandler = new(repository, installers);
|
|
|
|
ConfigureComponentRequest configRequest = new(
|
|
ComponentName: "keycloak",
|
|
Values: new Dictionary<string, string>
|
|
{
|
|
["realmName"] = "tenant-alpha",
|
|
["updateTheme"] = "true",
|
|
["themeLogo"] = "https://cdn.alpha.com/new-logo.png",
|
|
["themeBackground"] = "https://cdn.alpha.com/new-bg.jpg",
|
|
["themePrimaryColor"] = "#FF5722"
|
|
});
|
|
|
|
// Act
|
|
|
|
Result<InstallResult> result = await configHandler.HandleAsync(cluster.Id, configRequest);
|
|
|
|
// Assert — Theme updated without affecting realm config.
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
result.Value!.Actions.Should().Contain(a => a.Contains("Updated theme"));
|
|
result.Value.Actions.Should().Contain(a => a.Contains("#FF5722"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Keycloak_Configure_MultipleProviders_DifferentRealms()
|
|
{
|
|
// Arrange — Verify that providers are realm-scoped. Adding BankID to
|
|
// tenant-alpha does NOT affect tenant-beta which has its own OIDC provider.
|
|
// This test verifies the realm isolation.
|
|
|
|
KubernetesCluster cluster = CreateConnectedCluster();
|
|
await repository.AddAsync(cluster);
|
|
|
|
keycloakInstaller
|
|
.Setup(i => i.ConfigureAsync(cluster, It.IsAny<ComponentConfiguration>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync((KubernetesCluster c, ComponentConfiguration cfg, CancellationToken _) =>
|
|
{
|
|
string realm = cfg.Values["realmName"];
|
|
string provider = cfg.Values["addProvider"];
|
|
|
|
return new InstallResult(
|
|
Success: true,
|
|
ComponentName: "keycloak",
|
|
Message: $"Provider '{provider}' added to realm '{realm}'",
|
|
Actions: new List<string>
|
|
{
|
|
$"Configured {provider} provider in realm '{realm}' only",
|
|
$"Other realms not affected"
|
|
});
|
|
});
|
|
|
|
List<IComponentInstaller> installers = new() { keycloakInstaller.Object };
|
|
ConfigureComponentHandler configHandler = new(repository, installers);
|
|
|
|
// Act — Add BankID to tenant-alpha.
|
|
|
|
ConfigureComponentRequest request1 = new(
|
|
ComponentName: "keycloak",
|
|
Values: new Dictionary<string, string>
|
|
{
|
|
["realmName"] = "tenant-alpha",
|
|
["addProvider"] = "bankid",
|
|
["bankIdEnvironment"] = "test"
|
|
});
|
|
|
|
Result<InstallResult> result1 = await configHandler.HandleAsync(cluster.Id, request1);
|
|
|
|
// Act — Add OIDC to tenant-beta.
|
|
|
|
ConfigureComponentRequest request2 = new(
|
|
ComponentName: "keycloak",
|
|
Values: new Dictionary<string, string>
|
|
{
|
|
["realmName"] = "tenant-beta",
|
|
["addProvider"] = "oidc",
|
|
["providerAlias"] = "google",
|
|
["oidcDiscoveryUrl"] = "https://accounts.google.com/.well-known/openid-configuration",
|
|
["oidcClientId"] = "google-client-id",
|
|
["oidcClientSecret"] = "google-secret"
|
|
});
|
|
|
|
Result<InstallResult> result2 = await configHandler.HandleAsync(cluster.Id, request2);
|
|
|
|
// Assert — Each realm got its own provider independently.
|
|
|
|
result1.IsSuccess.Should().BeTrue();
|
|
result1.Value!.Actions.Should().Contain(a => a.Contains("tenant-alpha") && a.Contains("only"));
|
|
|
|
result2.IsSuccess.Should().BeTrue();
|
|
result2.Value!.Actions.Should().Contain(a => a.Contains("tenant-beta") && a.Contains("only"));
|
|
}
|
|
|
|
// ─── Keycloak Adoption Check ──────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task Keycloak_Check_DetectsExistingDeployment()
|
|
{
|
|
// Arrange — Keycloak is already running. The check discovers the
|
|
// instance, its realms, and configured providers.
|
|
|
|
KubernetesCluster cluster = CreateConnectedCluster();
|
|
await repository.AddAsync(cluster);
|
|
|
|
Mock<IAdoptionCheck> keycloakCheck = new();
|
|
keycloakCheck.Setup(c => c.ComponentName).Returns("keycloak");
|
|
keycloakCheck.Setup(c => c.DisplayName).Returns("Keycloak (Identity & Access Management)");
|
|
|
|
keycloakCheck
|
|
.Setup(c => c.CheckAsync(cluster, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new ComponentCheckResult(
|
|
"keycloak",
|
|
ComponentStatus.Installed,
|
|
Details: new List<string>
|
|
{
|
|
"Keycloak 26.0 detected (operator in namespace 'keycloak')",
|
|
"Endpoint: auth.internal.corp.com",
|
|
"Realms: master, tenant-alpha, tenant-beta",
|
|
"tenant-alpha: BankID provider enabled, custom theme",
|
|
"tenant-beta: OIDC (Azure AD) provider enabled",
|
|
"BankID plugin version 1.2.0 installed"
|
|
},
|
|
MissingItems: new List<string>(),
|
|
Configuration: new DiscoveredConfiguration(
|
|
Version: "26.0.0",
|
|
Namespace: "keycloak",
|
|
HelmReleaseName: "keycloak-operator",
|
|
Values: new Dictionary<string, string>
|
|
{
|
|
["endpoint"] = "auth.internal.corp.com",
|
|
["realms"] = "master,tenant-alpha,tenant-beta",
|
|
["bankIdPluginVersion"] = "1.2.0",
|
|
["databaseType"] = "postgres"
|
|
})));
|
|
|
|
// Act
|
|
|
|
ComponentCheckResult checkResult = await keycloakCheck.Object.CheckAsync(cluster);
|
|
|
|
// Assert — Full Keycloak state discovered.
|
|
|
|
checkResult.Status.Should().Be(ComponentStatus.Installed);
|
|
checkResult.Configuration.Should().NotBeNull();
|
|
checkResult.Configuration!.Version.Should().Be("26.0.0");
|
|
checkResult.Configuration.Values["realms"].Should().Contain("tenant-alpha");
|
|
checkResult.Details.Should().Contain(d => d.Contains("BankID"));
|
|
checkResult.Details.Should().Contain(d => d.Contains("Azure AD"));
|
|
}
|
|
|
|
private static KubernetesCluster CreateConnectedCluster()
|
|
{
|
|
KubernetesCluster cluster = KubernetesCluster.Register(
|
|
"prod-cluster", "https://k8s.example.com:6443",
|
|
"apiVersion: v1\nkind: Config\nclusters: []\nusers: []\ncontexts: []",
|
|
Guid.NewGuid(), "prod-ctx", Guid.NewGuid());
|
|
cluster.MarkConnected();
|
|
return cluster;
|
|
}
|
|
|
|
// ─── CNPG Database Integration ────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void KeycloakSchema_ContainsCnpgClusterNameParameter()
|
|
{
|
|
// Keycloak should reference the shared CNPG cluster for its database
|
|
// instead of assuming a dedicated keycloak-db cluster exists.
|
|
|
|
ConfigurationSchema schema = ComponentSchemas.GetSchema("keycloak");
|
|
|
|
schema.Parameters.Should().Contain(p =>
|
|
p.Key == "cnpgClusterName" && p.Type == ParameterType.PostgresCluster);
|
|
}
|
|
|
|
[Fact]
|
|
public void KeycloakSchema_ContainsCnpgNamespaceParameter()
|
|
{
|
|
// The CNPG cluster namespace — needed to resolve the database endpoint.
|
|
|
|
ConfigurationSchema schema = ComponentSchemas.GetSchema("keycloak");
|
|
|
|
schema.Parameters.Should().Contain(p => p.Key == "cnpgNamespace");
|
|
}
|
|
}
|