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;
///
/// CLI 测试宿主:用内存 SQLite 搭建与 Program.Main 一致的服务容器。
/// 每个测试实例持有独立的「本地库」/「发布库」连接与一个临时目录,互不污染。
///
public sealed class CliTestHost : IDisposable
{
private readonly SqliteConnection _localConnection;
private readonly SqliteConnection _releaseConnection;
private readonly ServiceProvider _provider;
private readonly IServiceScope _scope;
/// 测试作用域的服务提供者(与命令执行共用同一作用域)。
public IServiceProvider Services => _scope.ServiceProvider;
/// 每个测试独立的临时目录,用于放置日志 fixture 文件。
public string TempDir { get; }
public WctMinerDbContext LocalDb => Services.GetRequiredService();
public ReleaseDbContext ReleaseDb => Services.GetRequiredService();
private CliTestHost(
SqliteConnection localConnection,
SqliteConnection releaseConnection,
ServiceProvider provider,
IServiceScope scope,
string tempDir)
{
_localConnection = localConnection;
_releaseConnection = releaseConnection;
_provider = provider;
_scope = scope;
TempDir = tempDir;
}
///
/// 创建测试宿主:本地库/发布库各用一条独立的内存 SQLite 连接(镜像生产环境的两个库文件),
/// 并做 EnsureCreated 与种子数据初始化。
///
public static async Task 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
{
["Database:Type"] = "sqlite",
["Parser:BatchSize"] = "1000",
})
.Build();
var services = new ServiceCollection();
services.AddSingleton(configuration);
services.AddLogging();
services.AddDbContext(o => o.UseSqlite(localConnection));
services.AddDbContext(o => o.UseSqlite(releaseConnection));
services.AddApplicationServices();
var provider = services.BuildServiceProvider();
// 初始化表结构与种子数据(与 Program.Main 一致,经 EF 迁移建表)
using (var scope = provider.CreateScope())
{
var local = scope.ServiceProvider.GetRequiredService();
await DatabaseMigrationInitializer.MigrateOrUpgradeAsync(local);
SeedData.Initialize(local);
var release = scope.ServiceProvider.GetRequiredService();
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 { /* 尽力清理临时目录 */ }
}
}