feat(engine): add FinlyticEngine microservice with trade lifecycle, AI reasoning gate, composite scoring, and unit tests
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
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();
|
||||
}
|
||||
Reference in New Issue
Block a user