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;
|
|||
|
|
}
|
|||
|
|
}
|