using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using ProposalSystem.Infrastructure.Data; namespace ProposalSystem.Tests.Helpers; /// /// Creates SQLite-backed in-memory DbContext instances for tests that require relational /// features (e.g., ExecuteSqlRawAsync, transactions). The SQLite connection is kept open /// for the lifetime of the context; disposing the context closes the connection and /// discards the in-memory database. /// /// Registers stub Postgres functions (hashtext, pg_advisory_xact_lock) so that /// production SQL using these functions does not throw in the test environment. /// public static class SqliteDbContextFactory { /// /// Creates a new ProposalDbContext backed by a unique SQLite in-memory database. /// The returned context owns the connection; dispose the context to clean up. /// public static ProposalDbContext Create() { var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); // Register Postgres-specific function stubs so ExecuteSqlRawAsync calls // like "SELECT pg_advisory_xact_lock(hashtext('...'))" succeed in SQLite. RegisterPostgresStubs(connection); var options = new DbContextOptionsBuilder() .UseSqlite(connection) .Options; var context = new ProposalDbContext(options); context.Database.EnsureCreated(); return context; } /// /// Opens a shared in-memory database and returns the connection plus a factory /// for additional contexts over the SAME database — needed by optimistic- /// concurrency tests that simulate two competing writers. Callers dispose the /// connection after the contexts. /// public static (SqliteConnection Connection, Func ContextFactory) CreateShared() { var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); RegisterPostgresStubs(connection); var options = new DbContextOptionsBuilder() .UseSqlite(connection) .Options; var first = new ProposalDbContext(options); first.Database.EnsureCreated(); first.Dispose(); return (connection, () => new ProposalDbContext(options)); } private static void RegisterPostgresStubs(SqliteConnection connection) { // hashtext(text) -> integer — returns a constant; only needed so SQL parses connection.CreateFunction("hashtext", (string _) => 0); // pg_advisory_xact_lock(bigint) -> void — no-op in tests connection.CreateFunction("pg_advisory_xact_lock", (long _) => 0); } }