using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using ZB.MOM.WW.LocalDb; namespace ZB.MOM.WW.ScadaBridge.Host.Tests; /// /// Pins that a site node creates the directory holding LocalDb:Path before the /// database is opened. /// /// /// /// This guarantee used to live in StoreAndForwardStorage.EnsureDatabaseDirectoryExists, /// which created the parent directory for its own SQLite file. LocalDb Phase 2 moved that /// file into the consolidated database — but the LocalDb library does not create the /// directory, and SqliteLocalDb opens the file eagerly in its constructor, so a /// missing directory is a hard boot failure ("SQLite Error 14: unable to open database /// file") rather than a degraded start. /// /// /// This is not hypothetical: the default site configuration points at the relative /// path ./data/site-localdb.db, so any site node whose working directory has no /// data/ subdirectory hits it. The docker rig escapes only because its volume mount /// creates /app/data — which is exactly the kind of coincidence that hides a defect /// until a bare-metal or fresh deployment. /// /// public class SiteLocalDbDirectoryTests { [Fact] public void SiteRegistration_CreatesTheLocalDbDirectory_WhenItDoesNotExist() { var root = Path.Combine(Path.GetTempPath(), "localdb-dir-test-" + Guid.NewGuid().ToString("N")); var dbPath = Path.Combine(root, "nested", "site-localdb.db"); Assert.False(Directory.Exists(root)); try { var config = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { ["LocalDb:Path"] = dbPath, }) .Build(); // The registration path under test. Resolving ILocalDb is what actually opens // the file, so this fails with SQLite Error 14 if the directory step regresses. var services = new ServiceCollection(); SiteLocalDbDirectory.Ensure(config); services.AddZbLocalDb(config); using var provider = services.BuildServiceProvider(); using var scope = provider.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); Assert.NotNull(db); Assert.True(Directory.Exists(Path.GetDirectoryName(dbPath)!)); Assert.True(File.Exists(dbPath)); } finally { SqliteConnectionPoolCleanup(); if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } } private static void SqliteConnectionPoolCleanup() => Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools(); }