Alles Entfernte war nachweislich ohne Aufrufer. Build, 198/198 Tests, Smoke-UI
und Daemon-Prueflauf sind vor und nach jedem Schritt gruen.
Code:
- AIModelService: Platzhalter, der immer 0.5 lieferte. Im DI registriert,
aber nie irgendwo injiziert. Ordner Core/AI faellt mit weg.
- CtApiWrapper + CtMeta: JSON-Modelle fuer einen {data,meta}-Umschlag, den
CapitolTrades nicht mehr liefert. Der Scraper deserialisiert seit laengerem
direkt List<CtTrade>.
- IBKRGatewayService: DisconnectAsync, InitBrokerageSessionAsync und
SearchStocksBySymbolAsync. Der Dienst selbst bleibt - er versorgt
Instrument-Sync, Kurshistorie und den Watchdog-Heartbeat.
- Je eine Methode ohne Aufrufer: BudgetService.GetAvailableBudgetAsync,
TradeHistoryService.GetRecentTradesAsync, CongressRepository.
GetAllTradeIdsAsync und .ResetHistoryImportAsync, IbkrMapping.DefaultPortFor,
SecretProtection.IsEncrypted.
- CongressRepository bekam damit einen LoggingService injiziert, den es nicht
mehr benutzt - Abhaengigkeit samt Konstruktorparameter raus.
Ressourcen:
- 17 Symbole der WinForms-Oberflaeche entfernt. Das Wildcard-Muster im csproj
nahm sie in die Binaerdatei auf, ViewIcons.cs bildet aber nur sieben
Schluessel ab. Resources/ enthaelt jetzt genau die sieben.
NuGet-Allowlist:
- Dapper und HtmlAgilityPack sind seit R3 bzw. R1 aus dem Projekt raus,
Microsoft.WindowsDesktop.* seit L5. Muster entfernt.
- MySqlConnector und Newtonsoft.Json stehen NUR transitiv in den
Projektdateien und wurden zuerst mitentfernt - ein Restore in einen leeren
Paket-Ordner scheiterte darauf mit NU1100. Beide wieder aufgenommen, jetzt
mit Begruendung, damit der naechste Aufraeumlauf nicht dieselbe Falle tritt.
Dokumente an den tatsaechlichen Stand angeglichen:
- ARCHITECTURE: R2 fuehrte die Umstellung auf IHostedService als offen, obwohl
R4 sie erledigt hat. L6 und die Deploymentcenter-Phase fehlten ganz.
- DC-Konzept: Schritte 0-8 standen auf "dieser Durchlauf", sind aber umgesetzt.
Jetzt je Schritt der wirkliche Stand - inklusive der beiden Halbfertigen:
Update-PRUEFUNG laeuft, das Anwenden hat keinen Aufrufer; die
Release-Pipeline steht, ist aber nie gelaufen. P5 ist eingetreten.
- Accounting und Supervisor trugen keinen Umsetzungsvermerk, obwohl beide
Module gebaut sind. Vermerk nach dem Muster des Linux-Konzepts ergaenzt,
mit dem, was jeweils offen bleibt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
3.1 KiB
C#
82 lines
3.1 KiB
C#
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;
|
|
|
|
/// <summary>EF-Repo des Moduls gegen EF-InMemory (deterministisch, kein externer DB-Zugriff).</summary>
|
|
[Trait("cat", "unit")]
|
|
public class CongressRepositoryTests
|
|
{
|
|
private sealed class Factory<T>(DbContextOptions<T> options) : IDbContextFactory<T> where T : DbContext
|
|
{
|
|
public T CreateDbContext() => (T)Activator.CreateInstance(typeof(T), options)!;
|
|
}
|
|
|
|
private static CongressRepository CreateSut()
|
|
{
|
|
var ctOpts = new DbContextOptionsBuilder<CongressTradingDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;
|
|
var coreOpts = new DbContextOptionsBuilder<CoreDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;
|
|
|
|
var settings = new CoreSettingsService(new Factory<CoreDbContext>(coreOpts));
|
|
return new CongressRepository(new Factory<CongressTradingDbContext>(ctOpts), settings);
|
|
}
|
|
|
|
[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();
|
|
}
|
|
}
|