Files
IBKRTrader/tests/IBKRTrader.Tests/IBKR/IBKRMarketDataRepositoryTests.cs
Richard 475c0b41cb @
R3 Slice 4: IBKR-Marktdaten auf EF; Dapper vollstaendig entfernt

- 3 IBKR-Entities (IBKRInstrument/IBKRMarketBar/IBKRExternalIdentifier) im CoreDbContext
  (core_ibkr_instruments/_market_data/_external_identifiers); Migration AddIbkr
- IBKRMarketDataRepository von Dapper auf EF (IDbContextFactory<CoreDbContext>);
  Cross-Modul-Query (ct_trade) via Database.SqlQueryRaw
- DatabaseService, IBKRMigrations, Dapper-Package entfernt; Laufzeit-Migrationen komplett weg
  (Schema extern via dotnet ef database update)
- Tests: +4 IBKRMarketDataRepository (EF-InMemory) -> 43/43 gruen; Build + smoke-ui + App-Start ok

R3 abgeschlossen: Persistenz vollstaendig auf EF Core.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
2026-07-28 18:29:00 +02:00

79 lines
2.9 KiB
C#

using FluentAssertions;
using IBKRTrader.Core.IBKR;
using IBKRTrader.Core.Logging;
using IBKRTrader.Core.Persistence.Ef;
using Microsoft.EntityFrameworkCore;
namespace IBKRTrader.Tests.IBKR;
/// <summary>IBKR-Repo gegen EF-InMemory (Raw-SQL-Cross-Modul-Query wird hier nicht getestet).</summary>
[Trait("cat", "unit")]
public class IBKRMarketDataRepositoryTests
{
private sealed class Factory(DbContextOptions<CoreDbContext> o) : IDbContextFactory<CoreDbContext>
{
public CoreDbContext CreateDbContext() => new(o);
}
private static IBKRMarketDataRepository CreateSut()
{
var opts = new DbContextOptionsBuilder<CoreDbContext>()
.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();
}
}