106 lines
4.6 KiB
C#
106 lines
4.6 KiB
C#
using System.Net;
|
|
using System.Text.Json;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Tiku.Application.Jobs;
|
|
using Tiku.Domain.Operations;
|
|
using Tiku.Domain.Tenancy;
|
|
using Tiku.Infrastructure.Persistence;
|
|
|
|
namespace Tiku.IntegrationTests.Api;
|
|
|
|
public sealed class MonolithBackgroundProcessingTests
|
|
{
|
|
[Fact]
|
|
public async Task Anonymous_readiness_is_minimal_and_does_not_expose_dependency_topology()
|
|
{
|
|
await using var factory = new ApiTestFactory();
|
|
using var client = factory.CreateClient();
|
|
|
|
var response = await client.GetAsync("/api/system/health/ready");
|
|
using var document = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync());
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
Assert.Equal("ready", document.RootElement.GetProperty("status").GetString());
|
|
Assert.False(document.RootElement.TryGetProperty("database", out _));
|
|
Assert.False(document.RootElement.TryGetProperty("redis", out _));
|
|
Assert.False(document.RootElement.TryGetProperty("rabbitMq", out _));
|
|
Assert.False(document.RootElement.TryGetProperty("outbox", out _));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Api_host_does_not_register_background_processors()
|
|
{
|
|
await using var factory = new ApiTestFactory();
|
|
using var client = factory.CreateClient();
|
|
var hostedServiceNames = factory.Services.GetServices<IHostedService>()
|
|
.Select(service => service.GetType().Name)
|
|
.ToArray();
|
|
|
|
Assert.DoesNotContain("TenantDomainWorker", hostedServiceNames);
|
|
Assert.DoesNotContain("SaasSubscriptionWorker", hostedServiceNames);
|
|
Assert.DoesNotContain("FeatureUsageWorker", hostedServiceNames);
|
|
Assert.DoesNotContain("BackgroundJobsWorker", hostedServiceNames);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Postgres_leases_recover_expired_work_without_duplicate_claims()
|
|
{
|
|
await using var factory = new ApiTestFactory();
|
|
var tenantId = Guid.NewGuid();
|
|
await factory.SeedAsync(new Tenant
|
|
{
|
|
Id = tenantId,
|
|
Slug = tenantId.ToString("N"),
|
|
Name = "Monolith Lease Recovery"
|
|
});
|
|
|
|
Guid expiredJobId;
|
|
Guid pendingJobId;
|
|
using (var scope = factory.CreateSystemScope("Seed lease recovery jobs"))
|
|
{
|
|
var jobs = scope.ServiceProvider.GetRequiredService<IBackgroundJobService>();
|
|
expiredJobId = (await jobs.EnqueueAsync(new CreateBackgroundJobCommand(
|
|
tenantId,
|
|
"statistics_aggregation",
|
|
JsonSerializer.SerializeToElement(new { scope = "tenant" })))).Id;
|
|
pendingJobId = (await jobs.EnqueueAsync(new CreateBackgroundJobCommand(
|
|
tenantId,
|
|
"statistics_aggregation",
|
|
JsonSerializer.SerializeToElement(new { scope = "tenant" })))).Id;
|
|
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
|
await dbContext.BackgroundJobs
|
|
.Where(job => job.Id == expiredJobId)
|
|
.ExecuteUpdateAsync(setters => setters
|
|
.SetProperty(job => job.Status, BackgroundJobStatus.Processing)
|
|
.SetProperty(job => job.LockedBy, "stopped-worker")
|
|
.SetProperty(job => job.LockExpiresAt, DateTimeOffset.UtcNow.AddSeconds(-1)));
|
|
}
|
|
|
|
var processed = await Task.WhenAll(
|
|
ProcessPendingAsync(factory, "monolith-lease-a"),
|
|
ProcessPendingAsync(factory, "monolith-lease-b"));
|
|
|
|
Assert.Equal(2, processed.Sum());
|
|
Assert.Equal(BackgroundJobStatus.Succeeded, await ReadStatusAsync(factory, expiredJobId));
|
|
Assert.Equal(BackgroundJobStatus.Succeeded, await ReadStatusAsync(factory, pendingJobId));
|
|
}
|
|
|
|
private static async Task<int> ProcessPendingAsync(ApiTestFactory factory, string workerId)
|
|
{
|
|
using var scope = factory.CreateSystemScope($"Process jobs with {workerId}");
|
|
return await scope.ServiceProvider.GetRequiredService<IBackgroundJobService>()
|
|
.ProcessPendingAsync(workerId, 1);
|
|
}
|
|
|
|
private static async Task<BackgroundJobStatus> ReadStatusAsync(ApiTestFactory factory, Guid jobId)
|
|
{
|
|
using var scope = factory.CreateSystemScope("Read monolith background job status");
|
|
return await scope.ServiceProvider.GetRequiredService<TikuDbContext>()
|
|
.BackgroundJobs.AsNoTracking()
|
|
.Where(job => job.Id == jobId)
|
|
.Select(job => job.Status)
|
|
.SingleAsync();
|
|
}
|
|
} |