using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using ZB.MOM.WW.LocalDb; namespace ZB.MOM.WW.ScadaBridge.TestSupport; /// /// A real over a temp file, for tests that construct a /// LocalDb-backed store directly. /// /// /// /// The stores take rather than a connection string, and there is /// no in-memory mode — LocalDbOptions.Path is a filesystem path. A real one is /// used rather than a stub on purpose: connections handed out by ILocalDb carry /// the zb_hlc_next() UDF that the capture triggers call, so a stub returning a /// bare SqliteConnection would pass these tests while failing closed the moment /// the table is registered for replication. /// /// /// No onReady callback and no RegisterReplicated: each store's own schema /// initialization creates its table, which is what keeps a directly-constructed store /// self-sufficient. Registration is the host's job (SiteLocalDbSetup.OnReady). /// /// /// This lives in a shared support project rather than being copied per test project: /// seven test projects construct stores that now take , and /// duplicating the fixture would mean seven places to keep the WAL-sidecar cleanup and /// the "real, not stubbed" rationale correct. /// /// public sealed class TestLocalDb : IDisposable { private readonly ServiceProvider _provider; private TestLocalDb(ServiceProvider provider, ILocalDb db) { _provider = provider; Db = db; } /// The live local database. public ILocalDb Db { get; } /// Opens a local database at . public static TestLocalDb Create(string databasePath) { var configuration = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { ["LocalDb:Path"] = databasePath, }) .Build(); var provider = new ServiceCollection() .AddZbLocalDb(configuration) .BuildServiceProvider(); return new TestLocalDb(provider, provider.GetRequiredService()); } /// /// Creates a local database at a fresh temp path. Dispose the instance, then call /// with to clean up. /// public static TestLocalDb CreateTemp(string prefix = "localdb") { var path = System.IO.Path.Combine( System.IO.Path.GetTempPath(), $"{prefix}-{Guid.NewGuid():N}.db"); var db = Create(path); db.Path = path; return db; } /// The filesystem path, when created via . public string Path { get; private set; } = string.Empty; /// /// Deletes and its WAL sidecars. Call only after the /// owning is disposed — the master connection anchors the WAL. /// public static void DeleteFiles(string databasePath) { foreach (var suffix in new[] { "", "-wal", "-shm" }) { try { File.Delete(databasePath + suffix); } catch { /* best effort */ } } } public void Dispose() => _provider.Dispose(); }