using ErsatzTV.Infrastructure;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Sqlite.Data;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging.Abstractions;
namespace ErsatzTV.Tests.Support;
///
/// In-memory SQLite harness for handler/integration tests. A single
/// is kept open for the lifetime of the harness so the schema (built via
/// ) and data persist across the multiple
/// instances created by an .
/// Foreign keys are disabled so partial graphs can be seeded without satisfying every FK.
///
public sealed class InMemoryTvContext : IAsyncDisposable
{
private readonly SqliteConnection _connection;
private readonly DbContextOptions _options;
private InMemoryTvContext(SqliteConnection connection, DbContextOptions options)
{
_connection = connection;
_options = options;
}
public IDbContextFactory Factory => new TestDbContextFactory(_options);
public static async Task CreateAsync()
{
TvContext.IsSqlite = true;
TvContext.IsUniqueConstraintViolation = SqliteErrorClassifier.IsUniqueConstraintViolation;
TvContext.RegisterUnicodeCaseFunctions = SqliteUnicodeFunctions.Register;
var connection = new SqliteConnection("Data Source=:memory:;Foreign Keys=False");
await connection.OpenAsync();
DbContextOptions options = new DbContextOptionsBuilder()
.UseSqlite(connection)
.Options;
await using (TvContext context = Create(options))
{
await context.Database.EnsureCreatedAsync();
await context.Database.ExecuteSqlRawAsync("PRAGMA foreign_keys=OFF");
}
return new InMemoryTvContext(connection, options);
}
public TvContext CreateContext() => Create(_options);
public async ValueTask DisposeAsync()
{
await _connection.DisposeAsync();
}
private static TvContext Create(DbContextOptions options) =>
new(
options,
NullLoggerFactory.Instance,
new SlowQueryInterceptor(NullLogger.Instance));
private sealed class TestDbContextFactory(DbContextOptions options) : IDbContextFactory
{
public TvContext CreateDbContext() => Create(options);
}
}