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();
}
}