upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests/proactive_sync_catchup.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-12-08 20:39:58 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-12-08 20:39:58 +0000
commit91dc5e8d718475a73815892452a58e1dbf56c8d9 (patch)
tree4db4a5afe7f9457a348f8cb9e3f4d8e3a6c7e7b0 /tests/proactive_sync_catchup.rs
parent103ede51485601892af1df6dab9f96f232b10f49 (diff)
delete old bad AI genreated tests
Diffstat (limited to 'tests/proactive_sync_catchup.rs')
-rw-r--r--tests/proactive_sync_catchup.rs330
1 files changed, 0 insertions, 330 deletions
diff --git a/tests/proactive_sync_catchup.rs b/tests/proactive_sync_catchup.rs
deleted file mode 100644
index d8a2ef9..0000000
--- a/tests/proactive_sync_catchup.rs
+++ /dev/null
@@ -1,330 +0,0 @@
1//! GRASP-02 Phase 5: Negentropy Catchup Integration Tests
2//!
3//! Tests verify negentropy catchup functionality:
4//! - Startup catchup after warm-up delay (30s default)
5//! - Reconnect catchup recovers recent gaps (last 3 days)
6//! - Daily catchup runs once per 24h with stagger
7//! - Catchup uses same filters as live sync
8//! - Gap events logged at WARN level
9//!
10//! # Running Tests
11//!
12//! ```bash
13//! cargo test --test proactive_sync_catchup
14//! cargo test --test proactive_sync_catchup -- --nocapture
15//! ```
16
17use ngit_grasp::sync::SubscriptionManager;
18
19// ============================================================================
20// Catchup State Machine Tests
21// ============================================================================
22
23/// Test startup catchup should only run once
24#[test]
25fn test_startup_catchup_runs_once() {
26 // After startup catchup completes, should_run_startup_catchup should return false
27 // This is handled by the startup_catchup_completed flag in NegentropyService
28
29 // Simulating the state machine:
30 let mut startup_completed = false;
31
32 // Before running, should return true (if delay elapsed)
33 let should_run_before = !startup_completed;
34 assert!(should_run_before);
35
36 // After running, mark as completed
37 startup_completed = true;
38
39 // Now should return false
40 let should_run_after = !startup_completed;
41 assert!(!should_run_after);
42}
43
44/// Test daily catchup interval checking
45#[test]
46fn test_daily_catchup_interval_check() {
47 use std::time::{Duration, Instant};
48
49 const DAILY_INTERVAL_SECS: u64 = 86400;
50
51 // Simulate last catchup time
52 let last_catchup = Instant::now();
53
54 // Immediately after, should not run
55 let should_run_immediately = last_catchup.elapsed() >= Duration::from_secs(DAILY_INTERVAL_SECS);
56 assert!(!should_run_immediately);
57}
58
59/// Test that new relay (no previous catchup) should run daily catchup
60#[test]
61fn test_new_relay_should_run_daily_catchup() {
62 use std::collections::HashMap;
63 use std::time::Instant;
64
65 let last_daily_catchup: HashMap<String, Instant> = HashMap::new();
66 let relay_url = "wss://test-relay.example.com";
67
68 // No previous catchup recorded, should return true
69 let should_run = !last_daily_catchup.contains_key(relay_url);
70 assert!(should_run);
71}
72
73/// Test reconnect catchup only after successful reconnection
74#[test]
75fn test_reconnect_catchup_after_reconnection() {
76 // Reconnect catchup should only trigger when:
77 // 1. Connection was previously successful (had_previous_connection = true)
78 // 2. Connection was lost and restored
79
80 let mut had_previous_connection = false;
81
82 // First connection - should NOT trigger reconnect catchup
83 let is_reconnection_first = had_previous_connection;
84 assert!(!is_reconnection_first);
85 had_previous_connection = true;
86
87 // Second connection (after disconnection) - SHOULD trigger
88 let is_reconnection_second = had_previous_connection;
89 assert!(is_reconnection_second);
90}
91
92// ============================================================================
93// Gap Event Flow Tests
94// ============================================================================
95
96/// Test that gap events go through policy validation
97#[test]
98fn test_gap_events_validated_through_policy() {
99 // The NegentropyService uses write_policy.admit_event() for validation
100 // This test verifies the flow exists:
101 // 1. Fetch events from relay
102 // 2. Check if event exists locally
103 // 3. Validate through Nip34WritePolicy
104 // 4. Store if accepted
105
106 // This is verified by the implementation in negentropy.rs:run_catchup()
107 // where PolicyResult::Accept leads to storage and PolicyResult::Reject is logged
108
109 assert!(true); // Flow verification - actual validation tested in other tests
110}
111
112/// Test that gap events are distinguished from live events
113#[test]
114fn test_gap_events_logged_at_warn_level() {
115 // The spec requires gap events to be logged at WARN level
116 // to distinguish them from live events (which are logged at INFO)
117
118 // This is implemented in negentropy.rs with:
119 // tracing::warn!("Gap event filled via {} catchup: {} (kind {})", ...)
120
121 // We verify the logging pattern exists by testing the catchup types
122 let catchup_types = ["startup", "reconnect", "daily"];
123 assert_eq!(catchup_types.len(), 3);
124
125 for catchup_type in catchup_types {
126 assert!(!catchup_type.is_empty());
127 }
128}
129
130// ============================================================================
131// Stagger Logic Tests
132// ============================================================================
133
134/// Test stagger delay calculation for multiple relays
135#[test]
136fn test_stagger_delay_for_multiple_relays() {
137 const STAGGER_SECS: u64 = 300; // 5 minutes
138
139 let _relay_urls = vec![
140 "wss://relay1.example.com",
141 "wss://relay2.example.com",
142 "wss://relay3.example.com",
143 ];
144
145 // First relay (index 0) should have no stagger
146 let stagger_0 = 0 * STAGGER_SECS;
147 assert_eq!(stagger_0, 0);
148
149 // Second relay (index 1) should have 5 minute stagger
150 let stagger_1 = 1 * STAGGER_SECS;
151 assert_eq!(stagger_1, 300);
152
153 // Third relay (index 2) should have 10 minute stagger
154 let stagger_2 = 2 * STAGGER_SECS;
155 assert_eq!(stagger_2, 600);
156}
157
158/// Test that startup catchup waits for warm-up
159#[test]
160fn test_startup_catchup_waits_for_warmup() {
161 use std::time::{Duration, Instant};
162
163 const STARTUP_DELAY_SECS: u64 = 30;
164
165 let startup_time = Instant::now();
166
167 // Immediately after startup, should not run (delay not elapsed)
168 let elapsed = startup_time.elapsed();
169 let should_run = elapsed >= Duration::from_secs(STARTUP_DELAY_SECS);
170
171 // This should be false since we just created startup_time
172 assert!(!should_run);
173}
174
175// ============================================================================
176// Lookback Period Tests
177// ============================================================================
178
179/// Test reconnect lookback calculation
180#[test]
181fn test_reconnect_lookback_calculation() {
182 // 3 days = 3 * 24 * 60 * 60 = 259,200 seconds
183 let lookback_days: u64 = 3;
184 let lookback_secs = lookback_days * 24 * 60 * 60;
185
186 assert_eq!(lookback_secs, 259200);
187}
188
189/// Test that daily catchup uses no lookback (full reconciliation)
190#[test]
191fn test_daily_catchup_full_reconciliation() {
192 // Daily catchup should reconcile all events, not just recent ones
193 // This is implemented by passing None to the since parameter
194 let since: Option<u64> = None;
195 assert!(since.is_none());
196}
197
198// ============================================================================
199// Three Catchup Scenario Tests
200// ============================================================================
201
202/// Test startup catchup scenario
203#[test]
204fn test_startup_catchup_scenario() {
205 // Startup catchup:
206 // 1. Wait 30s for warm-up
207 // 2. Run full reconciliation (no time limit)
208 // 3. Mark as completed (runs only once)
209 // 4. Stagger between relays (5 minutes)
210
211 const STARTUP_DELAY: u64 = 30;
212 const STAGGER: u64 = 300;
213
214 assert_eq!(STARTUP_DELAY, 30);
215 assert_eq!(STAGGER, 300);
216}
217
218/// Test reconnect catchup scenario
219#[test]
220fn test_reconnect_catchup_scenario() {
221 // Reconnect catchup:
222 // 1. Trigger after connection restore (not first connection)
223 // 2. Wait 10s reconnect delay
224 // 3. Only fetch last 3 days of events
225 // 4. Runs in background (doesn't block connection)
226
227 const RECONNECT_DELAY: u64 = 10;
228 const LOOKBACK_DAYS: u64 = 3;
229
230 assert_eq!(RECONNECT_DELAY, 10);
231 assert_eq!(LOOKBACK_DAYS, 3);
232}
233
234/// Test daily catchup scenario
235#[test]
236fn test_daily_catchup_scenario() {
237 // Daily catchup:
238 // 1. Check hourly if any relay needs catchup
239 // 2. Run if 24h elapsed since last catchup for that relay
240 // 3. Full reconciliation (no time limit)
241 // 4. Stagger between relays (5 minutes)
242
243 const CHECK_INTERVAL: u64 = 3600; // 1 hour
244 const DAILY_INTERVAL: u64 = 86400; // 24 hours
245 const STAGGER: u64 = 300; // 5 minutes
246
247 assert_eq!(CHECK_INTERVAL, 3600);
248 assert_eq!(DAILY_INTERVAL, 86400);
249 assert_eq!(STAGGER, 300);
250}
251
252// ============================================================================
253// Event Existence Check Tests
254// ============================================================================
255
256/// Test that existing events are skipped during catchup
257#[test]
258fn test_existing_events_skipped() {
259 // The catchup flow should:
260 // 1. Fetch events from relay
261 // 2. For each event, check if it exists locally
262 // 3. Skip if exists, validate and store if not
263
264 // This is implemented in negentropy.rs:event_exists_locally()
265 // which queries the database for the event by ID
266
267 const SKIP_EXISTING: bool = true;
268 assert!(SKIP_EXISTING);
269}
270
271/// Test duplicate prevention during catchup
272#[test]
273fn test_duplicate_prevention() {
274 use std::collections::HashSet;
275
276 let mut processed_ids: HashSet<String> = HashSet::new();
277 let event_id = "abc123def456".to_string();
278
279 // First time seeing this event - should process
280 let is_new = !processed_ids.contains(&event_id);
281 assert!(is_new);
282 processed_ids.insert(event_id.clone());
283
284 // Second time - should skip
285 let is_duplicate = processed_ids.contains(&event_id);
286 assert!(is_duplicate);
287}
288
289// ============================================================================
290// Configuration Integration Tests
291// ============================================================================
292
293/// Test config fields exist for catchup timing
294#[test]
295fn test_config_fields_for_catchup() {
296 // The Config struct should have these fields:
297 // - sync_startup_delay_secs (default: 30)
298 // - sync_reconnect_delay_secs (default: 10)
299 // - sync_reconnect_lookback_days (default: 3)
300
301 // Environment variables:
302 // - NGIT_SYNC_STARTUP_DELAY_SECS
303 // - NGIT_SYNC_RECONNECT_DELAY_SECS
304 // - NGIT_SYNC_RECONNECT_LOOKBACK_DAYS
305
306 let expected_defaults = vec![
307 ("startup_delay_secs", 30u64),
308 ("reconnect_delay_secs", 10u64),
309 ("reconnect_lookback_days", 3u64),
310 ];
311
312 assert_eq!(expected_defaults.len(), 3);
313 assert_eq!(expected_defaults[0].1, 30);
314 assert_eq!(expected_defaults[1].1, 10);
315 assert_eq!(expected_defaults[2].1, 3);
316}
317
318/// Test that catchup respects configured delays
319#[test]
320fn test_catchup_respects_config() {
321 // Custom delays should be used instead of defaults
322 let custom_startup_delay: u64 = 60;
323 let custom_reconnect_delay: u64 = 20;
324 let custom_lookback_days: u64 = 7;
325
326 // All should be configurable to non-default values
327 assert_ne!(custom_startup_delay, 30);
328 assert_ne!(custom_reconnect_delay, 10);
329 assert_ne!(custom_lookback_days, 3);
330}