using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;
using Testcontainers.PostgreSql;
using Xunit;
namespace foodmarket.IntegrationTests.Support;
/// Поднимает реальный API (WebApplicationFactory) поверх одноразового
/// Postgres-контейнера (Testcontainers). Миграции применяются на старте host'а
/// (Program.Migrate), сидеры наполняют справочники и SuperAdmin. Запускается
/// один раз на всю коллекцию тестов.
public sealed class ApiFactory : WebApplicationFactory, IAsyncLifetime
{
static ApiFactory()
{
// Ryuk (reaper) тянет образ из Docker Hub — на этом хосте сеть к внешним
// реестрам нестабильна, а postgres:16-alpine уже закэширован. Выключаем.
Environment.SetEnvironmentVariable("TESTCONTAINERS_RYUK_DISABLED", "true");
// Лимитер читает конфиг ЭАГЕРНО при регистрации сервисов — поэтому
// переопределяем через env-переменную (её CreateBuilder подхватывает до
// регистрации), а не через ConfigureAppConfiguration (применяется позже).
// Тесты логинятся десятки раз с одного loopback-IP — иначе 429.
Environment.SetEnvironmentVariable("RateLimiting__Enabled", "false");
// Тише логи в тестовом прогоне (OpenIddict сыплет Debug-событиями).
Environment.SetEnvironmentVariable("Serilog__MinimumLevel__Default", "Warning");
}
private readonly PostgreSqlContainer _db = new PostgreSqlBuilder()
.WithImage("postgres:16-alpine")
.WithDatabase("food_market")
.WithUsername("food_market")
.WithPassword("food_market_test")
.Build();
public async Task InitializeAsync() => await _db.StartAsync();
public new async Task DisposeAsync()
{
await _db.DisposeAsync();
await base.DisposeAsync();
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
// Development — простые dev-ключи OpenIddict (без генерации сертификатов),
// 3-сегментный токен. Лимитер выключаем: тесты логинятся много раз с одного IP.
builder.UseEnvironment("Development");
builder.ConfigureAppConfiguration((_, cfg) =>
{
// Строку подключения AddDbContext читает лениво — здесь override
// срабатывает. RateLimiting выключён через env (см. static ctor).
cfg.AddInMemoryCollection(new Dictionary
{
["ConnectionStrings:Default"] = _db.GetConnectionString(),
});
});
}
}