using MeterVault.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Testcontainers.PostgreSql;
namespace MeterVault.Integration.Tests;
///
/// Spins up exactly one TimescaleDB container per test run (shared via the "Timescale"
/// collection) and applies migrations once. Tests reuse it and isolate themselves with Respawn.
/// The image tag is pinned: the compression DDL (add_compression_policy) was renamed toward
/// add_columnstore_policy in newer Timescale, so a floating tag would risk breaking migrations.
///
public sealed class TimescaleFixture : IAsyncLifetime, IDbContextFactory
{
private readonly PostgreSqlContainer _db = new PostgreSqlBuilder("timescale/timescaledb:2.17.2-pg16")
.WithDatabase("metervault")
.WithUsername("metervault")
.WithPassword("metervault")
.Build();
public string ConnectionString => _db.GetConnectionString();
public MeterVaultDbContext CreateContext()
{
var options = new DbContextOptionsBuilder()
.UseNpgsql(ConnectionString, npgsql =>
npgsql.MigrationsAssembly(typeof(MeterVaultDbContext).Assembly.FullName))
.UseSnakeCaseNamingConvention()
.Options;
return new MeterVaultDbContext(options);
}
/// Lets tests construct services that take an .
public MeterVaultDbContext CreateDbContext() => CreateContext();
public async Task InitializeAsync()
{
await _db.StartAsync();
await using var ctx = CreateContext();
await ctx.Database.MigrateAsync();
// The fixtures are historical (2022–2023), so freshly-inserted chunks immediately fall
// past the 30-day compression horizon. Pause the compression job's scheduler so its
// background worker can't deadlock a test's import transaction. The policy still exists
// (SchemaTests verifies that); only its scheduling is disabled.
await ctx.Database.ExecuteSqlRawAsync(
"SELECT alter_job(job_id, scheduled => false) FROM timescaledb_information.jobs " +
"WHERE proc_name IN ('policy_compression', 'policy_columnstore');");
}
public async Task DisposeAsync() => await _db.DisposeAsync();
}
/// Binds the shared to all tests in the "Timescale" collection.
[CollectionDefinition("Timescale")]
public sealed class TimescaleCollection : ICollectionFixture;