food-market/tests/food-market.IntegrationTests/HangfireDashboardTests.cs
nns a74fa114d8 feat(hangfire): dashboard + scheduled cleanup джобы (P1-16)
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>
2026-05-28 10:07:14 +05:00

27 lines
1.3 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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