using ErrorOr; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Musify.Application.Contracts; using Musify.Domain.Entities; using Musify.Infrastructure.Persistence; namespace Musify.Application.Tests.TestSupport; public sealed class TestDatabase : DbContext, IDatabase { private readonly SqliteConnection connection; private TestDatabase(SqliteConnection connection, DbContextOptions options) : base(options) { this.connection = connection; } public static async Task CreateAsync() { var connection = new SqliteConnection("Filename=:memory:"); await connection.OpenAsync(); var options = new DbContextOptionsBuilder() .UseSqlite(connection) .Options; var database = new TestDatabase(connection, options); await database.Database.EnsureCreatedAsync(); return database; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); ModelConfiguration.Apply(modelBuilder); } public DbSet Users => Set(); public DbSet PlayLists => Set(); public DbSet Albums => Set(); public DbSet AlbumHasTracks => Set(); public DbSet Tracks => Set(); public DbSet PlayListHasTracks => Set(); public DbSet Mixes => Set(); public DbSet MixItems => Set(); public DbSet UploadIntents => Set(); public DbSet ListeningHistories => Set(); public DbSet TrackLikes => Set(); public DbSet TrackTags => Set(); public DbSet UserFollows => Set(); public async Task BeginTransactionAsync(System.Data.IsolationLevel isolationLevel, CancellationToken cancellationToken) { var transaction = await Database.BeginTransactionAsync(isolationLevel, cancellationToken); return new SqliteDatabaseTransaction(transaction); } public async Task> TrySaveChangesAsync(Error onUniqueViolation, CancellationToken cancellationToken) { try { await SaveChangesAsync(cancellationToken); return Result.Success; } catch (DbUpdateException exception) when (exception.InnerException is SqliteException { SqliteErrorCode: 19 }) { return onUniqueViolation; } } public override async ValueTask DisposeAsync() { await base.DisposeAsync(); await connection.DisposeAsync(); } private sealed class SqliteDatabaseTransaction(Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction inner) : IDatabaseTransaction { public Task CommitAsync(CancellationToken cancellationToken = default) => inner.CommitAsync(cancellationToken); public ValueTask DisposeAsync() => inner.DisposeAsync(); } }