using FluentAssertions; using IBKRTrader.Core.Logging; using IBKRTrader.Core.Persistence.Ef; using IBKRTrader.Core.Settings; using IBKRTrader.Modules.CongressTrading.Database; using IBKRTrader.Modules.CongressTrading.Models; using IBKRTrader.Modules.CongressTrading.Persistence.Ef; using Microsoft.EntityFrameworkCore; namespace IBKRTrader.Tests.Modules; /// EF-Repo des Moduls gegen EF-InMemory (deterministisch, kein externer DB-Zugriff). [Trait("cat", "unit")] public class CongressRepositoryTests { private sealed class Factory(DbContextOptions options) : IDbContextFactory where T : DbContext { public T CreateDbContext() => (T)Activator.CreateInstance(typeof(T), options)!; } private static CongressRepository CreateSut() { var ctOpts = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()).Options; var coreOpts = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()).Options; var settings = new CoreSettingsService(new Factory(coreOpts)); return new CongressRepository(new Factory(ctOpts), settings, new LoggingService()); } [Fact] public async Task UpsertMember_IsIdempotent_ByBioId() { var repo = CreateSut(); await repo.UpsertMemberAsync(new CongressMember { BioId = "W1", Name = "Alice" }); await repo.UpsertMemberAsync(new CongressMember { BioId = "W1", Name = "Alice B." }); (await repo.MemberExistsAsync("W1")).Should().BeTrue(); (await repo.GetMemberCountAsync()).Should().Be(1); (await repo.GetAllMemberBioIdsAsync()).Should().Contain("W1"); } [Fact] public async Task InsertTrade_IgnoresDuplicateTradeId() { var repo = CreateSut(); await repo.InsertTradeAsync(new CongressTrade { TradeId = "T1", MemberBioId = "W1", Ticker = "AAPL", TradeType = "buy" }); await repo.InsertTradeAsync(new CongressTrade { TradeId = "T1", MemberBioId = "W1", Ticker = "AAPL" }); (await repo.TradeExistsAsync("T1")).Should().BeTrue(); (await repo.GetTradeCountAsync()).Should().Be(1); } [Fact] public async Task DetailsFlow_MarksTradeComplete() { var repo = CreateSut(); await repo.InsertTradeAsync(new CongressTrade { TradeId = "T2" }, detailsFetched: false); (await repo.GetTradesWithoutDetailsAsync()).Should().Contain("T2"); await repo.UpdateTradeDetailsAsync(new CongressTrade { TradeId = "T2", Ticker = "MSFT", TradeType = "sell" }); (await repo.GetTradesWithoutDetailsAsync()).Should().NotContain("T2"); } [Fact] public async Task Settings_RoundTripThroughCoreSettings() { var repo = CreateSut(); await repo.SetSettingAsync("ct.history_import_page", "7"); (await repo.GetSettingAsync("ct.history_import_page")).Should().Be("7"); await repo.DeleteSettingAsync("ct.history_import_page"); (await repo.GetSettingAsync("ct.history_import_page")).Should().BeNull(); } }