File size: 2,580 Bytes
f86fb3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <gtest/gtest.h>
#include "../include/market_data/gap_recovery.hpp"

using namespace quant::market_data;
using namespace quant::protocol;
using namespace quant::core;

TEST(GapRecoveryTest, SequentialStreamNoGaps) {
    std::vector<uint64_t> received_seqs;
    std::vector<SequenceGap> detected_gaps;

    GapRecoveryTracker tracker(
        [&](const SequenceGap& gap) { detected_gaps.push_back(gap); },
        [&](const MarketDataTickPayload&, uint64_t seq, uint64_t) { received_seqs.push_back(seq); }
    );

    MarketDataTickPayload tick;
    tracker.on_packet_received(1, 1000, tick);
    tracker.on_packet_received(2, 2000, tick);
    tracker.on_packet_received(3, 3000, tick);

    EXPECT_EQ(detected_gaps.size(), 0);
    ASSERT_EQ(received_seqs.size(), 3);
    EXPECT_EQ(received_seqs[0], 1);
    EXPECT_EQ(received_seqs[1], 2);
    EXPECT_EQ(received_seqs[2], 3);
    EXPECT_EQ(tracker.expected_seq(), 4);
}

TEST(GapRecoveryTest, GapDetectionAndBufferedDrain) {
    std::vector<uint64_t> received_seqs;
    std::vector<SequenceGap> detected_gaps;

    GapRecoveryTracker tracker(
        [&](const SequenceGap& gap) { detected_gaps.push_back(gap); },
        [&](const MarketDataTickPayload&, uint64_t seq, uint64_t) { received_seqs.push_back(seq); }
    );

    MarketDataTickPayload tick;
    tracker.on_packet_received(1, 1000, tick);
    // Skip seq 2, packet 3 arrives!
    tracker.on_packet_received(3, 3000, tick);

    ASSERT_EQ(detected_gaps.size(), 1);
    EXPECT_EQ(detected_gaps[0].from_seq, 2);
    EXPECT_EQ(detected_gaps[0].to_seq, 2);
    // Only packet 1 processed so far; packet 3 is buffered waiting for 2
    EXPECT_EQ(received_seqs.size(), 1);
    EXPECT_EQ(tracker.expected_seq(), 2);

    // Now packet 2 arrives via recovery
    tracker.on_gap_packet_recovered(2, 2000, tick);

    // Both 2 and 3 should now be drained in strict sequence!
    ASSERT_EQ(received_seqs.size(), 3);
    EXPECT_EQ(received_seqs[0], 1);
    EXPECT_EQ(received_seqs[1], 2);
    EXPECT_EQ(received_seqs[2], 3);
    EXPECT_EQ(tracker.expected_seq(), 4);
}

TEST(GapRecoveryTest, HistoricalCacheLookup) {
    GapRecoveryTracker tracker;
    MarketDataTickPayload tick;
    tick.bid_price_raw = 1500000;
    tick.bid_qty = 300;

    tracker.store_historical_packet(101, tick);

    auto retrieved = tracker.get_historical_packet(101);
    ASSERT_TRUE(retrieved.has_value());
    EXPECT_EQ(retrieved->bid_price_raw, 1500000);
    EXPECT_EQ(retrieved->bid_qty, 300);

    auto missing = tracker.get_historical_packet(999);
    EXPECT_FALSE(missing.has_value());
}