fix(localdb): Task 4 review fixes — synchronous allow-list, dictionary params, async conn setup, dispose guard

This commit is contained in:
Joseph Doherty
2026-07-17 21:11:07 -04:00
parent 827bf44684
commit 12b8839f33
3 changed files with 148 additions and 20 deletions
@@ -82,6 +82,80 @@ public sealed class SqliteLocalDbTests : IDisposable
Assert.Equal(new[] { 1L }, ids);
}
[Fact]
public async Task Transaction_DisposeWithoutCommit_RollsBack()
{
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 });
}
var count = await db.QueryAsync("SELECT COUNT(*) FROM t", r => r.GetInt64(0));
Assert.Equal(0L, count[0]);
}
[Fact]
public async Task Bind_GuidByteArrayAndNull_RoundTrip()
{
using var db = Create();
await db.ExecuteAsync("CREATE TABLE t (id INTEGER PRIMARY KEY, g TEXT, blob BLOB, missing TEXT)");
var guid = Guid.NewGuid();
var bytes = new byte[] { 1, 2, 3 };
await db.ExecuteAsync(
"INSERT INTO t (id, g, blob, missing) VALUES (@id, @g, @blob, @missing)",
new { id = 1, g = guid, blob = bytes, missing = (string?)null });
var rows = await db.QueryAsync(
"SELECT g, blob, missing FROM t WHERE id = 1",
r => (Guid: r.GetGuid(0), Blob: (byte[])r.GetValue(1), MissingIsNull: r.IsDBNull(2)));
Assert.Equal(guid, rows[0].Guid);
Assert.Equal(bytes, rows[0].Blob);
Assert.True(rows[0].MissingIsNull);
}
[Fact]
public async Task Bind_DictionaryParameters_BindByKey()
{
using var db = Create();
await db.ExecuteAsync("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)");
var parameters = new Dictionary<string, object?> { ["id"] = 7, ["name"] = null };
await db.ExecuteAsync("INSERT INTO t (id, name) VALUES (@id, @name)", parameters);
var rows = await db.QueryAsync(
"SELECT id, name FROM t",
r => (Id: r.GetInt64(0), NameIsNull: r.IsDBNull(1)));
Assert.Equal(7L, rows[0].Id);
Assert.True(rows[0].NameIsNull);
}
[Fact]
public void Ctor_EmptyPath_Throws()
{
Assert.Throws<ArgumentException>(() => new SqliteLocalDb(new LocalDbOptions { Path = " " }));
}
[Fact]
public void Ctor_InvalidSynchronous_Throws()
{
Assert.Throws<ArgumentException>(() =>
new SqliteLocalDb(new LocalDbOptions { Path = _path, Synchronous = "NORMAL; PRAGMA foreign_keys=OFF" }));
}
[Fact]
public void Dispose_IsIdempotent()
{
var db = Create();
db.Dispose();
db.Dispose();
}
[Fact]
public async Task HlcUdf_RegisteredOnConnection()
{