a025655b4d
Point Jellyfin.Database.Tests.PostgreSQL at the server postgres-migration-chain already runs, give every test its own database, and fix the two tests that only ever failed against real PostgreSQL.
101 lines
3.8 KiB
C#
101 lines
3.8 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Jellyfin.Database.Implementations;
|
|
using Jellyfin.Database.Implementations.DbConfiguration;
|
|
using Jellyfin.Database.Implementations.Locking;
|
|
using Jellyfin.Database.Providers.PostgreSQL;
|
|
using Jellyfin.Server.Tests.Migrations;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Npgsql;
|
|
using Xunit;
|
|
|
|
namespace Jellyfin.Database.Tests.PostgreSQL;
|
|
|
|
/// <summary>
|
|
/// Integration tests that validate PostgreSQL migrations against a real server.
|
|
/// </summary>
|
|
[Xunit.Trait("Category", "RequiresDocker")]
|
|
public sealed class PostgreSqlMigrationTests : IAsyncLifetime
|
|
{
|
|
private static int _databaseSequence;
|
|
|
|
private PostgreSqlTestServer? _server;
|
|
private NpgsqlDataSource? _dataSource;
|
|
|
|
/// <summary>
|
|
/// Attaches to the test server and hands this test an empty database of its own.
|
|
/// </summary>
|
|
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
|
|
public async ValueTask InitializeAsync()
|
|
{
|
|
_server = await PostgreSqlTestServer.StartAsync().ConfigureAwait(false);
|
|
|
|
var databaseName = FormattableString.Invariant($"pg_migration_{Interlocked.Increment(ref _databaseSequence)}");
|
|
var connectionString = await _server.CreateDatabaseAsync(databaseName, TestContext.Current.CancellationToken).ConfigureAwait(false);
|
|
_dataSource = new NpgsqlDataSourceBuilder(connectionString).Build();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases the data source and the test server.
|
|
/// </summary>
|
|
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
// InitializeAsync can fail before the data source exists; its error must not be masked by an NRE here.
|
|
if (_dataSource is not null)
|
|
{
|
|
await _dataSource.DisposeAsync().ConfigureAwait(false);
|
|
}
|
|
|
|
if (_server is not null)
|
|
{
|
|
await _server.DisposeAsync().ConfigureAwait(false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that the <c>InitialPostgreSql</c> migration applies cleanly to a fresh database.
|
|
/// </summary>
|
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
|
[Fact]
|
|
public async Task MigrateAsync_AppliesInitialMigrationCleanly()
|
|
{
|
|
var context = CreateContext(_dataSource!);
|
|
await using (context)
|
|
{
|
|
await context.Database.MigrateAsync(TestContext.Current.CancellationToken);
|
|
|
|
var pendingMigrations = await context.Database.GetPendingMigrationsAsync(TestContext.Current.CancellationToken);
|
|
Assert.Empty(pendingMigrations);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that no pending model changes exist for the PostgreSQL provider,
|
|
/// acting as a CI gate that fails when model changes are added without a corresponding migration.
|
|
/// </summary>
|
|
[Fact]
|
|
public void CheckForUnappliedMigrations_PostgreSql()
|
|
{
|
|
using var context = CreateContext(_dataSource!);
|
|
|
|
Assert.False(
|
|
context.Database.HasPendingModelChanges(),
|
|
"There are unapplied changes to the EFCore model for PostgreSQL. Please create a Migration.");
|
|
}
|
|
|
|
private static JellyfinDbContext CreateContext(NpgsqlDataSource dataSource)
|
|
{
|
|
var optionsBuilder = new DbContextOptionsBuilder<JellyfinDbContext>();
|
|
var provider = new PostgreSqlDatabaseProvider(dataSource);
|
|
provider.Initialise(optionsBuilder, new DatabaseConfigurationOptions { DatabaseType = "PostgreSQL" });
|
|
return new JellyfinDbContext(
|
|
optionsBuilder.Options,
|
|
NullLogger<JellyfinDbContext>.Instance,
|
|
provider,
|
|
new NoLockBehavior(NullLogger<NoLockBehavior>.Instance));
|
|
}
|
|
}
|