using Microsoft.Data.Sqlite; using ZB.MOM.WW.LocalDb.Internal; namespace ZB.MOM.WW.LocalDb.Tests; public sealed class SqliteLocalDbTests : IDisposable { private readonly string _path = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".db"); public void Dispose() { SqliteConnection.ClearAllPools(); if (File.Exists(_path)) File.Delete(_path); } private SqliteLocalDb Create() => new(new LocalDbOptions { Path = _path }); [Fact] public void Open_CreatesDbFile_WalMode() { using var db = Create(); Assert.True(File.Exists(_path)); using var conn = db.CreateConnection(); using var cmd = conn.CreateCommand(); cmd.CommandText = "PRAGMA journal_mode"; var mode = (string)cmd.ExecuteScalar()!; Assert.Equal("wal", mode, ignoreCase: true); } [Fact] public async Task ExecuteAsync_ParameterizedInsert_Works() { using var db = Create(); await db.ExecuteAsync("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)"); await db.ExecuteAsync("INSERT INTO t (id, name) VALUES (@id, @name)", new { id = 1, name = "x" }); var rows = await db.QueryAsync("SELECT COUNT(*) FROM t", r => r.GetInt64(0)); Assert.Equal(1L, rows[0]); } [Fact] public async Task QueryAsync_MapsRows() { using var db = Create(); await db.ExecuteAsync("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)"); await db.ExecuteAsync("INSERT INTO t (id, name) VALUES (@id, @name)", new { id = 1, name = "a" }); await db.ExecuteAsync("INSERT INTO t (id, name) VALUES (@id, @name)", new { id = 2, name = "b" }); var rows = await db.QueryAsync( "SELECT id, name FROM t ORDER BY id", r => (Id: r.GetInt64(0), Name: r.GetString(1))); Assert.Equal(2, rows.Count); Assert.Equal((1L, "a"), rows[0]); Assert.Equal((2L, "b"), rows[1]); } [Fact] public async Task Transaction_CommitAndRollback() { using var db = Create(); await db.ExecuteAsync("CREATE TABLE t (id INTEGER PRIMARY KEY)"); await using (var tx = await db.BeginTransactionAsync()) { await tx.ExecuteAsync("INSERT INTO t (id) VALUES (@id)", new { id = 1 }); await tx.CommitAsync(default); } await using (var tx = await db.BeginTransactionAsync()) { await tx.ExecuteAsync("INSERT INTO t (id) VALUES (@id)", new { id = 2 }); await tx.RollbackAsync(default); } var ids = await db.QueryAsync("SELECT id FROM t ORDER BY id", r => r.GetInt64(0)); Assert.Equal(new[] { 1L }, ids); } [Fact] public async Task HlcUdf_RegisteredOnConnection() { using var db = Create(); var values = await db.QueryAsync( "SELECT zb_hlc_next() UNION ALL SELECT zb_hlc_next()", r => r.GetInt64(0)); Assert.Equal(2, values.Count); Assert.True(values[1] > values[0], $"{values[1]} should be greater than {values[0]}"); } [Fact] public async Task Hlc_ResumesFromDurableMax_OnReopen() { const long huge = 9_000_000_000_000_000L; using (var db = Create()) { await db.ExecuteAsync( "INSERT INTO __localdb_oplog (table_name, pk_json, hlc, node_id) VALUES ('t', '{}', @hlc, 'n')", new { hlc = huge }); } SqliteConnection.ClearAllPools(); using (var db = Create()) { var next = await db.QueryAsync("SELECT zb_hlc_next()", r => r.GetInt64(0)); Assert.True(next[0] > huge, $"{next[0]} should be greater than {huge}"); } } }