upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-12-10 16:31:47 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-12-10 16:31:47 +0000
commit5f3c8b7085f6652fdde4443983df0aad561e3c67 (patch)
tree7ce2bfda0ab5bd3871112f0e9bb396928a7652e6 /tests
parentbb9abe353024878e6bf1884cf3fb5a115e75bd03 (diff)
Phase 8: Create catchup sync stub test
Diffstat (limited to 'tests')
-rw-r--r--tests/sync.rs7
-rw-r--r--tests/sync/catchup.rs234
-rw-r--r--tests/sync/mod.rs19
3 files changed, 249 insertions, 11 deletions
diff --git a/tests/sync.rs b/tests/sync.rs
index 7756c17..2836d8d 100644
--- a/tests/sync.rs
+++ b/tests/sync.rs
@@ -5,6 +5,9 @@
5//! 5//!
6//! - `bootstrap` - Tests for sync from pre-configured bootstrap relay 6//! - `bootstrap` - Tests for sync from pre-configured bootstrap relay
7//! - `discovery` - Tests for relay discovery from announcement events 7//! - `discovery` - Tests for relay discovery from announcement events
8//! - `live_sync` - Tests for real-time sync after connection established
9//! - `tag_variations` - Tests for different Layer 2/3 tag types
10//! - `catchup` - Tests for catchup sync after disconnect (not yet implemented)
8//! 11//!
9//! # Running Tests 12//! # Running Tests
10//! 13//!
@@ -17,6 +20,9 @@
17//! 20//!
18//! # Run specific test 21//! # Run specific test
19//! cargo test --test sync test_bootstrap_syncs -- --nocapture 22//! cargo test --test sync test_bootstrap_syncs -- --nocapture
23//!
24//! # Run ignored tests (like catchup)
25//! cargo test --test sync -- --ignored
20//! ``` 26//! ```
21 27
22// Include the common test utilities 28// Include the common test utilities
@@ -25,6 +31,7 @@ mod common;
25// Include sync test submodules (located in tests/sync/) 31// Include sync test submodules (located in tests/sync/)
26mod sync { 32mod sync {
27 pub mod bootstrap; 33 pub mod bootstrap;
34 pub mod catchup;
28 pub mod discovery; 35 pub mod discovery;
29 pub mod live_sync; 36 pub mod live_sync;
30 pub mod tag_variations; 37 pub mod tag_variations;
diff --git a/tests/sync/catchup.rs b/tests/sync/catchup.rs
new file mode 100644
index 0000000..2d0af16
--- /dev/null
+++ b/tests/sync/catchup.rs
@@ -0,0 +1,234 @@
1//! Catchup Sync Tests
2//!
3//! Tests for the catchup synchronization feature (Test 0).
4//!
5//! # Catchup Sync Overview
6//!
7//! Catchup sync refers to the ability of a relay to synchronize historical events
8//! that were published while it was offline or unreachable. This is critical for
9//! ensuring data consistency across the relay network.
10//!
11//! ## Expected Behavior
12//!
13//! When a relay comes back online after being offline:
14//! 1. Detect gap in event history by comparing timestamps
15//! 2. Query connected relays for events in the gap period
16//! 3. Backfill Layer 2 events (kind 1618) from bootstrap relays
17//! 4. Discover and sync Layer 3 events (kinds 1, 1111) referencing Layer 2 events
18//! 5. Maintain chronological ordering during backfill
19//!
20//! ## Implementation Status
21//!
22//! ⚠ **NOT YET IMPLEMENTED** - Tests marked with `#[ignore]`
23//!
24//! These tests are ready to enable once catchup sync is implemented in the relay.
25//!
26//! ## See Also
27//!
28//! - Bootstrap sync: [`tests/sync/bootstrap.rs`](bootstrap.rs)
29//! - Live sync: [`tests/sync/live_sync.rs`](live_sync.rs)
30//! - Discovery sync: [`tests/sync/discovery.rs`](discovery.rs)
31
32use std::time::Duration;
33
34use nostr_sdk::prelude::*;
35
36use crate::common::{sync_helpers::*, TestRelay};
37
38/// Create a valid repository announcement event for testing sync.
39///
40/// This creates a kind 30617 event with required clone and relays tags.
41/// The event lists all provided domains so it will be accepted by each
42/// relay's write policy.
43///
44/// # Arguments
45/// * `keys` - Keys for signing
46/// * `domains` - Slice of domain strings (e.g., "127.0.0.1:8080")
47/// * `identifier` - Repository identifier (d-tag)
48fn create_repo_announcement(keys: &Keys, domains: &[&str], identifier: &str) -> Event {
49 // Build clone URLs for all domains (with .git suffix)
50 let clone_urls: Vec<String> = domains
51 .iter()
52 .map(|d| format!("http://{}/{}.git", d, identifier))
53 .collect();
54
55 // Build relay URLs for all domains
56 let relay_urls: Vec<String> = domains.iter().map(|d| format!("ws://{}", d)).collect();
57
58 // Build tags for repository announcement
59 let tags = vec![
60 Tag::identifier(identifier),
61 Tag::custom(TagKind::custom("clone"), clone_urls),
62 Tag::custom(TagKind::custom("relays"), relay_urls),
63 ];
64
65 EventBuilder::new(Kind::Custom(KIND_REPOSITORY_STATE), "Repository state")
66 .tags(tags)
67 .sign_with_keys(keys)
68 .expect("Failed to sign repo announcement")
69}
70
71/// Test that relay performs catchup sync after being offline
72///
73/// # Scenario
74///
75/// 1. Start two relays (relay1, relay2) with discovery configured
76/// 2. Publish several Layer 2 events to relay2
77/// 3. Stop relay1 (simulating offline state)
78/// 4. Publish more Layer 2 events to relay2 while relay1 is offline
79/// 5. Restart relay1
80/// 6. Verify relay1 catches up and syncs events it missed
81///
82/// # Expected Result
83///
84/// All events published while relay1 was offline should be synced
85/// to relay1 after it comes back online, maintaining chronological order.
86///
87/// # TODO
88///
89/// - Implement catchup sync mechanism in relay
90/// - Add timestamp-based gap detection
91/// - Add backfill query generation
92/// - Enable this test by removing `#[ignore]`
93#[tokio::test]
94#[ignore = "Catchup sync not yet implemented"]
95async fn test_catchup_sync_after_relay_restart() {
96 // NOTE: This is a skeleton implementation ready for when catchup sync is added
97
98 // 1. Start two relays
99 let relay1 = TestRelay::start().await;
100 let relay2 = TestRelay::start().await;
101
102 // 2. Set up discovery between relays via shared announcement
103 let keys = Keys::generate();
104 let identifier = "catchup-test-repo";
105
106 // Create announcement listing both relays
107 let domain1 = relay1.domain();
108 let domain2 = relay2.domain();
109 let announcement = create_repo_announcement(
110 &keys,
111 &[&domain1, &domain2],
112 identifier,
113 );
114
115 // Publish announcement to both relays
116 let client1 = TestClient::new(relay1.url(), keys.clone())
117 .await
118 .expect("Failed to connect to relay1");
119 let client2 = TestClient::new(relay2.url(), keys.clone())
120 .await
121 .expect("Failed to connect to relay2");
122
123 client1
124 .send_event(&announcement)
125 .await
126 .expect("Failed to send announcement to relay1");
127 client2
128 .send_event(&announcement)
129 .await
130 .expect("Failed to send announcement to relay2");
131
132 // Wait for discovery connections to establish
133 tokio::time::sleep(Duration::from_secs(2)).await;
134
135 // 3. Publish initial Layer 2 event (while both relays are online)
136 let repo_coord_str = repo_coord(&keys, identifier);
137 let event1 = build_layer2_issue_event(&keys, &repo_coord_str, "Issue 1 - before offline")
138 .expect("Failed to build event1");
139 let event1_id = client2
140 .send_event(&event1)
141 .await
142 .expect("Failed to send event1");
143
144 // Verify initial sync works (baseline check)
145 let synced = wait_for_event_on_relay(
146 relay1.url(),
147 Filter::new().id(event1_id),
148 Duration::from_secs(5),
149 )
150 .await;
151 assert!(synced, "Initial event should sync normally via live sync");
152
153 // 4. Stop relay1 (simulating offline state)
154 // Note: In a real implementation, we'd need a way to stop and restart a relay
155 // For now, this skeleton demonstrates the intended test flow
156 relay1.stop().await;
157
158 // Small delay to ensure relay1 is fully stopped
159 tokio::time::sleep(Duration::from_millis(500)).await;
160
161 // 5. Publish events while relay1 is offline
162 let event2 = build_layer2_issue_event(&keys, &repo_coord_str, "Issue 2 - during offline")
163 .expect("Failed to build event2");
164 let event2_id = client2
165 .send_event(&event2)
166 .await
167 .expect("Failed to send event2");
168
169 let event3 = build_layer2_issue_event(&keys, &repo_coord_str, "Issue 3 - during offline")
170 .expect("Failed to build event3");
171 let event3_id = client2
172 .send_event(&event3)
173 .await
174 .expect("Failed to send event3");
175
176 // Give time for events to be stored in relay2
177 tokio::time::sleep(Duration::from_secs(1)).await;
178
179 // 6. Restart relay1
180 // Note: TestRelay doesn't currently support restart, so we start a new instance
181 // A real implementation would need persistent storage and relay restart capability
182 let relay1_restarted = TestRelay::start().await;
183
184 // Reconnect client to the new relay instance
185 let client1_restarted = TestClient::new(relay1_restarted.url(), keys.clone())
186 .await
187 .expect("Failed to connect to restarted relay1");
188
189 // Re-publish announcement to establish discovery
190 let domain1_restarted = relay1_restarted.domain();
191 let announcement_restarted = create_repo_announcement(
192 &keys,
193 &[&domain1_restarted, &domain2],
194 identifier,
195 );
196 client1_restarted
197 .send_event(&announcement_restarted)
198 .await
199 .expect("Failed to send announcement to restarted relay1");
200
201 // 7. Wait for catchup sync to complete
202 // This is where the catchup sync mechanism would kick in
203 tokio::time::sleep(Duration::from_secs(5)).await;
204
205 // 8. Verify missed events were synced via catchup
206 let event2_synced = wait_for_event_on_relay(
207 relay1_restarted.url(),
208 Filter::new().id(event2_id),
209 Duration::from_secs(5),
210 )
211 .await;
212
213 let event3_synced = wait_for_event_on_relay(
214 relay1_restarted.url(),
215 Filter::new().id(event3_id),
216 Duration::from_secs(5),
217 )
218 .await;
219
220 assert!(
221 event2_synced,
222 "Event 2 (missed while offline) should be synced via catchup"
223 );
224 assert!(
225 event3_synced,
226 "Event 3 (missed while offline) should be synced via catchup"
227 );
228
229 // 9. Cleanup
230 client1_restarted.disconnect().await;
231 client2.disconnect().await;
232 relay1_restarted.stop().await;
233 relay2.stop().await;
234} \ No newline at end of file
diff --git a/tests/sync/mod.rs b/tests/sync/mod.rs
index a3d7bb5..b0da8b8 100644
--- a/tests/sync/mod.rs
+++ b/tests/sync/mod.rs
@@ -9,13 +9,13 @@
9//! - Tag variations (testing different Layer 2/3 tag types: a/A/q, e/E/q) 9//! - Tag variations (testing different Layer 2/3 tag types: a/A/q, e/E/q)
10//! - Catchup sync (events from disconnected period sync on reconnect) 10//! - Catchup sync (events from disconnected period sync on reconnect)
11//! 11//!
12//! # Test Files (to be added in subsequent phases) 12//! # Test Files
13//! 13//!
14//! - `bootstrap.rs` - Tests 1, 4: sync from bootstrap relay 14//! - `bootstrap.rs` - Tests 1, 4: sync from bootstrap relay
15//! - `discovery.rs` - Tests 2, 3: relay discovery from announcements 15//! - `discovery.rs` - Tests 2, 3: relay discovery from announcements
16//! - `live_sync.rs` - Tests 5, 6, 7: real-time sync after connection 16//! - `live_sync.rs` - Tests 5, 6, 7: real-time sync after connection
17//! - `tag_variations.rs` - Tests 8, 9: Layer 2/3 tag type coverage 17//! - `tag_variations.rs` - Tests 8, 9: Layer 2/3 tag type coverage
18//! - `catchup.rs` - Test 0: catchup after disconnect (stub) 18//! - `catchup.rs` - Test 0: catchup after disconnect (stub, `#[ignore]`)
19//! 19//!
20//! # Shared Imports 20//! # Shared Imports
21//! 21//!
@@ -23,13 +23,10 @@
23//! - `TestClient` - Client with retry logic 23//! - `TestClient` - Client with retry logic
24//! - Event builders for Layer 2/3 events 24//! - Event builders for Layer 2/3 events
25//! - `wait_for_event_on_relay()` - Non-panicking assertion helper 25//! - `wait_for_event_on_relay()` - Non-panicking assertion helper
26//!
27//! See `work/proactive-sync-test-implementation-plan.md` for full design.
28
29// Re-export sync helpers for convenient access in test files
30// Tests in this module can use:
31// use super::*;
32// to get access to these helpers.
33 26
34// Note: The actual test file modules will be added in Phase 5+ 27// Test modules
35// For now, this module serves as the organizational root. \ No newline at end of file 28pub mod bootstrap;
29pub mod catchup;
30pub mod discovery;
31pub mod live_sync;
32pub mod tag_variations; \ No newline at end of file