using System.Collections.Generic; using System.Linq; using PolyTrader.Modules.CopyTrading.Logic; using PolyTraderSharp.Models; using Xunit; namespace PolyTrader.Tests { /// /// Sichert den Aufbau des Alchemy-Blockchain-Abos ab: falsche Filter/Padding bedeuten /// verpasste (oder falsch zugeordnete) Master-Trades. /// public class BlockchainSubscriptionTests { private static List Traders(int n) => Enumerable.Range(1, n).Select(i => new TrackedTrader { Id = i, WalletAddress = $"0x{i:x40}" }).ToList(); // ---------------- PadAddress ---------------- [Fact] public void PadAddress_strips_prefix_lowercases_and_pads_to_32_bytes() { var r = BlockchainSubscription.PadAddress("0x628914CF1e96A9D1Ab8F0489A9f64be5633bac41"); Assert.Equal("0x000000000000000000000000628914cf1e96a9d1ab8f0489a9f64be5633bac41", r); Assert.Equal(66, r.Length); // "0x" + 64 Hex-Zeichen } [Fact] public void PadAddress_pads_short_input_with_leading_zeros() { Assert.Equal("0x" + new string('0', 61) + "abc", BlockchainSubscription.PadAddress("0xABC")); } // ---------------- Build: Chunk-Anzahl ---------------- [Theory] [InlineData(0, 0)] [InlineData(1, 2)] // 1 Chunk -> Buy + Sell [InlineData(3, 2)] // 1 Chunk (voll) [InlineData(4, 4)] // 2 Chunks (3 + 1) [InlineData(6, 4)] // 2 Chunks (3 + 3) [InlineData(7, 6)] // 3 Chunks (3 + 3 + 1) public void Build_creates_two_filters_per_chunk(int traderCount, int expectedFilters) { Assert.Equal(expectedFilters, BlockchainSubscription.Build(Traders(traderCount)).Filters.Count); } // ---------------- Build: Filter-Inhalt ---------------- [Fact] public void Build_pairs_buy_topic3_and_sell_topic2() { var sub = BlockchainSubscription.Build(Traders(1)); var buy = sub.Filters[0]; var sell = sub.Filters[1]; Assert.NotNull(buy.Topic3); Assert.Null(buy.Topic2); Assert.NotNull(sell.Topic2); Assert.Null(sell.Topic3); } [Fact] public void Build_sets_ctf_address_and_transfer_topics() { var buy = BlockchainSubscription.Build(Traders(1)).Filters[0]; Assert.Equal(BlockchainSubscription.CtfContractAddress, buy.Address); Assert.Equal( new[] { BlockchainSubscription.TransferSingleTopic, BlockchainSubscription.TransferBatchTopic }, buy.Topic0.ToArray()); } [Fact] public void Build_chunks_addresses_by_three() { var sub = BlockchainSubscription.Build(Traders(4)); // 3 + 1 Assert.Equal(3, sub.Filters[0].Topic3!.Count); // Chunk 1 (Buy) Assert.Equal(3, sub.Filters[1].Topic2!.Count); // Chunk 1 (Sell) Assert.Single(sub.Filters[2].Topic3!); // Chunk 2 (Buy) Assert.Single(sub.Filters[3].Topic2!); // Chunk 2 (Sell) } [Fact] public void Build_uses_padded_addresses() { var addr = BlockchainSubscription.Build(Traders(1)).Filters[0].Topic3![0]; Assert.Equal(66, addr.Length); Assert.StartsWith("0x", addr); } [Fact] public void Build_empty_returns_no_filters() { Assert.Empty(BlockchainSubscription.Build(Traders(0)).Filters); } } }