using FluentAssertions; using IBKRTrader.Core.Logging; using IBKRTrader.Core.Persistence.Ef; using IBKRTrader.Core.Persistence.Entities; using Microsoft.EntityFrameworkCore; namespace IBKRTrader.Tests.Persistence; /// Entscheidungsjournal + Order-Event-Log gegen EF-InMemory, inkl. Robustheits-Garantie. [Trait("cat", "unit")] public class AnalysisJournalsTests { private sealed class Factory(DbContextOptions options) : IDbContextFactory where T : DbContext { public T CreateDbContext() => (T)Activator.CreateInstance(typeof(T), options)!; } /// Factory, die immer wirft – simuliert einen DB-Ausfall. private sealed class ThrowingFactory : IDbContextFactory { public CoreDbContext CreateDbContext() => throw new InvalidOperationException("DB weg"); } private static Factory InMemory() => new(new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()).Options); [Fact] public void DecisionJournal_WritesAndQueriesBack() { var journal = new EfDecisionJournal(InMemory(), new LoggingService()); journal.Write(new CoreDecisionRecord { SignalId = "sig-1", Module = "CT", Symbol = "AAPL", Side = "BUY", Decision = TradeDecision.Rejected, Reason = DecisionReason.RiskRejected, Message = "Limit" }); var rows = journal.Query(d => d.SignalId == "sig-1"); rows.Should().HaveCount(1); rows[0].Reason.Should().Be(DecisionReason.RiskRejected); } [Fact] public void OrderEventLog_WritesAndQueriesBack() { var log = new EfOrderEventLog(InMemory(), new LoggingService()); log.Write(new CoreOrderEvent { SignalId = "sig-2", Module = "CT", Symbol = "AAPL", EventType = OrderEventType.Filled, Side = "BUY", Quantity = 5, Price = 100m, Response = "OK" }); var rows = log.Query(e => e.SignalId == "sig-2"); rows.Should().HaveCount(1); rows[0].EventType.Should().Be(OrderEventType.Filled); } [Fact] public void Write_NeverThrows_OnDbFailure() { var journal = new EfDecisionJournal(new ThrowingFactory(), new LoggingService()); var log = new EfOrderEventLog(new ThrowingFactory(), new LoggingService()); var writeJournal = () => journal.Write(new CoreDecisionRecord { SignalId = "x" }); var writeEvent = () => log.Write(new CoreOrderEvent { SignalId = "x" }); writeJournal.Should().NotThrow(); writeEvent.Should().NotThrow(); } }