using PolyTrader.Modules.CopyTrading.Logic; using Xunit; using static PolyTrader.Modules.CopyTrading.Logic.FeeModel; namespace PolyTrader.Tests { /// /// Sicherheitsnetz für die Fee-Logik (Phase 0.2): Kategorie-Fallback + Fee-Berechnung. /// public class FeeModelTests { [Theory] [InlineData("Sports", 75)] [InlineData("SPORTS", 75)] // case-insensitive [InlineData("Crypto", 180)] [InlineData("Geopolitics", 0)] [InlineData("Politics", 100)] [InlineData("Finance", 100)] [InlineData("Economics", 100)] [InlineData("Weird-Unknown", 100)] // konservativer Default [InlineData("", 100)] [InlineData(null, 100)] public void FallbackBps_maps_category(string? category, int expected) { Assert.Equal(expected, FallbackBps(category)); } [Fact] public void FeeUsd_is_notional_times_bps() { Assert.Equal(1.0m, FeeUsd(100m, 100)); // 1,0 % von 100 Assert.Equal(0.9m, FeeUsd(50m, 180)); // 1,8 % von 50 } [Theory] [InlineData(100, 0)] // kein Satz [InlineData(0, 100)] // kein Notional [InlineData(-5, 100)] // negatives Notional public void FeeUsd_zero_cases(double notional, int bps) { Assert.Equal(0m, FeeUsd((decimal)notional, bps)); } // ----- ResolveBps (M6) ----- [Theory] [InlineData(120, "Sports", 120)] // realer Satz gewinnt [InlineData(0, "Sports", 75)] // kein realer Satz -> Kategorie-Fallback [InlineData(0, "Crypto", 180)] [InlineData(0, null, 100)] // unbekannt -> Default public void ResolveBps_prefers_actual_then_fallback(int takerFeeBps, string? category, int expected) { Assert.Equal(expected, ResolveBps(takerFeeBps, category)); } } }