using System; using FinlyticEngine.Database; using FinlyticEngine.Services.Trading; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; namespace FinlyticEngine.Tests.TestSupport; /// /// Builds a real wired against an EF Core InMemory-backed /// resolved through a genuine — the same /// DI shape production code uses (a fresh scoped DbContext per call). This is deliberately NOT a fake /// DbContext: using the real EngineDbContext against the InMemory provider means the tenant-filtering LINQ /// predicates in TradeLifecycleService are actually evaluated by EF Core, not bypassed. /// /// DB approach: see the "DB-Ansatz" section of the final task report for why InMemory was chosen over /// SQLite and Testcontainers/real Postgres. /// public sealed class TradeLifecycleServiceHarness : IDisposable { private readonly ServiceProvider _provider; public TradeLifecycleService Sut { get; } public FakeEngineRpcClient RpcClient { get; } public FakeSettingsService SettingsService { get; } public IServiceScopeFactory ScopeFactory { get; } public TradeLifecycleServiceHarness() { var dbName = Guid.NewGuid().ToString("N"); var services = new ServiceCollection(); services.AddDbContext(o => o.UseInMemoryDatabase(dbName)); _provider = services.BuildServiceProvider(); ScopeFactory = _provider.GetRequiredService(); RpcClient = new FakeEngineRpcClient(); SettingsService = new FakeSettingsService(); Sut = new TradeLifecycleService( ScopeFactory, new NeverInvokedCompositeOpportunityScorer(), new NeverInvokedAiReasoningGateService(), new NeverInvokedKnockOutDerivativeResolver(), RpcClient, SettingsService, new FakeFinlyticLogger()); } /// /// Opens a fresh scope and returns its , mirroring how the service itself /// obtains a DbContext per call. Caller is responsible for disposing the returned scope via /// semantics (use inside a using block on the returned context's /// owning scope where needed) — for simplicity in tests we just dispose the DbContext itself, since the /// InMemory provider keeps data keyed by database name, not by context instance. /// public EngineDbContext OpenDbContext() { var scope = ScopeFactory.CreateScope(); return scope.ServiceProvider.GetRequiredService(); } public void Dispose() => _provider.Dispose(); }