Files
EIS-dotnet/tests/Eis.Infrastructure.Tests/Data/DatabaseInitializerTests.cs
T
biss b139834396 已完成迁移验收和清理。
删除全部 Node.js 后端、.mjs 测试、辅助脚本、package*.json、node_modules 和迁移对照工具。
浏览器前端保留并迁入 [Eis.Web/wwwroot](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Web/wwwroot/index.html),统一改为 .js 模块。
修复一个隐藏遗漏:.NET 仍依赖 schema.mjs,且 MySQL 结构解析可能被转义反引号截断。现已改为原生 [SQLite SQL](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Data/Schema.sqlite.sql) 和 [MySQL SQL](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Data/Schema.mysql.sql) 资源。
更新了 [DatabaseInitializer.cs](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Data/DatabaseInitializer.cs)、项目配置、README 和迁移文档。
data 目录和现有数据库未修改;测试仅使用系统临时 SQLite 数据库。
2026-07-23 17:49:57 +08:00

84 lines
3.9 KiB
C#

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(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<long> ScalarAsync(SqliteConnection connection, string sql)
{
await using var command = connection.CreateCommand();
command.CommandText = sql;
return Convert.ToInt64(await command.ExecuteScalarAsync());
}
private static async Task<string> 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();
}
}