too much for one commit
This commit is contained in:
546
tests/EntKube.Clusters.Tests/Features/KeycloakTests.cs
Normal file
546
tests/EntKube.Clusters.Tests/Features/KeycloakTests.cs
Normal file
@@ -0,0 +1,546 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user