основное по тестам готово
Tests / test (pull_request) Successful in 4m5s

This commit is contained in:
gorbenko.pavel
2026-07-23 17:21:30 +05:00
parent 253fafa575
commit e201823f69
10 changed files with 522 additions and 40 deletions
@@ -0,0 +1,59 @@
using Microsoft.EntityFrameworkCore;
using Npgsql;
using Tnsu.ApprovalRoute.Infrastructure.Persistence;
using Xunit;
namespace Tnsu.ApprovalRoute.IntegrationTests.Infrastructure;
// Отдельная БД на один xUnit-collection (= один тестовый класс) в общем инстансе Postgres.
// Тесты внутри класса делят эту БД; разные классы (разные коллекции) получают разные БД.
public sealed class PostgresDatabaseFixture : IAsyncLifetime
{
private readonly string _databaseName = $"tnsu_test_{Guid.NewGuid():N}";
private readonly NpgsqlConnectionStringBuilder _adminConnectionBuilder = new(TestSettings.BaseConnectionString);
public string ConnectionString { get; private set; } = string.Empty;
public async Task InitializeAsync()
{
await using (var adminConnection = new NpgsqlConnection(_adminConnectionBuilder.ConnectionString))
{
await adminConnection.OpenAsync();
await using var createCommand = new NpgsqlCommand($"CREATE DATABASE \"{_databaseName}\"", adminConnection);
await createCommand.ExecuteNonQueryAsync();
}
ConnectionString = new NpgsqlConnectionStringBuilder(_adminConnectionBuilder.ConnectionString)
{
Database = _databaseName
}.ConnectionString;
var options = new DbContextOptionsBuilder<ApprovalRouteDbContext>()
.UseNpgsql(ConnectionString)
.UseSnakeCaseNamingConvention()
.Options;
await using var db = new ApprovalRouteDbContext(options);
await db.Database.MigrateAsync();
}
public async Task DisposeAsync()
{
await using var adminConnection = new NpgsqlConnection(_adminConnectionBuilder.ConnectionString);
await adminConnection.OpenAsync();
await using (var terminateCommand = new NpgsqlCommand(
"""
SELECT pg_terminate_backend(pid) FROM pg_stat_activity
WHERE datname = @databaseName AND pid <> pg_backend_pid();
""",
adminConnection))
{
terminateCommand.Parameters.AddWithValue("databaseName", _databaseName);
await terminateCommand.ExecuteNonQueryAsync();
}
await using var dropCommand = new NpgsqlCommand($"DROP DATABASE IF EXISTS \"{_databaseName}\"", adminConnection);
await dropCommand.ExecuteNonQueryAsync();
}
}
@@ -0,0 +1,8 @@
using Tnsu.ApprovalRoute.Application.Common.Interfaces;
namespace Tnsu.ApprovalRoute.IntegrationTests.Infrastructure;
internal sealed class TestCurrentUser(string? email = null) : ICurrentUser
{
public string? Email { get; } = email;
}
@@ -0,0 +1,29 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Tnsu.ApprovalRoute.Application;
using Tnsu.ApprovalRoute.Application.Common.Interfaces;
using Tnsu.ApprovalRoute.Infrastructure;
namespace Tnsu.ApprovalRoute.IntegrationTests.Infrastructure;
// Собирает такой же DI-контейнер, как реальное приложение (те же extension-методы, что в Program.cs),
// но с тестовой БД и тестовым ICurrentUser вместо HttpContext-based реализации.
internal static class TestServiceProviderFactory
{
public static ServiceProvider Build(string connectionString, string? currentUserEmail = "initiator@tnsu.kz")
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["ConnectionStrings:Postgres"] = connectionString
})
.Build();
var services = new ServiceCollection();
services.AddApprovalRouteInfrastructure(configuration);
services.AddApprovalRouteApplication();
services.AddSingleton<ICurrentUser>(new TestCurrentUser(currentUserEmail));
return services.BuildServiceProvider();
}
}
@@ -0,0 +1,8 @@
namespace Tnsu.ApprovalRoute.IntegrationTests.Infrastructure;
internal static class TestSettings
{
public static string BaseConnectionString =>
Environment.GetEnvironmentVariable("TEST_POSTGRES_CONNECTION")
?? "Host=localhost;Port=5435;Database=tnsu_approval_route;Username=tnsu_approval_route;Password=tnsu_approval_route";
}