107 lines
4.2 KiB
C#
107 lines
4.2 KiB
C#
using System.Reflection;
|
||
using Gpulse.WCT.DataAnalyzer.Core.Infrastructure.Extensions;
|
||
using Gpulse.WCT.DataAnalyzer.Core.Infrastructure.LocalData;
|
||
using Microsoft.Data.Sqlite;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Configuration;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
|
||
namespace Gpulse.WCT.DataAnalyzer.Tests;
|
||
|
||
/// <summary>
|
||
/// CLI 测试宿主:用内存 SQLite 搭建与 Program.Main 一致的服务容器。
|
||
/// 每个测试实例持有独立的「本地库」/「发布库」连接与一个临时目录,互不污染。
|
||
/// </summary>
|
||
public sealed class CliTestHost : IDisposable
|
||
{
|
||
private readonly SqliteConnection _localConnection;
|
||
private readonly SqliteConnection _releaseConnection;
|
||
private readonly ServiceProvider _provider;
|
||
private readonly IServiceScope _scope;
|
||
|
||
/// <summary>测试作用域的服务提供者(与命令执行共用同一作用域)。</summary>
|
||
public IServiceProvider Services => _scope.ServiceProvider;
|
||
|
||
/// <summary>每个测试独立的临时目录,用于放置日志 fixture 文件。</summary>
|
||
public string TempDir { get; }
|
||
|
||
public WctMinerDbContext LocalDb => Services.GetRequiredService<WctMinerDbContext>();
|
||
public ReleaseDbContext ReleaseDb => Services.GetRequiredService<ReleaseDbContext>();
|
||
|
||
private CliTestHost(
|
||
SqliteConnection localConnection,
|
||
SqliteConnection releaseConnection,
|
||
ServiceProvider provider,
|
||
IServiceScope scope,
|
||
string tempDir)
|
||
{
|
||
_localConnection = localConnection;
|
||
_releaseConnection = releaseConnection;
|
||
_provider = provider;
|
||
_scope = scope;
|
||
TempDir = tempDir;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建测试宿主:本地库/发布库各用一条独立的内存 SQLite 连接(镜像生产环境的两个库文件),
|
||
/// 并做 EnsureCreated 与种子数据初始化。
|
||
/// </summary>
|
||
public static async Task<CliTestHost> CreateAsync()
|
||
{
|
||
var tempDir = Directory.CreateTempSubdirectory("wct-tests-").FullName;
|
||
|
||
// 两个库各自独立连接,避免共享连接时 EnsureCreated 相互干扰
|
||
var localConnection = new SqliteConnection("Data Source=:memory:");
|
||
await localConnection.OpenAsync();
|
||
|
||
var releaseConnection = new SqliteConnection("Data Source=:memory:");
|
||
await releaseConnection.OpenAsync();
|
||
|
||
// 从嵌入资源加载 appsettings.json,与 Program.Main 生产路径一致
|
||
var configuration = new ConfigurationBuilder()
|
||
.AddEmbeddedResourceJson(
|
||
typeof(Program).Assembly,
|
||
"Gpulse.WCT.DataAnalyzer.appsettings.json")
|
||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||
{
|
||
["Database:Type"] = "sqlite",
|
||
["Parser:BatchSize"] = "1000",
|
||
})
|
||
.Build();
|
||
|
||
var services = new ServiceCollection();
|
||
services.AddSingleton<IConfiguration>(configuration);
|
||
services.AddLogging();
|
||
services.AddDbContext<WctMinerDbContext>(o => o.UseSqlite(localConnection));
|
||
services.AddDbContext<ReleaseDbContext>(o => o.UseSqlite(releaseConnection));
|
||
services.AddApplicationServices();
|
||
|
||
var provider = services.BuildServiceProvider();
|
||
|
||
// 初始化表结构与种子数据(与 Program.Main 一致,经 EF 迁移建表)
|
||
using (var scope = provider.CreateScope())
|
||
{
|
||
var local = scope.ServiceProvider.GetRequiredService<WctMinerDbContext>();
|
||
await DatabaseMigrationInitializer.MigrateOrUpgradeAsync(local);
|
||
SeedData.Initialize(local);
|
||
|
||
var release = scope.ServiceProvider.GetRequiredService<ReleaseDbContext>();
|
||
await DatabaseMigrationInitializer.MigrateOrUpgradeAsync(release);
|
||
}
|
||
|
||
var hostScope = provider.CreateScope();
|
||
return new CliTestHost(localConnection, releaseConnection, provider, hostScope, tempDir);
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
_scope.Dispose();
|
||
_provider.Dispose();
|
||
_localConnection.Dispose();
|
||
_releaseConnection.Dispose();
|
||
|
||
try { Directory.Delete(TempDir, recursive: true); }
|
||
catch { /* 尽力清理临时目录 */ }
|
||
}
|
||
}
|