using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using System.Text.Json; using Tiku.Application.Jobs; using Tiku.Application.Security; using Tiku.Domain.Commerce; using Tiku.Domain.Operations; using Tiku.Domain.Platform; using Tiku.Domain.Tenancy; using Tiku.Infrastructure.Persistence; namespace Tiku.IntegrationTests.Api; public sealed class CapabilityAuthorizationTests { [Fact] public async Task Background_job_rechecks_capability_after_enqueue_before_execution() { await using var factory = new ApiTestFactory(); var tenantId = Guid.NewGuid(); await factory.SeedAsync( new Tenant { Id = tenantId, Slug = tenantId.ToString("N"), Name = "Job Capability Tenant" }, new PlatformSaasPlan { Code = "job-capability-test", Name = "Job Capability Test" }, new PlanModuleEntitlement { PlanCode = "job-capability-test", ModuleCode = "content" }, new TenantSubscription { TenantId = tenantId, PlanCode = "job-capability-test", Status = TenantSubscriptionStatus.Active, StartsAt = DateTimeOffset.UtcNow.AddDays(-1), ExpiresAt = DateTimeOffset.UtcNow.AddDays(30) }); using var scope = factory.CreateSystemScope("Verify job capability at consumption"); var jobs = scope.ServiceProvider.GetRequiredService(); var job = await jobs.EnqueueAsync(new CreateBackgroundJobCommand( tenantId, "content_export", JsonSerializer.SerializeToElement(new { exportType = "capability-test" }))); var dbContext = scope.ServiceProvider.GetRequiredService(); dbContext.TenantModuleOverrides.Add(new TenantModuleOverride { TenantId = tenantId, ModuleCode = "content", Mode = TenantModuleOverrideMode.Disabled, Reason = "Integration test revocation" }); await dbContext.SaveChangesAsync(); Assert.True(await jobs.ProcessRequestedAsync( job.Id, tenantId, job.JobType, "capability-test-worker")); var stored = await dbContext.BackgroundJobs.AsNoTracking().SingleAsync(item => item.Id == job.Id); Assert.Equal(BackgroundJobStatus.Failed, stored.Status); Assert.Equal("Tenant capability was revoked before job execution.", stored.LastError); } [Fact] public async Task Entitlement_is_database_backed_and_past_due_is_read_only() { await using var factory = new ApiTestFactory(); var tenantId = Guid.NewGuid(); await factory.SeedAsync( new Tenant { Id = tenantId, Slug = tenantId.ToString("N"), Name = "Capability Tenant" }, new PlatformSaasPlan { Code = "capability-test", Name = "Capability Test" }, new TenantSubscription { TenantId = tenantId, PlanCode = "capability-test", Status = TenantSubscriptionStatus.Active, StartsAt = DateTimeOffset.UtcNow.AddDays(-1), ExpiresAt = DateTimeOffset.UtcNow.AddDays(30) }); using var scope = factory.CreateSystemScope("Verify capability authorization"); var evaluator = scope.ServiceProvider.GetRequiredService(); Assert.False(await evaluator.IsAllowedAsync(tenantId, "content", CapabilityOperation.Read)); var dbContext = scope.ServiceProvider.GetRequiredService(); dbContext.PlanModuleEntitlements.Add(new PlanModuleEntitlement { PlanCode = "capability-test", ModuleCode = "content" }); await dbContext.SaveChangesAsync(); Assert.True(await evaluator.IsAllowedAsync(tenantId, "content", CapabilityOperation.Write)); var subscription = dbContext.TenantSubscriptions.Single(item => item.TenantId == tenantId); subscription.Status = TenantSubscriptionStatus.PastDue; await dbContext.SaveChangesAsync(); Assert.True(await evaluator.IsAllowedAsync(tenantId, "content", CapabilityOperation.Read)); Assert.False(await evaluator.IsAllowedAsync(tenantId, "content", CapabilityOperation.Write)); } }