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); } }