64 lines
2.8 KiB
C#
64 lines
2.8 KiB
C#
using System;
|
|
using FinlyticEngine.Database;
|
|
using FinlyticEngine.Services.Trading;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace FinlyticEngine.Tests.TestSupport;
|
|
|
|
/// <summary>
|
|
/// Builds a real <see cref="TradeLifecycleService"/> wired against an EF Core InMemory-backed
|
|
/// <see cref="EngineDbContext"/> resolved through a genuine <see cref="IServiceScopeFactory"/> — 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.
|
|
/// </summary>
|
|
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<EngineDbContext>(o => o.UseInMemoryDatabase(dbName));
|
|
_provider = services.BuildServiceProvider();
|
|
|
|
ScopeFactory = _provider.GetRequiredService<IServiceScopeFactory>();
|
|
RpcClient = new FakeEngineRpcClient();
|
|
SettingsService = new FakeSettingsService();
|
|
|
|
Sut = new TradeLifecycleService(
|
|
ScopeFactory,
|
|
new NeverInvokedCompositeOpportunityScorer(),
|
|
new NeverInvokedAiReasoningGateService(),
|
|
new NeverInvokedKnockOutDerivativeResolver(),
|
|
RpcClient,
|
|
SettingsService,
|
|
new FakeFinlyticLogger<TradeLifecycleService>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens a fresh scope and returns its <see cref="EngineDbContext"/>, mirroring how the service itself
|
|
/// obtains a DbContext per call. Caller is responsible for disposing the returned scope via
|
|
/// <see cref="IServiceScope"/> semantics (use inside a <c>using</c> 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.
|
|
/// </summary>
|
|
public EngineDbContext OpenDbContext()
|
|
{
|
|
var scope = ScopeFactory.CreateScope();
|
|
return scope.ServiceProvider.GetRequiredService<EngineDbContext>();
|
|
}
|
|
|
|
public void Dispose() => _provider.Dispose();
|
|
}
|