using Eis.Infrastructure.Authentication; using Eis.Infrastructure.Data; using Microsoft.Data.Sqlite; namespace Eis.Infrastructure.Tests.Data; public sealed class DatabaseInitializerTests { [Fact] public async Task EmbeddedSchemas_AreNativeSqlResources() { var assembly = typeof(DatabaseInitializer).Assembly; var resourceNames = assembly.GetManifestResourceNames(); var sqliteName = Assert.Single(resourceNames, name => name.EndsWith("Schema.sqlite.sql", StringComparison.Ordinal)); var mysqlName = Assert.Single(resourceNames, name => name.EndsWith("Schema.mysql.sql", StringComparison.Ordinal)); var sqlite = await ReadResourceAsync(assembly, sqliteName); var mysql = (await ReadResourceAsync(assembly, mysqlName)).ReplaceLineEndings("\n"); var mysqlStatements = mysql.Split( "\n-- EIS-STATEMENT\n", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); Assert.Contains("CREATE TABLE IF NOT EXISTS schema_metadata", sqlite, StringComparison.Ordinal); Assert.DoesNotContain("export const", sqlite, StringComparison.Ordinal); Assert.True(mysqlStatements.Length >= 30); Assert.All(mysqlStatements, statement => Assert.StartsWith("CREATE TABLE IF NOT EXISTS", statement, StringComparison.Ordinal)); Assert.Contains("`separator` VARCHAR(10)", mysql, StringComparison.Ordinal); Assert.DoesNotContain("export const", mysql, StringComparison.Ordinal); } [Fact] public async Task InitializeAsync_CreatesFreshDatabaseAndIsIdempotent() { var root = Path.Combine(Path.GetTempPath(), $"eis-fresh-init-{Guid.NewGuid():N}"); var path = Path.Combine(root, "eis.sqlite"); Directory.CreateDirectory(root); try { var options = DatabaseOptions.CreateSqliteForTests(path); var factory = new RelationalConnectionFactory(options); var passwords = new PasswordCompatibilityService(); var initializer = new DatabaseInitializer(factory, options, passwords); await initializer.InitializeAsync(CancellationToken.None); await initializer.InitializeAsync(CancellationToken.None); await using var connection = new SqliteConnection($"Data Source={path}"); await connection.OpenAsync(); Assert.True(await ScalarAsync(connection, "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'") >= 32); Assert.Equal(20, await ScalarAsync(connection, "SELECT schema_version FROM schema_metadata WHERE id = 1")); Assert.Equal(1, await ScalarAsync(connection, "SELECT COUNT(*) FROM users WHERE id = 'usr_admin' AND role = 'admin' AND admin_level = 'super'")); Assert.Equal("-", await ScalarTextAsync(connection, "SELECT `separator` FROM number_rules WHERE id = 'rule_default'")); Assert.Equal(5, await ScalarAsync(connection, "SELECT COUNT(*) FROM workflow_definitions WHERE active = 1")); Assert.Equal(4, await ScalarAsync(connection, "SELECT COUNT(*) FROM admission_number_rules WHERE active = 1")); await connection.DisposeAsync(); SqliteConnection.ClearAllPools(); } finally { if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } } private static async Task ScalarAsync(SqliteConnection connection, string sql) { await using var command = connection.CreateCommand(); command.CommandText = sql; return Convert.ToInt64(await command.ExecuteScalarAsync()); } private static async Task ScalarTextAsync(SqliteConnection connection, string sql) { await using var command = connection.CreateCommand(); command.CommandText = sql; return Convert.ToString(await command.ExecuteScalarAsync()) ?? ""; } private static async Task ReadResourceAsync(System.Reflection.Assembly assembly, string name) { await using var stream = assembly.GetManifestResourceStream(name) ?? throw new InvalidOperationException($"Missing resource: {name}"); using var reader = new StreamReader(stream); return await reader.ReadToEndAsync(); } }