using FluentAssertions; using IBKRTrader.Core.IBKR; using IBKRTrader.Core.Logging; using IBKRTrader.Core.Persistence.Ef; using Microsoft.EntityFrameworkCore; namespace IBKRTrader.Tests.IBKR; /// IBKR-Repo gegen EF-InMemory (Raw-SQL-Cross-Modul-Query wird hier nicht getestet). [Trait("cat", "unit")] public class IBKRMarketDataRepositoryTests { private sealed class Factory(DbContextOptions o) : IDbContextFactory { public CoreDbContext CreateDbContext() => new(o); } private static IBKRMarketDataRepository CreateSut() { var opts = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()).Options; return new IBKRMarketDataRepository(new Factory(opts), new LoggingService()); } [Fact] public async Task UpsertInstrument_IsIdempotent_ByConid_AndReturnsId() { var repo = CreateSut(); var id1 = await repo.UpsertInstrumentAsync(new IBKRInstrument { IbkrConid = 111, Symbol = "AAPL" }); var id2 = await repo.UpsertInstrumentAsync(new IBKRInstrument { IbkrConid = 111, Symbol = "AAPL2" }); id2.Should().Be(id1); (await repo.GetActiveInstrumentCountAsync()).Should().Be(1); (await repo.GetInstrumentByConidAsync(111))!.Symbol.Should().Be("AAPL2"); } [Fact] public async Task ExternalIdentifier_Dedup_AndLookup() { var repo = CreateSut(); var id = await repo.UpsertInstrumentAsync(new IBKRInstrument { IbkrConid = 222, Symbol = "MSFT" }); await repo.UpsertExternalIdentifierAsync(id, "capitoltrades", "MSFT"); await repo.UpsertExternalIdentifierAsync(id, "capitoltrades", "MSFT"); // Duplikat var found = await repo.FindInstrumentByExternalTickerAsync("capitoltrades", "MSFT"); found.Should().NotBeNull(); found!.IbkrConid.Should().Be(222); } [Fact] public async Task MarketData_Upsert_Count_AndLatest() { var repo = CreateSut(); await repo.UpsertMarketDataBatchAsync(new[] { new IBKRMarketBar { InstrumentId = 1, BarSize = "daily", Timestamp = new DateTime(2026, 1, 1), Close = 10m }, new IBKRMarketBar { InstrumentId = 1, BarSize = "daily", Timestamp = new DateTime(2026, 1, 2), Close = 11m }, }); // Upsert desselben Keys aktualisiert (kein zweiter Eintrag). await repo.UpsertMarketDataBatchAsync(new[] { new IBKRMarketBar { InstrumentId = 1, BarSize = "daily", Timestamp = new DateTime(2026, 1, 2), Close = 12m }, }); (await repo.GetBarCountAsync(1)).Should().Be(2); (await repo.GetLatestBarTimestampAsync(1)).Should().Be(new DateTime(2026, 1, 2)); } [Fact] public async Task LatestBarTimestamp_IsNull_WhenNoData() { var repo = CreateSut(); (await repo.GetLatestBarTimestampAsync(999)).Should().BeNull(); } }