using System; using System.Threading.Tasks; using FinlyticEngine.Database; using FinlyticEngine.Services.Trading; using FinlyticEngine.Tests.TestSupport; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Xunit; using Xunit.Abstractions; namespace FinlyticEngine.Tests._Verify; /// /// ONE-OFF verification against a real, throwaway, locally-run PostgreSQL container (NOT the OmniDB /// production database — a brand-new container started solely for this check, no compose.yaml/appsettings /// connection strings involved) to settle whether the AddTradeFillAsync DbUpdateConcurrencyException /// reproduced under EF InMemory/SQLite is a provider artifact or a genuine, provider-independent EF Core /// change-tracking defect that would also occur in production. Deleted after the verdict is recorded. /// public class PostgresVerificationTests { private readonly ITestOutputHelper _output; public PostgresVerificationTests(ITestOutputHelper output) => _output = output; private const string ConnString = "Host=localhost;Port=55987;Database=finlytic_verify;Username=postgres;Password=test"; [Fact] public async Task RealPostgres_AddTradeFillAsync_ExactProductionCallPath_OwnerSucceeds() { var services = new ServiceCollection(); services.AddDbContext(o => o.UseNpgsql(ConnString)); await using var provider = services.BuildServiceProvider(); await using (var schemaDb = provider.GetRequiredService()) { await schemaDb.Database.EnsureDeletedAsync(); await schemaDb.Database.EnsureCreatedAsync(); } var scopeFactory = provider.GetRequiredService(); var owner = Guid.NewGuid(); var trade = TestData.ActiveTrade(owner); using (var scope = scopeFactory.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); db.Trades.Add(trade); await db.SaveChangesAsync(); } var sut = new TradeLifecycleService( scopeFactory, new NeverInvokedCompositeOpportunityScorer(), new NeverInvokedAiReasoningGateService(), new NeverInvokedKnockOutDerivativeResolver(), new FakeEngineRpcClient(), new FakeSettingsService(), new FakeFinlyticLogger()); // This calls the REAL, unmodified TradeLifecycleService.AddTradeFillAsync exactly as production code // does, against a real PostgreSQL instance. Exception? caught = null; try { var dto = await sut.AddTradeFillAsync(owner, trade.Id, 105m, 1m); _output.WriteLine($"SUCCEEDED. Trade {dto.TradeId} now has {dto.Fills.Count} fill(s)."); } catch (Exception ex) { caught = ex; _output.WriteLine($"THREW: {ex.GetType().FullName}: {ex.Message}"); } Assert.Null(caught); } }