mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-03-20 07:39:07 -04:00
Refactor integration test TestHostBuilder setup to shared abstract class (#512)
This commit is contained in:
161
src/Tests/AliasVault.IntegrationTests/AbstractTestHostBuilder.cs
Normal file
161
src/Tests/AliasVault.IntegrationTests/AbstractTestHostBuilder.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="AbstractTestHostBuilder.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace AliasVault.IntegrationTests;
|
||||
|
||||
using AliasServerDb;
|
||||
using AliasServerDb.Configuration;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Npgsql;
|
||||
|
||||
/// <summary>
|
||||
/// Builder class for creating a test host for services in order to run integration tests against them. This class
|
||||
/// contains common logic such as creating a temporary database.
|
||||
/// </summary>
|
||||
public class AbstractTestHostBuilder : IAsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The DbContextFactory instance that is created for the test.
|
||||
/// </summary>
|
||||
private IAliasServerDbContextFactory _dbContextFactory = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The cached DbContext instance that can be used during the test.
|
||||
/// </summary>
|
||||
private AliasServerDbContext? _dbContext;
|
||||
|
||||
/// <summary>
|
||||
/// The temporary database name for the test.
|
||||
/// </summary>
|
||||
private string? _tempDbName;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the DbContext instance for the test. This can be used to seed the database with test data.
|
||||
/// </summary>
|
||||
/// <returns>AliasServerDbContext instance.</returns>
|
||||
public AliasServerDbContext GetDbContext()
|
||||
{
|
||||
if (_dbContext != null)
|
||||
{
|
||||
return _dbContext;
|
||||
}
|
||||
|
||||
_dbContext = _dbContextFactory.CreateDbContext();
|
||||
return _dbContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the DbContext instance for the test. This can be used to seed the database with test data.
|
||||
/// </summary>
|
||||
/// <returns>AliasServerDbContext instance.</returns>
|
||||
public async Task<AliasServerDbContext> GetDbContextAsync()
|
||||
{
|
||||
return await _dbContextFactory.CreateDbContextAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of the test host and cleans up the temporary database.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_dbContext != null)
|
||||
{
|
||||
await _dbContext.DisposeAsync();
|
||||
_dbContext = null;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_tempDbName))
|
||||
{
|
||||
// Create a connection to 'postgres' database to drop the test database
|
||||
using var conn =
|
||||
new NpgsqlConnection(
|
||||
"Host=localhost;Port=5433;Database=postgres;Username=aliasvault;Password=password");
|
||||
await conn.OpenAsync();
|
||||
|
||||
// First terminate existing connections
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = $"""
|
||||
SELECT pg_terminate_backend(pid)
|
||||
FROM pg_stat_activity
|
||||
WHERE datname = '{_tempDbName}';
|
||||
""";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
// Then drop the database
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = $"""
|
||||
DROP DATABASE IF EXISTS "{_tempDbName}";
|
||||
""";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new test host builder with test database connection already configured.
|
||||
/// </summary>
|
||||
/// <returns>IHost.</returns>
|
||||
protected IHostBuilder CreateBuilder()
|
||||
{
|
||||
_tempDbName = $"aliasdb_test_{Guid.NewGuid()}";
|
||||
|
||||
// Create a connection to 'postgres' database to ensure the test database exists
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
|
||||
using (var conn = new NpgsqlConnection("Host=localhost;Port=5433;Database=postgres;Username=aliasvault;Password=password"))
|
||||
{
|
||||
conn.Open();
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = $"""
|
||||
CREATE DATABASE "{_tempDbName}";
|
||||
""";
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
// Create a connection to the new test database
|
||||
var dbConnection = new NpgsqlConnection($"Host=localhost;Port=5433;Database={_tempDbName};Username=aliasvault;Password=password");
|
||||
|
||||
var builder = Host.CreateDefaultBuilder()
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
// Override configuration
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json", optional: true)
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["DatabaseProvider"] = "postgresql",
|
||||
["ConnectionStrings:AliasServerDbContext"] = dbConnection.ConnectionString,
|
||||
})
|
||||
.Build();
|
||||
|
||||
services.AddSingleton<IConfiguration>(configuration);
|
||||
services.AddAliasVaultDatabaseConfiguration(configuration);
|
||||
|
||||
// Ensure the in-memory database is populated with tables
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
using (var scope = serviceProvider.CreateScope())
|
||||
{
|
||||
_dbContextFactory = scope.ServiceProvider.GetRequiredService<IAliasServerDbContextFactory>();
|
||||
var dbContext = _dbContextFactory.CreateDbContext();
|
||||
dbContext.Database.Migrate();
|
||||
}
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -7,185 +7,60 @@
|
||||
|
||||
namespace AliasVault.IntegrationTests.SmtpServer;
|
||||
|
||||
using System.Data.Common;
|
||||
using AliasServerDb;
|
||||
using AliasServerDb.Configuration;
|
||||
using AliasVault.SmtpService;
|
||||
using AliasVault.SmtpService.Handlers;
|
||||
using AliasVault.SmtpService.Workers;
|
||||
using global::SmtpServer;
|
||||
using global::SmtpServer.Storage;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Npgsql;
|
||||
|
||||
/// <summary>
|
||||
/// Builder class for creating a test host for the SmtpServiceWorker in order to run integration tests against it.
|
||||
/// </summary>
|
||||
public class TestHostBuilder : IAsyncDisposable
|
||||
public class TestHostBuilder : AbstractTestHostBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The DbContextFactory instance that is created for the test.
|
||||
/// </summary>
|
||||
private IAliasServerDbContextFactory _dbContextFactory = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The cached DbContext instance that can be used during the test.
|
||||
/// </summary>
|
||||
private AliasServerDbContext? _dbContext;
|
||||
|
||||
/// <summary>
|
||||
/// The temporary database name for the test.
|
||||
/// </summary>
|
||||
private string? _tempDbName;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the DbContext instance for the test. This can be used to seed the database with test data.
|
||||
/// </summary>
|
||||
/// <returns>AliasServerDbContext instance.</returns>
|
||||
public AliasServerDbContext GetDbContext()
|
||||
{
|
||||
if (_dbContext != null)
|
||||
{
|
||||
return _dbContext;
|
||||
}
|
||||
|
||||
_dbContext = _dbContextFactory.CreateDbContext();
|
||||
return _dbContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the SmtpService test host.
|
||||
/// Builds the SmtpService test host with a provided database connection.
|
||||
/// </summary>
|
||||
/// <returns>IHost.</returns>
|
||||
public IHost Build()
|
||||
{
|
||||
_tempDbName = $"aliasdb_test_{Guid.NewGuid()}";
|
||||
// Get base builder with database connection already configured.
|
||||
var builder = CreateBuilder();
|
||||
|
||||
// Create a connection to 'postgres' database to ensure the test database exists
|
||||
using (var conn = new NpgsqlConnection("Host=localhost;Port=5433;Database=postgres;Username=aliasvault;Password=password"))
|
||||
// Add specific services for the TestExceptionWorker.
|
||||
builder.ConfigureServices((context, services) =>
|
||||
{
|
||||
conn.Open();
|
||||
using (var cmd = conn.CreateCommand())
|
||||
services.AddSingleton(new Config
|
||||
{
|
||||
cmd.CommandText = $"""
|
||||
CREATE DATABASE "{_tempDbName}";
|
||||
""";
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
AllowedToDomains = new List<string> { "example.tld" },
|
||||
SmtpTlsEnabled = "false",
|
||||
});
|
||||
|
||||
// Create a connection to the new test database
|
||||
var dbConnection = new NpgsqlConnection($"Host=localhost;Port=5433;Database={_tempDbName};Username=aliasvault;Password=password");
|
||||
|
||||
return Build(dbConnection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the SmtpService test host with a provided database connection.
|
||||
/// </summary>
|
||||
/// <param name="dbConnection">The database connection to use for the test.</param>
|
||||
/// <returns>IHost.</returns>
|
||||
public IHost Build(DbConnection dbConnection)
|
||||
{
|
||||
var builder = Host.CreateDefaultBuilder()
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
// Override configuration
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json", optional: true)
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["DatabaseProvider"] = "postgresql",
|
||||
["ConnectionStrings:AliasServerDbContext"] = dbConnection.ConnectionString,
|
||||
})
|
||||
.Build();
|
||||
|
||||
services.AddSingleton<IConfiguration>(configuration);
|
||||
|
||||
services.AddSingleton(new Config
|
||||
services.AddTransient<IMessageStore, DatabaseMessageStore>();
|
||||
services.AddSingleton<SmtpServer>(
|
||||
provider =>
|
||||
{
|
||||
AllowedToDomains = new List<string> { "example.tld" },
|
||||
SmtpTlsEnabled = "false",
|
||||
var options = new SmtpServerOptionsBuilder()
|
||||
.ServerName("aliasvault");
|
||||
|
||||
// Note: port 25 doesn't work in GitHub actions so we use these instead for the integration tests:
|
||||
// - 2525 for the SMTP server
|
||||
// - 5870 for the submission server
|
||||
options.Endpoint(serverBuilder =>
|
||||
serverBuilder
|
||||
.Port(2525, false))
|
||||
.Endpoint(serverBuilder =>
|
||||
serverBuilder
|
||||
.Port(5870, false));
|
||||
|
||||
return new SmtpServer(options.Build(), provider.GetRequiredService<IServiceProvider>());
|
||||
});
|
||||
|
||||
services.AddTransient<IMessageStore, DatabaseMessageStore>();
|
||||
services.AddSingleton<SmtpServer>(
|
||||
provider =>
|
||||
{
|
||||
var options = new SmtpServerOptionsBuilder()
|
||||
.ServerName("aliasvault");
|
||||
|
||||
// Note: port 25 doesn't work in GitHub actions so we use these instead for the integration tests:
|
||||
// - 2525 for the SMTP server
|
||||
// - 5870 for the submission server
|
||||
options.Endpoint(serverBuilder =>
|
||||
serverBuilder
|
||||
.Port(2525, false))
|
||||
.Endpoint(serverBuilder =>
|
||||
serverBuilder
|
||||
.Port(5870, false));
|
||||
|
||||
return new SmtpServer(options.Build(), provider.GetRequiredService<IServiceProvider>());
|
||||
});
|
||||
|
||||
services.AddAliasVaultDatabaseConfiguration(configuration);
|
||||
services.AddHostedService<SmtpServerWorker>();
|
||||
|
||||
// Ensure the in-memory database is populated with tables
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
using (var scope = serviceProvider.CreateScope())
|
||||
{
|
||||
_dbContextFactory = scope.ServiceProvider.GetRequiredService<IAliasServerDbContextFactory>();
|
||||
var dbContext = _dbContextFactory.CreateDbContext();
|
||||
dbContext.Database.Migrate();
|
||||
}
|
||||
});
|
||||
services.AddHostedService<SmtpServerWorker>();
|
||||
});
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of the test host and cleans up the temporary database.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_dbContext != null)
|
||||
{
|
||||
await _dbContext.DisposeAsync();
|
||||
_dbContext = null;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_tempDbName))
|
||||
{
|
||||
// Create a connection to 'postgres' database to drop the test database
|
||||
using var conn = new NpgsqlConnection("Host=localhost;Port=5433;Database=postgres;Username=aliasvault;Password=password");
|
||||
await conn.OpenAsync();
|
||||
|
||||
// First terminate existing connections
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = $"""
|
||||
SELECT pg_terminate_backend(pid)
|
||||
FROM pg_stat_activity
|
||||
WHERE datname = '{_tempDbName}';
|
||||
""";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
// Then drop the database
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = $"""
|
||||
DROP DATABASE IF EXISTS "{_tempDbName}";
|
||||
""";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="StatusHostedServiceTests.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace AliasVault.IntegrationTests.StatusHostedService;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for StatusHostedService wrapper.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class StatusHostedServiceTests
|
||||
{
|
||||
/// <summary>
|
||||
/// The test host instance.
|
||||
/// </summary>
|
||||
private IHost _testHost;
|
||||
|
||||
/// <summary>
|
||||
/// The test host builder instance.
|
||||
/// </summary>
|
||||
private TestHostBuilder _testHostBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Setup logic for every test.
|
||||
/// </summary>
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_testHostBuilder = new TestHostBuilder();
|
||||
_testHost = _testHostBuilder.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tear down logic for every test.
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
[TearDown]
|
||||
public async Task TearDown()
|
||||
{
|
||||
await _testHost.StopAsync();
|
||||
_testHost.Dispose();
|
||||
await _testHostBuilder.DisposeAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that the StatusHostedService properly logs errors from the wrapped service.
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
[Test]
|
||||
public async Task LogsExceptionFromWrappedService()
|
||||
{
|
||||
// Start the service which will trigger the TestExceptionWorker to throw
|
||||
await _testHost.StartAsync();
|
||||
|
||||
// Give it a moment to process
|
||||
await Task.Delay(5000);
|
||||
|
||||
// Check the logs for the expected error
|
||||
await using var dbContext = _testHostBuilder.GetDbContext();
|
||||
var errorLog = await dbContext.Logs
|
||||
.OrderByDescending(l => l.TimeStamp)
|
||||
.FirstOrDefaultAsync(l => l.Level == "Error" && l.Exception.Contains("Test exception"));
|
||||
|
||||
Assert.That(errorLog, Is.Not.Null, "Expected error log from TestExceptionWorker was not found");
|
||||
Assert.That(errorLog.Message, Does.Contain("An error occurred in StatusHostedService"), "Error log does not contain expected message from StatusHostedService");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="TestExceptionWorker.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace AliasVault.IntegrationTests.StatusHostedService;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
/// <summary>
|
||||
/// A simple worker that throws an exception during task execution. This is used for testing purposes.
|
||||
/// </summary>
|
||||
public class TestExceptionWorker() : BackgroundService
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(100), stoppingToken);
|
||||
throw new Exception("Test exception");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="TestHostBuilder.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace AliasVault.IntegrationTests.StatusHostedService;
|
||||
|
||||
using System.Data.Common;
|
||||
using System.Reflection;
|
||||
using AliasServerDb;
|
||||
using AliasServerDb.Configuration;
|
||||
using AliasVault.WorkerStatus.ServiceExtensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Npgsql;
|
||||
|
||||
/// <summary>
|
||||
/// Builder class for creating a test host for the StatusHostedService wrapper in order to run integration tests
|
||||
/// against it. This primarily tests basic functionality of the hosted service such as starting, stopping and error
|
||||
/// handling.
|
||||
///
|
||||
/// The StatusHostedService is a wrapper around the HostedService class that provides additional functionality for
|
||||
/// managing the status of the hosted service. This includes being able to start and stop the services from the
|
||||
/// AliasVault admin panel.
|
||||
/// </summary>
|
||||
public class TestHostBuilder : AbstractTestHostBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds the test host for the TestExceptionWorker.
|
||||
/// </summary>
|
||||
/// <returns>IHost.</returns>
|
||||
public IHost Build()
|
||||
{
|
||||
// Get base builder with database connection already configured.
|
||||
var builder = CreateBuilder();
|
||||
|
||||
// Add specific services for the TestExceptionWorker.
|
||||
builder.ConfigureServices((context, services) =>
|
||||
{
|
||||
services.AddStatusHostedService<TestExceptionWorker, AliasServerDbContext>(Assembly.GetExecutingAssembly().GetName().Name!);
|
||||
});
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
@@ -7,149 +7,41 @@
|
||||
|
||||
namespace AliasVault.IntegrationTests.TaskRunner;
|
||||
|
||||
using AliasServerDb;
|
||||
using AliasServerDb.Configuration;
|
||||
using AliasVault.Shared.Server.Services;
|
||||
using AliasVault.TaskRunner.Tasks;
|
||||
using AliasVault.TaskRunner.Workers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Npgsql;
|
||||
|
||||
/// <summary>
|
||||
/// Builder class for creating a test host for the TaskRunner in order to run integration tests against it.
|
||||
/// </summary>
|
||||
public class TestHostBuilder : IAsyncDisposable
|
||||
public class TestHostBuilder : AbstractTestHostBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The DbContextFactory instance that is created for the test.
|
||||
/// </summary>
|
||||
private IAliasServerDbContextFactory _dbContextFactory = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The cached DbContext instance that can be used during the test.
|
||||
/// </summary>
|
||||
private AliasServerDbContext? _dbContext;
|
||||
|
||||
/// <summary>
|
||||
/// The temporary database name for the test.
|
||||
/// </summary>
|
||||
private string? _tempDbName;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the DbContext instance for the test. This can be used to seed the database with test data.
|
||||
/// </summary>
|
||||
/// <returns>AliasServerDbContext instance.</returns>
|
||||
public async Task<AliasServerDbContext> GetDbContextAsync()
|
||||
{
|
||||
return await _dbContextFactory.CreateDbContextAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the TaskRunner test host.
|
||||
/// </summary>
|
||||
/// <returns>IHost.</returns>
|
||||
public IHost Build()
|
||||
{
|
||||
// Create a temporary database for the test
|
||||
_tempDbName = $"aliasdb_test_{Guid.NewGuid()}";
|
||||
// Get base builder with database connection already configured.
|
||||
var builder = CreateBuilder();
|
||||
|
||||
// Create a connection to 'postgres' database to create the test database
|
||||
using (var conn = new NpgsqlConnection("Host=localhost;Port=5433;Database=postgres;Username=aliasvault;Password=password"))
|
||||
// Add specific services for the TestExceptionWorker.
|
||||
builder.ConfigureServices((context, services) =>
|
||||
{
|
||||
conn.Open();
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = $"""
|
||||
CREATE DATABASE "{_tempDbName}";
|
||||
""";
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
// Add server settings service
|
||||
services.AddSingleton<ServerSettingsService>();
|
||||
|
||||
// Create the connection to the new test database
|
||||
var dbConnection = new NpgsqlConnection($"Host=localhost;Port=5433;Database={_tempDbName};Username=aliasvault;Password=password");
|
||||
var builder = Host.CreateDefaultBuilder()
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
// Override configuration
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json", optional: true)
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["DatabaseProvider"] = "postgresql",
|
||||
["ConnectionStrings:AliasServerDbContext"] = dbConnection.ConnectionString,
|
||||
})
|
||||
.Build();
|
||||
services.AddSingleton<IConfiguration>(configuration);
|
||||
// Add maintenance tasks
|
||||
services.AddTransient<IMaintenanceTask, LogCleanupTask>();
|
||||
services.AddTransient<IMaintenanceTask, EmailCleanupTask>();
|
||||
services.AddTransient<IMaintenanceTask, EmailQuotaCleanupTask>();
|
||||
|
||||
// Add server settings service
|
||||
services.AddSingleton<ServerSettingsService>();
|
||||
|
||||
// Add maintenance tasks
|
||||
services.AddTransient<IMaintenanceTask, LogCleanupTask>();
|
||||
services.AddTransient<IMaintenanceTask, EmailCleanupTask>();
|
||||
services.AddTransient<IMaintenanceTask, EmailQuotaCleanupTask>();
|
||||
|
||||
services.AddAliasVaultDatabaseConfiguration(configuration);
|
||||
|
||||
// Add the TaskRunner worker
|
||||
services.AddHostedService<TaskRunnerWorker>();
|
||||
|
||||
// Ensure the database is populated with tables
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
using (var scope = serviceProvider.CreateScope())
|
||||
{
|
||||
_dbContextFactory = scope.ServiceProvider.GetRequiredService<IAliasServerDbContextFactory>();
|
||||
var dbContext = _dbContextFactory.CreateDbContext();
|
||||
dbContext.Database.Migrate();
|
||||
}
|
||||
});
|
||||
// Add the TaskRunner worker
|
||||
services.AddHostedService<TaskRunnerWorker>();
|
||||
});
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of the test host and cleans up the temporary database.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_dbContext != null)
|
||||
{
|
||||
await _dbContext.DisposeAsync();
|
||||
_dbContext = null;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_tempDbName))
|
||||
{
|
||||
// Create a connection to 'postgres' database to drop the test database
|
||||
using var conn = new NpgsqlConnection("Host=localhost;Port=5433;Database=postgres;Username=aliasvault;Password=password");
|
||||
await conn.OpenAsync();
|
||||
|
||||
// First terminate existing connections
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = $"""
|
||||
SELECT pg_terminate_backend(pid)
|
||||
FROM pg_stat_activity
|
||||
WHERE datname = '{_tempDbName}';
|
||||
""";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
// Then drop the database
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = $"""
|
||||
DROP DATABASE IF EXISTS "{_tempDbName}";
|
||||
""";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user