upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/tests/sync/catchup.rs
blob: 1ddafd1bcebcbab668c551f817faa3ecb9e35d19 (plain)
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! Catchup Sync Tests
//!
//! Tests for the catchup synchronization feature (Test 0).
//!
//! # Catchup Sync Overview
//!
//! Catchup sync refers to the ability of a relay to synchronize historical events
//! that were published while it was offline or unreachable. This is critical for
//! ensuring data consistency across the relay network.
//!
//! ## Expected Behavior
//!
//! When a relay comes back online after being offline:
//! 1. Detect gap in event history by comparing timestamps
//! 2. Query connected relays for events in the gap period
//! 3. Backfill Layer 2 events (kind 1618) from bootstrap relays
//! 4. Discover and sync Layer 3 events (kinds 1, 1111) referencing Layer 2 events
//! 5. Maintain chronological ordering during backfill
//!
//! ## Implementation Status
//!
//! ⚠ **NOT YET IMPLEMENTED** - Tests marked with `#[ignore]`
//!
//! These tests are ready to enable once catchup sync is implemented in the relay.
//!
//! ## See Also
//!
//! - Bootstrap sync: [`tests/sync/bootstrap.rs`](bootstrap.rs)
//! - Live sync: [`tests/sync/live_sync.rs`](live_sync.rs)
//! - Discovery sync: [`tests/sync/discovery.rs`](discovery.rs)

use std::time::Duration;

use nostr_sdk::prelude::*;

use crate::common::{sync_helpers::*, TestRelay};

/// Test that relay performs catchup sync after being offline
///
/// # Scenario
///
/// 1. Start two relays (relay1, relay2) with discovery configured
/// 2. Publish several Layer 2 events to relay2
/// 3. Stop relay1 (simulating offline state)
/// 4. Publish more Layer 2 events to relay2 while relay1 is offline
/// 5. Restart relay1
/// 6. Verify relay1 catches up and syncs events it missed
///
/// # Expected Result
///
/// All events published while relay1 was offline should be synced
/// to relay1 after it comes back online, maintaining chronological order.
///
/// # TODO
///
/// - Implement catchup sync mechanism in relay
/// - Add timestamp-based gap detection
/// - Add backfill query generation
/// - Enable this test by removing `#[ignore]`
#[tokio::test]
#[ignore = "Catchup sync not yet implemented"]
async fn test_catchup_sync_after_relay_restart() {
    // NOTE: This is a skeleton implementation ready for when catchup sync is added

    // 1. Start two relays
    let relay1 = TestRelay::start().await;
    let relay2 = TestRelay::start().await;

    // 2. Set up discovery between relays via shared announcement
    let keys = Keys::generate();
    let identifier = "catchup-test-repo";

    // Create announcement listing both relays
    let domain1 = relay1.domain();
    let domain2 = relay2.domain();
    let announcement = create_repo_announcement(
        &keys,
        &[&domain1, &domain2],
        identifier,
    );

    // Publish announcement to both relays
    let client1 = TestClient::new(relay1.url(), keys.clone())
        .await
        .expect("Failed to connect to relay1");
    let client2 = TestClient::new(relay2.url(), keys.clone())
        .await
        .expect("Failed to connect to relay2");

    client1
        .send_event(&announcement)
        .await
        .expect("Failed to send announcement to relay1");
    client2
        .send_event(&announcement)
        .await
        .expect("Failed to send announcement to relay2");

    // Wait for discovery connections to establish
    tokio::time::sleep(Duration::from_secs(2)).await;

    // 3. Publish initial Layer 2 event (while both relays are online)
    let repo_coord_str = repo_coord(&keys, identifier);
    let event1 = build_layer2_issue_event(&keys, &repo_coord_str, "Issue 1 - before offline")
        .expect("Failed to build event1");
    let event1_id = client2
        .send_event(&event1)
        .await
        .expect("Failed to send event1");

    // Verify initial sync works (baseline check)
    let synced = wait_for_event_on_relay(
        relay1.url(),
        Filter::new().id(event1_id),
        Duration::from_secs(5),
    )
    .await;
    assert!(synced, "Initial event should sync normally via live sync");

    // 4. Stop relay1 (simulating offline state)
    // Note: In a real implementation, we'd need a way to stop and restart a relay
    // For now, this skeleton demonstrates the intended test flow
    relay1.stop().await;

    // Small delay to ensure relay1 is fully stopped
    tokio::time::sleep(Duration::from_millis(500)).await;

    // 5. Publish events while relay1 is offline
    let event2 = build_layer2_issue_event(&keys, &repo_coord_str, "Issue 2 - during offline")
        .expect("Failed to build event2");
    let event2_id = client2
        .send_event(&event2)
        .await
        .expect("Failed to send event2");

    let event3 = build_layer2_issue_event(&keys, &repo_coord_str, "Issue 3 - during offline")
        .expect("Failed to build event3");
    let event3_id = client2
        .send_event(&event3)
        .await
        .expect("Failed to send event3");

    // Give time for events to be stored in relay2
    tokio::time::sleep(Duration::from_secs(1)).await;

    // 6. Restart relay1
    // Note: TestRelay doesn't currently support restart, so we start a new instance
    // A real implementation would need persistent storage and relay restart capability
    let relay1_restarted = TestRelay::start().await;

    // Reconnect client to the new relay instance
    let client1_restarted = TestClient::new(relay1_restarted.url(), keys.clone())
        .await
        .expect("Failed to connect to restarted relay1");

    // Re-publish announcement to establish discovery
    let domain1_restarted = relay1_restarted.domain();
    let announcement_restarted = create_repo_announcement(
        &keys,
        &[&domain1_restarted, &domain2],
        identifier,
    );
    client1_restarted
        .send_event(&announcement_restarted)
        .await
        .expect("Failed to send announcement to restarted relay1");

    // 7. Wait for catchup sync to complete
    // This is where the catchup sync mechanism would kick in
    tokio::time::sleep(Duration::from_secs(5)).await;

    // 8. Verify missed events were synced via catchup
    let event2_synced = wait_for_event_on_relay(
        relay1_restarted.url(),
        Filter::new().id(event2_id),
        Duration::from_secs(5),
    )
    .await;

    let event3_synced = wait_for_event_on_relay(
        relay1_restarted.url(),
        Filter::new().id(event3_id),
        Duration::from_secs(5),
    )
    .await;

    assert!(
        event2_synced,
        "Event 2 (missed while offline) should be synced via catchup"
    );
    assert!(
        event3_synced,
        "Event 3 (missed while offline) should be synced via catchup"
    );

    // 9. Cleanup
    client1_restarted.disconnect().await;
    client2.disconnect().await;
    relay1_restarted.stop().await;
    relay2.stop().await;
}