Files
tiku-backend.net/Tiku.IntegrationTests/TenantDomainLifecycleTests.cs

118 lines
4.6 KiB
C#

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<ITenantDomainLifecycleService>()
.ProcessPendingAsync();
Assert.Equal(1, processed);
}
using var verificationScope = factory.CreateSystemScope();
var dbContext = verificationScope.ServiceProvider.GetRequiredService<TikuDbContext>();
var domain = await dbContext.TenantDomains.SingleAsync(item => item.Id == domainId);
Assert.Equal(TenantDomainStatus.Active, domain.Status);
Assert.NotNull(domain.DnsVerifiedAt);
Assert.NotNull(domain.TlsReadyAt);
Assert.Null(domain.LastFailureReason);
Assert.True(await dbContext.AuditLogs.AnyAsync(item =>
item.TenantId == tenantId && item.Action == "tenant.domain.activated" &&
item.TargetId == domainId.ToString("N")));
}
[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<ITenantDomainLifecycleService>().ProcessPendingAsync();
}
using var verificationScope = factory.CreateSystemScope();
var domain = await verificationScope.ServiceProvider.GetRequiredService<TikuDbContext>()
.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<DomainOwnershipResult> 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<DomainGatewayResult> EnsureTlsAsync(
string host,
CancellationToken cancellationToken = default) =>
Task.FromResult(new DomainGatewayResult(
ready,
configured,
ready ? null : configured ? "TLS pending" : "Gateway is not configured"));
}
}