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(); var configuration = new ConfigurationBuilder() .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 一致) using (var scope = provider.CreateScope()) { var local = scope.ServiceProvider.GetRequiredService(); await local.Database.EnsureCreatedAsync(); SeedData.Initialize(local); var release = scope.ServiceProvider.GetRequiredService(); await release.Database.EnsureCreatedAsync(); } 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 { /* 尽力清理临时目录 */ } } }