test(inboundapi): root the shared in-memory SQLite keep-alive so the database cannot vanish mid-test

Script_UsingDatabase_QueriesViaGateway failed in about 2 of 27 full InboundAPI runs. Looping the
suite 30 times captured the failure 4 times, always as "Internal script error". The exception
underneath was SqliteException 'no such table: Machine'.

Cause: the seeding helper opened its keep-alive SqliteConnection in a local. Once the helper
returned, nothing referenced that connection. After a GC (likely during the Roslyn compile, under
full-suite load), Microsoft.Data.Sqlite's pool reclaimed the leaked connection and closed the last
connection to the shared in-memory database, which destroyed it.

Fix: hold the keep-alive in a static field. InboundDatabaseHelperTests had the same latent bug and
gets the same fix. The test now forces GC plus SqliteConnection.ClearAllPools() before the query.
That makes the old pattern fail deterministically and the fix pass deterministically.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
Joseph Doherty
2026-11-10 06:12:34 -05:00
parent edd0ca672f
commit 463aced7a1
2 changed files with 42 additions and 6 deletions
@@ -33,11 +33,25 @@ public class InboundDatabaseHelperTests
=> throw new NotImplementedException();
}
private static SqliteGateway SeededGateway(bool honorTokenOnOpen = true)
/// <summary>
/// Keep-alive for the shared in-memory database, rooted in a static field. It used to be a
/// local in the seeding helper: once the helper returned nothing referenced it, so after a GC the
/// Sqlite pool could reclaim the leaked connection, close the last connection and destroy the
/// database before the test's query ran (<c>no such table: Machine</c>). This is the same latent
/// flake the executor tests hit.
/// </summary>
private static readonly SqliteConnection KeepAlive = OpenKeepAlive();
private static SqliteConnection OpenKeepAlive()
{
var keep = new SqliteConnection("DataSource=file:movein?mode=memory&cache=shared");
keep.Open(); // keep-alive: shared in-memory db lives until process exit
using var cmd = keep.CreateCommand();
keep.Open(); // lives until process exit
return keep;
}
private static SqliteGateway SeededGateway(bool honorTokenOnOpen = true)
{
using var cmd = KeepAlive.CreateCommand();
cmd.CommandText = "CREATE TABLE IF NOT EXISTS Machine(Code TEXT, SAPID TEXT); DELETE FROM Machine; INSERT INTO Machine VALUES('Z28061A','131453');";
cmd.ExecuteNonQuery();
return new SqliteGateway("DataSource=file:movein?mode=memory&cache=shared", honorTokenOnOpen);