121 lines
5.2 KiB
C#
121 lines
5.2 KiB
C#
using Gpulse.WCT.DataAnalyzer.Core.Infrastructure.LocalData;
|
||
using Microsoft.EntityFrameworkCore;
|
||
|
||
namespace Gpulse.WCT.DataAnalyzer.Tests.Commands;
|
||
|
||
/// <summary>
|
||
/// RV-001:EF 迁移替代 EnsureCreated。验证两类场景:
|
||
/// 1. 全新库经迁移建全部表(CliTestHost 已覆盖,此处断言关键新表存在)。
|
||
/// 2. 既有 EnsureCreated 库(无迁移历史但有表)经 MigrateOrUpgradeAsync 升级后补齐
|
||
/// 本变更新增的 3 张维度表 + test_scenario 的 3 列 FK。
|
||
/// </summary>
|
||
[TestFixture]
|
||
public class DatabaseMigrationInitializerTests
|
||
{
|
||
/// <summary>全新库经迁移建表后,3 张新维度表存在。</summary>
|
||
[Test]
|
||
public async Task MigrateOrUpgrade_FreshDb_CreatesNewDimensionTables()
|
||
{
|
||
var dbPath = Path.Combine(Path.GetTempPath(), $"wct_fresh_{Guid.NewGuid():N}.db");
|
||
try
|
||
{
|
||
await using var context = CreateContext(dbPath);
|
||
await DatabaseMigrationInitializer.MigrateOrUpgradeAsync(context);
|
||
|
||
Assert.Multiple(async () =>
|
||
{
|
||
Assert.That(await TableExistsAsync(context, "manufacturer"), Is.True);
|
||
Assert.That(await TableExistsAsync(context, "car_model"), Is.True);
|
||
Assert.That(await TableExistsAsync(context, "foreign_object"), Is.True);
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||
if (File.Exists(dbPath)) File.Delete(dbPath);
|
||
}
|
||
}
|
||
|
||
/// <summary>既有 EnsureCreated 库(旧 schema:无新表/FK 列)经升级补齐。</summary>
|
||
[Test]
|
||
public async Task MigrateOrUpgrade_LegacyEnsureCreatedDb_AddsNewTablesAndFkColumns()
|
||
{
|
||
var dbPath = Path.Combine(Path.GetTempPath(), $"wct_legacy_{Guid.NewGuid():N}.db");
|
||
try
|
||
{
|
||
// 1. 构造一个"旧 schema"库:test_scenario 无 manufacturer_id/car_model_id/foreign_object_id,
|
||
// 无 __EFMigrationsHistory(模拟 EnsureCreated 既有库)
|
||
CreateLegacyEnsureCreatedDatabase(dbPath);
|
||
|
||
// 2. 运行升级
|
||
await using var context = CreateContext(dbPath);
|
||
await DatabaseMigrationInitializer.MigrateOrUpgradeAsync(context);
|
||
|
||
// 3. 断言新维度表与 test_scenario 的新 FK 列已补齐
|
||
Assert.Multiple(async () =>
|
||
{
|
||
Assert.That(await TableExistsAsync(context, "manufacturer"), Is.True);
|
||
Assert.That(await TableExistsAsync(context, "car_model"), Is.True);
|
||
Assert.That(await TableExistsAsync(context, "foreign_object"), Is.True);
|
||
Assert.That(await ColumnExistsAsync(context, "test_scenario", "manufacturer_id"), Is.True);
|
||
Assert.That(await ColumnExistsAsync(context, "test_scenario", "car_model_id"), Is.True);
|
||
Assert.That(await ColumnExistsAsync(context, "test_scenario", "foreign_object_id"), Is.True);
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||
if (File.Exists(dbPath)) File.Delete(dbPath);
|
||
}
|
||
}
|
||
|
||
private static WctMinerDbContext CreateContext(string dbPath)
|
||
{
|
||
var dir = Path.GetDirectoryName(dbPath);
|
||
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
||
Directory.CreateDirectory(dir);
|
||
|
||
var options = new DbContextOptionsBuilder<WctMinerDbContext>()
|
||
.UseSqlite($"Data Source={dbPath};Pooling=False")
|
||
.Options;
|
||
return new WctMinerDbContext(options);
|
||
}
|
||
|
||
private static void CreateLegacyEnsureCreatedDatabase(string dbPath)
|
||
{
|
||
using var connection = new Microsoft.Data.Sqlite.SqliteConnection($"Data Source={dbPath};Pooling=False");
|
||
connection.Open();
|
||
using var cmd = connection.CreateCommand();
|
||
cmd.CommandText = """
|
||
CREATE TABLE test_scenario (
|
||
id TEXT NOT NULL PRIMARY KEY,
|
||
tx_panel_id TEXT,
|
||
tx_hardware_id TEXT,
|
||
tx_software_id TEXT,
|
||
rx_type_id TEXT,
|
||
test_purpose TEXT,
|
||
test_date TEXT,
|
||
test_sequence INTEGER,
|
||
is_deleted INTEGER NOT NULL DEFAULT 0,
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||
);
|
||
CREATE TABLE tx_panel (id TEXT NOT NULL PRIMARY KEY, name TEXT, is_deleted INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (datetime('now')));
|
||
""";
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
|
||
private static async Task<bool> TableExistsAsync(WctMinerDbContext context, string tableName)
|
||
{
|
||
var count = await context.Database.SqlQueryRaw<long>(
|
||
$"SELECT count(*) AS Value FROM sqlite_master WHERE type='table' AND name = '{tableName}'").FirstOrDefaultAsync();
|
||
return count > 0;
|
||
}
|
||
|
||
private static async Task<bool> ColumnExistsAsync(WctMinerDbContext context, string table, string column)
|
||
{
|
||
var count = await context.Database.SqlQueryRaw<long>(
|
||
$"SELECT count(*) AS Value FROM pragma_table_info('{table}') WHERE name = '{column}'").FirstOrDefaultAsync();
|
||
return count > 0;
|
||
}
|
||
}
|