using FluentAssertions; using IBKRTrader.Core.Persistence.Ef; using IBKRTrader.Core.Persistence.Entities; using IBKRTrader.Core.Trading; using Microsoft.EntityFrameworkCore; namespace IBKRTrader.Tests.Trading; [Trait("cat", "unit")] public class DashboardServiceTests { private sealed class Factory(DbContextOptions o) : IDbContextFactory { public CoreDbContext CreateDbContext() => new(o); } private static (DashboardService sut, IDbContextFactory factory) Create() { var opts = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()).Options; var factory = new Factory(opts); return (new DashboardService(factory), factory); } [Fact] public async Task EmptyDatabase_ReturnsZeros() { var (sut, _) = Create(); var snap = await sut.GetSnapshotAsync(); snap.Should().Be(new DashboardSnapshot(0, 0m, 0)); } [Fact] public async Task AggregatesPositionsAndTrades() { var (sut, factory) = Create(); await using (var db = await factory.CreateDbContextAsync()) { db.Positions.Add(new CorePosition { Module = "CT", Symbol = "AAPL", Quantity = 5, AvgPrice = 100m }); db.Positions.Add(new CorePosition { Module = "CT", Symbol = "MSFT", Quantity = 2, AvgPrice = 200m }); db.TradeHistory.Add(new CoreTrade { Module = "CT", Symbol = "AAPL", Action = "BUY", Quantity = 5, Price = 100m }); await db.SaveChangesAsync(); } var snap = await sut.GetSnapshotAsync(); snap.OpenPositions.Should().Be(2); snap.TotalExposure.Should().Be(900m); // 5*100 + 2*200 snap.TotalTrades.Should().Be(1); } }