using EntKube.Provisioning.Domain; using FluentAssertions; namespace EntKube.Provisioning.Tests.Domain; public class IdentityRealmTests { [Fact] public void Create_ValidName_ReturnsRealmInProvisioningState() { // When creating a new realm, it starts in Provisioning status // with the given name and cluster association. Guid environmentId = Guid.NewGuid(); Guid clusterId = Guid.NewGuid(); IdentityRealm realm = IdentityRealm.Create(environmentId, "my-app", clusterId); realm.Id.Should().NotBeEmpty(); realm.EnvironmentId.Should().Be(environmentId); realm.ClusterId.Should().Be(clusterId); realm.Name.Should().Be("my-app"); realm.Status.Should().Be(IdentityRealmStatus.Provisioning); realm.Branding.Should().Be(RealmBranding.Default); realm.Pages.LoginEnabled.Should().BeTrue(); realm.Pages.ProfileEnabled.Should().BeFalse(); } [Fact] public void Create_EmptyName_ThrowsArgumentException() { // A realm must have a name — empty is not valid. Action act = () => IdentityRealm.Create(Guid.NewGuid(), "", Guid.NewGuid()); act.Should().Throw(); } [Fact] public void UpdateBranding_ChangesBrandingAndTimestamp() { // After updating branding, the new values stick and // the last modified timestamp is updated. IdentityRealm realm = IdentityRealm.Create(Guid.NewGuid(), "branded", Guid.NewGuid()); RealmBranding newBranding = new( LogoUrl: "https://example.com/logo.png", BackgroundImageUrl: null, BackgroundColor: "#1a1a2e", PrimaryColor: "#16213e", SecondaryColor: "#0f3460", TextColor: "#ffffff"); realm.UpdateBranding(newBranding); realm.Branding.LogoUrl.Should().Be("https://example.com/logo.png"); realm.Branding.BackgroundColor.Should().Be("#1a1a2e"); realm.LastModifiedAt.Should().NotBeNull(); } [Fact] public void UpdatePages_EnablesProfile() { // A realm starts with only login enabled. We can enable profile pages too. IdentityRealm realm = IdentityRealm.Create(Guid.NewGuid(), "with-profile", Guid.NewGuid()); RealmPages pages = new( LoginEnabled: true, ProfileEnabled: true, RegistrationEnabled: false, ForgotPasswordEnabled: true); realm.UpdatePages(pages); realm.Pages.ProfileEnabled.Should().BeTrue(); } [Fact] public void AddIdentityProvider_NewProvider_AddsToList() { // Adding an OIDC identity provider to the realm. IdentityRealm realm = IdentityRealm.Create(Guid.NewGuid(), "idp-realm", Guid.NewGuid()); RealmIdentityProvider provider = RealmIdentityProvider.Create( alias: "azure-ad", displayName: "Azure AD", type: IdentityProviderType.Oidc, authorizationUrl: "https://login.microsoftonline.com/authorize", tokenUrl: "https://login.microsoftonline.com/token", clientId: "my-client-id"); realm.AddIdentityProvider(provider); realm.IdentityProviders.Should().HaveCount(1); realm.IdentityProviders[0].Alias.Should().Be("azure-ad"); realm.IdentityProviders[0].Type.Should().Be(IdentityProviderType.Oidc); } [Fact] public void AddIdentityProvider_DuplicateAlias_Throws() { // Each provider must have a unique alias within the realm. IdentityRealm realm = IdentityRealm.Create(Guid.NewGuid(), "dup-realm", Guid.NewGuid()); RealmIdentityProvider first = RealmIdentityProvider.Create("github", "GitHub", IdentityProviderType.GitHub); RealmIdentityProvider duplicate = RealmIdentityProvider.Create("github", "GitHub Copy", IdentityProviderType.GitHub); realm.AddIdentityProvider(first); Action act = () => realm.AddIdentityProvider(duplicate); act.Should().Throw(); } [Fact] public void RemoveIdentityProvider_ExistingAlias_RemovesFromList() { // Removing an identity provider by alias. IdentityRealm realm = IdentityRealm.Create(Guid.NewGuid(), "remove-realm", Guid.NewGuid()); RealmIdentityProvider provider = RealmIdentityProvider.Create("google", "Google", IdentityProviderType.Google); realm.AddIdentityProvider(provider); realm.RemoveIdentityProvider("google"); realm.IdentityProviders.Should().BeEmpty(); } [Fact] public void AddOrganization_NewOrg_AddsToList() { // Organizations let you group users within a realm. IdentityRealm realm = IdentityRealm.Create(Guid.NewGuid(), "org-realm", Guid.NewGuid()); RealmOrganization org = RealmOrganization.Create("Acme Corp", "Top-level org"); realm.AddOrganization(org); realm.Organizations.Should().HaveCount(1); realm.Organizations[0].Name.Should().Be("Acme Corp"); } [Fact] public void Organization_AddChild_CreatesHierarchy() { // Organizations support nested children for hierarchical structure. RealmOrganization parent = RealmOrganization.Create("Acme Corp"); RealmOrganization child = RealmOrganization.Create("Engineering"); RealmOrganization grandchild = RealmOrganization.Create("Backend Team"); parent.AddChild(child); child.AddChild(grandchild); parent.Children.Should().HaveCount(1); parent.Children[0].Name.Should().Be("Engineering"); parent.Children[0].Children.Should().HaveCount(1); parent.Children[0].Children[0].Name.Should().Be("Backend Team"); } [Fact] public void Organization_DuplicateChildName_Throws() { // Child names must be unique within a parent. RealmOrganization parent = RealmOrganization.Create("Acme Corp"); RealmOrganization child1 = RealmOrganization.Create("Engineering"); RealmOrganization child2 = RealmOrganization.Create("Engineering"); parent.AddChild(child1); Action act = () => parent.AddChild(child2); act.Should().Throw(); } [Fact] public void MarkActive_ChangesStatusToActive() { IdentityRealm realm = IdentityRealm.Create(Guid.NewGuid(), "active-realm", Guid.NewGuid()); realm.MarkActive(); realm.Status.Should().Be(IdentityRealmStatus.Active); } }