Files
tiku-backend.net/Tiku.IntegrationTests/Api/MonolithBackgroundProcessingTests.cs
xiong c497a3ca8d
Some checks failed
ci / release-gate (push) Has been cancelled
清理代码
2026-08-03 12:31:39 +08:00

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();
}
}