Hangfire.PostgreSql storage (тот же ConnectionString:Default). Сервер стартует только когда Hangfire:Enabled (true по умолчанию) — в интеграционных тестах выключаем через env Hangfire__Enabled=false, чтобы тесты не плодили служебные таблицы в одноразовом контейнере. Dashboard на /hangfire с авторизационным фильтром SuperAdminHangfireFilter — требует роли SuperAdmin (стандартный OpenIddict-токен валидируется аутентификационным middleware'ом перед этим). Recurring jobs (HangfireJobsConfigurator): • prune-stock-movements — ежедневно 03:30 UTC, удаляет StockMovement старше 730 дней (Hangfire:Retention:StockMovementDays). За 30 минут до бэкапа, чтобы pg_dump не цеплял временные блокировки. • prune-audit-log — ежедневно 03:45 UTC, удаляет super_admin_audit_log старше 90 дней (Hangfire:Retention:AuditLogDays). Логика очистки в HousekeepingJobs (scoped, использует AppDbContext с IgnoreQueryFilters — это межтенантная задача). Тесты: 1 unit (PruneStockMovements удаляет только старые), 1 интеграционный (dashboard не отвечает без Hangfire-сервера). Полный прогон: 24 unit + 32 integration = 56 зелёных. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
27 lines
1.3 KiB
C#
27 lines
1.3 KiB
C#
using FluentAssertions;
|
||
using foodmarket.IntegrationTests.Support;
|
||
using Xunit;
|
||
|
||
namespace foodmarket.IntegrationTests;
|
||
|
||
[Collection(ApiCollection.Name)]
|
||
public class HangfireDashboardTests
|
||
{
|
||
private readonly ApiFactory _factory;
|
||
public HangfireDashboardTests(ApiFactory factory) => _factory = factory;
|
||
|
||
/// <summary>В тестах Hangfire-сервер выключен (Hangfire__Enabled=false), и
|
||
/// dashboard-маршрут не маппится. Проверяем что /hangfire вообще не отвечает
|
||
/// 200/302 без auth — то есть гейт работает на уровне отсутствия маршрута.
|
||
/// На проде (Hangfire включён) фильтр SuperAdminHangfireFilter блокирует
|
||
/// неавторизованных пользователей. См. SuperAdminHangfireFilter.</summary>
|
||
[Fact]
|
||
public async Task Dashboard_not_exposed_when_disabled_in_tests()
|
||
{
|
||
var api = new ApiActor(_factory.CreateClient());
|
||
using var resp = await api.Http.GetAsync("/hangfire");
|
||
// В тестовом режиме сервер не поднят, маршрут не зарегистрирован → 404.
|
||
resp.StatusCode.Should().Be(System.Net.HttpStatusCode.NotFound);
|
||
}
|
||
}
|