using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Tiku.Application.Tenancy; using Tiku.Domain.Tenancy; using Tiku.Infrastructure.Persistence; using Tiku.IntegrationTests.Api; namespace Tiku.IntegrationTests; public sealed class TenantDomainLifecycleTests { [Fact] public async Task Domain_becomes_active_only_after_dns_and_tls_are_ready() { await using var factory = new ApiTestFactory( domainOwnershipVerifier: new FakeOwnershipVerifier(true), domainGatewayProvisioner: new FakeGatewayProvisioner(true)); var tenantId = Guid.NewGuid(); var domainId = Guid.NewGuid(); await factory.SeedAsync( new Tenant { Id = tenantId, Slug = "tenant-a", Name = "Tenant A", Status = TenantStatus.Active, Mode = TenantMode.Saas }, new TenantDomain { Id = domainId, TenantId = tenantId, Host = "learn.tenant-a.example", DomainType = TenantDomainType.Custom, Status = TenantDomainStatus.Pending, VerificationToken = "verification-token" }); using (var scope = factory.CreateSystemScope("Process domain lifecycle")) { var processed = await scope.ServiceProvider .GetRequiredService() .ProcessPendingAsync(); Assert.Equal(1, processed); } using var verificationScope = factory.CreateSystemScope(); var domain = await verificationScope.ServiceProvider.GetRequiredService() .TenantDomains.SingleAsync(item => item.Id == domainId); Assert.Equal(TenantDomainStatus.Active, domain.Status); Assert.NotNull(domain.DnsVerifiedAt); Assert.NotNull(domain.TlsReadyAt); Assert.Null(domain.LastFailureReason); } [Fact] public async Task Missing_gateway_configuration_keeps_domain_pending() { await using var factory = new ApiTestFactory( domainOwnershipVerifier: new FakeOwnershipVerifier(true), domainGatewayProvisioner: new FakeGatewayProvisioner(false, configured: false)); var tenantId = Guid.NewGuid(); var domainId = Guid.NewGuid(); await factory.SeedAsync( new Tenant { Id = tenantId, Slug = "tenant-a", Name = "Tenant A", Status = TenantStatus.Active, Mode = TenantMode.Saas }, new TenantDomain { Id = domainId, TenantId = tenantId, Host = "pending.tenant-a.example", Status = TenantDomainStatus.Pending, VerificationToken = "verification-token" }); using (var scope = factory.CreateSystemScope("Process pending domain")) { await scope.ServiceProvider.GetRequiredService().ProcessPendingAsync(); } using var verificationScope = factory.CreateSystemScope(); var domain = await verificationScope.ServiceProvider.GetRequiredService() .TenantDomains.SingleAsync(item => item.Id == domainId); Assert.Equal(TenantDomainStatus.Pending, domain.Status); Assert.NotNull(domain.DnsVerifiedAt); Assert.Contains("not configured", domain.LastFailureReason, StringComparison.OrdinalIgnoreCase); } private sealed class FakeOwnershipVerifier(bool verified) : IDomainOwnershipVerifier { public Task VerifyAsync( string host, string verificationToken, CancellationToken cancellationToken = default) => Task.FromResult(new DomainOwnershipResult(verified, true, verified ? null : "DNS failed")); } private sealed class FakeGatewayProvisioner(bool ready, bool configured = true) : IDomainGatewayProvisioner { public Task EnsureTlsAsync( string host, CancellationToken cancellationToken = default) => Task.FromResult(new DomainGatewayResult( ready, configured, ready ? null : configured ? "TLS pending" : "Gateway is not configured")); } }