diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-18 23:17:08 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-23 12:05:05 +0000 |
| commit | 49b9405dfcbb872686acdd7abc12dc9c94adc2ab (patch) | |
| tree | 2bd54765aad3853dddd68119c9143626ba3bfdaa /tests | |
| parent | 63865548b07e44d69321af3b03ca2c29aa60d74d (diff) | |
test: update sync tests to set up git data for purgatory flow
All sync tests now create a local git repo, send announcement + state
event to the source relay, and push git data to release both from
purgatory before the syncing relay starts bootstrap sync.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/common/sync_helpers.rs | 336 | ||||
| -rw-r--r-- | tests/sync/discovery.rs | 164 | ||||
| -rw-r--r-- | tests/sync/historic_sync.rs | 32 | ||||
| -rw-r--r-- | tests/sync/live_sync.rs | 119 | ||||
| -rw-r--r-- | tests/sync/maintainer_reprocessing.rs | 153 | ||||
| -rw-r--r-- | tests/sync/metrics.rs | 139 | ||||
| -rw-r--r-- | tests/sync/tag_variations.rs | 244 |
7 files changed, 680 insertions, 507 deletions
diff --git a/tests/common/sync_helpers.rs b/tests/common/sync_helpers.rs index daa684b..af51e78 100644 --- a/tests/common/sync_helpers.rs +++ b/tests/common/sync_helpers.rs | |||
| @@ -507,41 +507,53 @@ fn check_sync_connections_in_metrics(metrics: &str, expected: usize) -> bool { | |||
| 507 | /// assert!(found, "Expected event {} to sync to relay", event.id); | 507 | /// assert!(found, "Expected event {} to sync to relay", event.id); |
| 508 | /// ``` | 508 | /// ``` |
| 509 | pub async fn wait_for_event_on_relay(relay_url: &str, filter: Filter, timeout: Duration) -> bool { | 509 | pub async fn wait_for_event_on_relay(relay_url: &str, filter: Filter, timeout: Duration) -> bool { |
| 510 | // Create a temporary client for querying | 510 | let deadline = tokio::time::Instant::now() + timeout; |
| 511 | let temp_keys = Keys::generate(); | 511 | let poll_interval = Duration::from_millis(200); |
| 512 | let client = Client::new(temp_keys); | ||
| 513 | |||
| 514 | // Try to connect | ||
| 515 | if client.add_relay(relay_url).await.is_err() { | ||
| 516 | return false; | ||
| 517 | } | ||
| 518 | 512 | ||
| 519 | client.connect().await; | 513 | loop { |
| 514 | // Create a fresh client for each poll attempt (avoids stale connection state) | ||
| 515 | let temp_keys = Keys::generate(); | ||
| 516 | let client = Client::new(temp_keys); | ||
| 520 | 517 | ||
| 521 | // Wait for connection (brief timeout) | 518 | if client.add_relay(relay_url).await.is_err() { |
| 522 | let mut connected = false; | 519 | if tokio::time::Instant::now() >= deadline { |
| 523 | for _ in 0..10 { | 520 | return false; |
| 524 | tokio::time::sleep(Duration::from_millis(100)).await; | 521 | } |
| 525 | let relays = client.relays().await; | 522 | tokio::time::sleep(poll_interval).await; |
| 526 | if relays.values().any(|r| r.is_connected()) { | 523 | continue; |
| 527 | connected = true; | ||
| 528 | break; | ||
| 529 | } | 524 | } |
| 530 | } | ||
| 531 | 525 | ||
| 532 | if !connected { | 526 | client.connect().await; |
| 533 | client.disconnect().await; | ||
| 534 | return false; | ||
| 535 | } | ||
| 536 | 527 | ||
| 537 | // Fetch events with the provided timeout | 528 | // Wait for connection |
| 538 | let result = client.fetch_events(filter, timeout).await; | 529 | let mut connected = false; |
| 530 | for _ in 0..10 { | ||
| 531 | tokio::time::sleep(Duration::from_millis(100)).await; | ||
| 532 | let relays = client.relays().await; | ||
| 533 | if relays.values().any(|r| r.is_connected()) { | ||
| 534 | connected = true; | ||
| 535 | break; | ||
| 536 | } | ||
| 537 | } | ||
| 539 | 538 | ||
| 540 | client.disconnect().await; | 539 | if connected { |
| 540 | // Use a short fetch window — if the event is there, EOSE comes back quickly | ||
| 541 | let fetch_timeout = Duration::from_millis(500); | ||
| 542 | let result = client.fetch_events(filter.clone(), fetch_timeout).await; | ||
| 543 | client.disconnect().await; | ||
| 541 | 544 | ||
| 542 | match result { | 545 | match result { |
| 543 | Ok(events) => !events.is_empty(), | 546 | Ok(events) if !events.is_empty() => return true, |
| 544 | Err(_) => false, | 547 | _ => {} |
| 548 | } | ||
| 549 | } else { | ||
| 550 | client.disconnect().await; | ||
| 551 | } | ||
| 552 | |||
| 553 | if tokio::time::Instant::now() >= deadline { | ||
| 554 | return false; | ||
| 555 | } | ||
| 556 | tokio::time::sleep(poll_interval).await; | ||
| 545 | } | 557 | } |
| 546 | } | 558 | } |
| 547 | 559 | ||
| @@ -774,6 +786,11 @@ impl MetricsTestHarness { | |||
| 774 | self.source_relays[idx].domain() | 786 | self.source_relays[idx].domain() |
| 775 | } | 787 | } |
| 776 | 788 | ||
| 789 | /// Get a reference to a source relay (for advanced test operations) | ||
| 790 | pub fn source_relay(&self, idx: usize) -> &TestRelay { | ||
| 791 | &self.source_relays[idx] | ||
| 792 | } | ||
| 793 | |||
| 777 | /// Submit events to a specific source relay | 794 | /// Submit events to a specific source relay |
| 778 | pub async fn submit_events(&self, source_idx: usize, events: &[Event]) -> Result<(), String> { | 795 | pub async fn submit_events(&self, source_idx: usize, events: &[Event]) -> Result<(), String> { |
| 779 | let relay = &self.source_relays[source_idx]; | 796 | let relay = &self.source_relays[source_idx]; |
| @@ -1099,6 +1116,259 @@ pub async fn send_to_relay_url(relay_url: &str, event: &Event) -> Result<(), Str | |||
| 1099 | Ok(()) | 1116 | Ok(()) |
| 1100 | } | 1117 | } |
| 1101 | 1118 | ||
| 1119 | /// Push git repository data to a relay to release a purgatory-held announcement. | ||
| 1120 | /// | ||
| 1121 | /// Creates a local git repo, sends a state event, and pushes to the relay. | ||
| 1122 | /// Use this when you need to build a custom announcement but still need the | ||
| 1123 | /// relay to accept it (i.e., release it from purgatory). | ||
| 1124 | /// | ||
| 1125 | /// # Arguments | ||
| 1126 | /// * `relay` - The relay to push to | ||
| 1127 | /// * `keys` - Keys of the repository owner | ||
| 1128 | /// * `identifier` - Repository identifier | ||
| 1129 | /// * `domains` - All domains in the announcement (for state event URLs) | ||
| 1130 | /// | ||
| 1131 | /// # Returns | ||
| 1132 | /// `tempfile::TempDir` - Keep alive for test duration | ||
| 1133 | pub async fn push_git_data_to_relay( | ||
| 1134 | relay: &TestRelay, | ||
| 1135 | keys: &Keys, | ||
| 1136 | identifier: &str, | ||
| 1137 | domains: &[&str], | ||
| 1138 | ) -> tempfile::TempDir { | ||
| 1139 | use super::purgatory_helpers::{ | ||
| 1140 | create_state_event, create_test_repo_with_commit, push_to_relay, CommitVariant, | ||
| 1141 | }; | ||
| 1142 | |||
| 1143 | let npub = keys | ||
| 1144 | .public_key() | ||
| 1145 | .to_bech32() | ||
| 1146 | .expect("Failed to convert public key to npub"); | ||
| 1147 | |||
| 1148 | // Create local git repo | ||
| 1149 | let git_temp_dir = tempfile::tempdir().expect("Failed to create temp dir for git repo"); | ||
| 1150 | let commit_hash = create_test_repo_with_commit(git_temp_dir.path(), CommitVariant::StateTest) | ||
| 1151 | .expect("Failed to create test git repo"); | ||
| 1152 | |||
| 1153 | let clone_urls: Vec<String> = domains | ||
| 1154 | .iter() | ||
| 1155 | .map(|d| format!("http://{}/{}/{}.git", d, npub, identifier)) | ||
| 1156 | .collect(); | ||
| 1157 | let relay_urls: Vec<String> = domains.iter().map(|d| format!("ws://{}", d)).collect(); | ||
| 1158 | |||
| 1159 | // Build and send state event with all domains' clone URLs | ||
| 1160 | let state_event = create_state_event( | ||
| 1161 | keys, | ||
| 1162 | identifier, | ||
| 1163 | &[("main", &commit_hash)], | ||
| 1164 | &[], | ||
| 1165 | &clone_urls.iter().map(|s| s.as_str()).collect::<Vec<_>>(), | ||
| 1166 | &relay_urls.iter().map(|s| s.as_str()).collect::<Vec<_>>(), | ||
| 1167 | ) | ||
| 1168 | .expect("Failed to create state event"); | ||
| 1169 | |||
| 1170 | send_to_relay(relay, &state_event) | ||
| 1171 | .await | ||
| 1172 | .expect("Failed to send state event"); | ||
| 1173 | |||
| 1174 | // Git push to relay → releases state event from purgatory, authorizes push | ||
| 1175 | push_to_relay(git_temp_dir.path(), &relay.domain(), &npub, identifier) | ||
| 1176 | .expect("Failed to push git data to relay"); | ||
| 1177 | |||
| 1178 | // Brief wait for push processing | ||
| 1179 | tokio::time::sleep(Duration::from_millis(500)).await; | ||
| 1180 | |||
| 1181 | git_temp_dir | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | /// Like `push_git_data_to_relay` but writes a unique marker file so each call | ||
| 1185 | /// produces a distinct commit hash. | ||
| 1186 | /// | ||
| 1187 | /// Use this when multiple callers push to the same relay with the same identifier | ||
| 1188 | /// but different keys — identical commit hashes cause git to skip pack transfer, | ||
| 1189 | /// which can leave the announcement in purgatory. | ||
| 1190 | /// | ||
| 1191 | /// # Arguments | ||
| 1192 | /// * `relay` - The relay to push to | ||
| 1193 | /// * `keys` - Keys of the repository owner | ||
| 1194 | /// * `identifier` - Repository identifier | ||
| 1195 | /// * `domains` - All domains in the announcement (for state event URLs) | ||
| 1196 | /// * `unique_seed` - A string written into a `.unique` file to differentiate commits | ||
| 1197 | /// | ||
| 1198 | /// # Returns | ||
| 1199 | /// `tempfile::TempDir` - Keep alive for test duration | ||
| 1200 | pub async fn push_unique_git_data_to_relay( | ||
| 1201 | relay: &TestRelay, | ||
| 1202 | keys: &Keys, | ||
| 1203 | identifier: &str, | ||
| 1204 | domains: &[&str], | ||
| 1205 | unique_seed: &str, | ||
| 1206 | ) -> tempfile::TempDir { | ||
| 1207 | use super::purgatory_helpers::{create_state_event, push_to_relay}; | ||
| 1208 | |||
| 1209 | let npub = keys | ||
| 1210 | .public_key() | ||
| 1211 | .to_bech32() | ||
| 1212 | .expect("Failed to convert public key to npub"); | ||
| 1213 | |||
| 1214 | let git_temp_dir = tempfile::tempdir().expect("Failed to create temp dir for git repo"); | ||
| 1215 | let path = git_temp_dir.path(); | ||
| 1216 | |||
| 1217 | fn git(path: &std::path::Path, args: &[&str]) { | ||
| 1218 | let status = std::process::Command::new("git") | ||
| 1219 | .args(args) | ||
| 1220 | .current_dir(path) | ||
| 1221 | .env("GIT_AUTHOR_NAME", "Test User") | ||
| 1222 | .env("GIT_AUTHOR_EMAIL", "test@example.com") | ||
| 1223 | .env("GIT_COMMITTER_NAME", "Test User") | ||
| 1224 | .env("GIT_COMMITTER_EMAIL", "test@example.com") | ||
| 1225 | .env("GIT_AUTHOR_DATE", "2024-01-01T00:00:00+00:00") | ||
| 1226 | .env("GIT_COMMITTER_DATE", "2024-01-01T00:00:00+00:00") | ||
| 1227 | .output() | ||
| 1228 | .unwrap_or_else(|e| panic!("git {:?} failed to spawn: {}", args, e)); | ||
| 1229 | assert!( | ||
| 1230 | status.status.success(), | ||
| 1231 | "git {:?} failed: {}", | ||
| 1232 | args, | ||
| 1233 | String::from_utf8_lossy(&status.stderr) | ||
| 1234 | ); | ||
| 1235 | } | ||
| 1236 | |||
| 1237 | git(path, &["init", "--initial-branch=main"]); | ||
| 1238 | git(path, &["config", "user.email", "test@example.com"]); | ||
| 1239 | git(path, &["config", "user.name", "Test User"]); | ||
| 1240 | git(path, &["config", "commit.gpgsign", "false"]); | ||
| 1241 | |||
| 1242 | // Write a unique file so each maintainer gets a distinct commit hash | ||
| 1243 | std::fs::write(path.join("state_test.txt"), "State test content for purgatory sync") | ||
| 1244 | .expect("write state_test.txt"); | ||
| 1245 | std::fs::write(path.join(".unique"), unique_seed).expect("write .unique"); | ||
| 1246 | git(path, &["add", "."]); | ||
| 1247 | git(path, &["commit", "-m", "State test commit"]); | ||
| 1248 | |||
| 1249 | let commit_hash = { | ||
| 1250 | let out = std::process::Command::new("git") | ||
| 1251 | .args(["rev-parse", "HEAD"]) | ||
| 1252 | .current_dir(path) | ||
| 1253 | .output() | ||
| 1254 | .expect("git rev-parse"); | ||
| 1255 | String::from_utf8_lossy(&out.stdout).trim().to_string() | ||
| 1256 | }; | ||
| 1257 | |||
| 1258 | let clone_urls: Vec<String> = domains | ||
| 1259 | .iter() | ||
| 1260 | .map(|d| format!("http://{}/{}/{}.git", d, npub, identifier)) | ||
| 1261 | .collect(); | ||
| 1262 | let relay_urls: Vec<String> = domains.iter().map(|d| format!("ws://{}", d)).collect(); | ||
| 1263 | |||
| 1264 | let state_event = create_state_event( | ||
| 1265 | keys, | ||
| 1266 | identifier, | ||
| 1267 | &[("main", &commit_hash)], | ||
| 1268 | &[], | ||
| 1269 | &clone_urls.iter().map(|s| s.as_str()).collect::<Vec<_>>(), | ||
| 1270 | &relay_urls.iter().map(|s| s.as_str()).collect::<Vec<_>>(), | ||
| 1271 | ) | ||
| 1272 | .expect("Failed to create state event"); | ||
| 1273 | |||
| 1274 | send_to_relay(relay, &state_event) | ||
| 1275 | .await | ||
| 1276 | .expect("Failed to send state event"); | ||
| 1277 | |||
| 1278 | push_to_relay(path, &relay.domain(), &npub, identifier) | ||
| 1279 | .expect("Failed to push git data to relay"); | ||
| 1280 | |||
| 1281 | tokio::time::sleep(Duration::from_millis(500)).await; | ||
| 1282 | |||
| 1283 | git_temp_dir | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | /// Set up a repository announcement on a relay with git data so it passes purgatory. | ||
| 1287 | /// | ||
| 1288 | /// With the announcement purgatory feature, announcements (kind 30617) require git | ||
| 1289 | /// data before they are promoted to the relay's main DB. This helper: | ||
| 1290 | /// | ||
| 1291 | /// 1. Creates a local git repo with a commit | ||
| 1292 | /// 2. Builds an announcement and state event (kind 30618) pointing to the relay | ||
| 1293 | /// 3. Sends both to the relay (they go to purgatory) | ||
| 1294 | /// 4. Git pushes to the relay → releases both from purgatory immediately | ||
| 1295 | /// 5. Returns the announcement event and temp dir (keep alive for test duration) | ||
| 1296 | /// | ||
| 1297 | /// # Arguments | ||
| 1298 | /// * `relay` - The relay to set up the announcement on | ||
| 1299 | /// * `keys` - Keys to sign the announcement with (repo owner) | ||
| 1300 | /// * `domains` - All domains that should be listed in the announcement (including relay.domain()) | ||
| 1301 | /// * `identifier` - Repository identifier (d-tag) | ||
| 1302 | /// | ||
| 1303 | /// # Returns | ||
| 1304 | /// `(Event, tempfile::TempDir)` - The announcement event and temp dir. | ||
| 1305 | /// The temp dir MUST be kept alive for the duration of the test. | ||
| 1306 | pub async fn setup_announcement_on_relay( | ||
| 1307 | relay: &TestRelay, | ||
| 1308 | keys: &Keys, | ||
| 1309 | domains: &[&str], | ||
| 1310 | identifier: &str, | ||
| 1311 | ) -> (Event, tempfile::TempDir) { | ||
| 1312 | use super::purgatory_helpers::{ | ||
| 1313 | create_state_event, create_test_repo_with_commit, push_to_relay, CommitVariant, | ||
| 1314 | }; | ||
| 1315 | |||
| 1316 | let npub = keys | ||
| 1317 | .public_key() | ||
| 1318 | .to_bech32() | ||
| 1319 | .expect("Failed to convert public key to npub"); | ||
| 1320 | |||
| 1321 | // Create local git repo with a commit | ||
| 1322 | let git_temp_dir = tempfile::tempdir().expect("Failed to create temp dir for git repo"); | ||
| 1323 | let commit_hash = create_test_repo_with_commit(git_temp_dir.path(), CommitVariant::StateTest) | ||
| 1324 | .expect("Failed to create test git repo"); | ||
| 1325 | |||
| 1326 | // Build clone URLs and relay URLs from domains | ||
| 1327 | let clone_urls: Vec<String> = domains | ||
| 1328 | .iter() | ||
| 1329 | .map(|d| format!("http://{}/{}/{}.git", d, npub, identifier)) | ||
| 1330 | .collect(); | ||
| 1331 | let relay_urls: Vec<String> = domains.iter().map(|d| format!("ws://{}", d)).collect(); | ||
| 1332 | |||
| 1333 | // Build announcement event (lists ALL domains for relay discovery) | ||
| 1334 | let announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "Repository state") | ||
| 1335 | .tags(vec![ | ||
| 1336 | Tag::identifier(identifier), | ||
| 1337 | Tag::custom(TagKind::custom("clone"), clone_urls.clone()), | ||
| 1338 | Tag::custom(TagKind::custom("relays"), relay_urls.clone()), | ||
| 1339 | ]) | ||
| 1340 | .sign_with_keys(keys) | ||
| 1341 | .expect("Failed to sign repo announcement"); | ||
| 1342 | |||
| 1343 | // Build state event with all domains' clone URLs | ||
| 1344 | let state_event = create_state_event( | ||
| 1345 | keys, | ||
| 1346 | identifier, | ||
| 1347 | &[("main", &commit_hash)], | ||
| 1348 | &[], | ||
| 1349 | &clone_urls.iter().map(|s| s.as_str()).collect::<Vec<_>>(), | ||
| 1350 | &relay_urls.iter().map(|s| s.as_str()).collect::<Vec<_>>(), | ||
| 1351 | ) | ||
| 1352 | .expect("Failed to create state event"); | ||
| 1353 | |||
| 1354 | // Send announcement and state event to relay (both go to purgatory) | ||
| 1355 | send_to_relay(relay, &announcement) | ||
| 1356 | .await | ||
| 1357 | .expect("Failed to send announcement"); | ||
| 1358 | send_to_relay(relay, &state_event) | ||
| 1359 | .await | ||
| 1360 | .expect("Failed to send state event"); | ||
| 1361 | |||
| 1362 | // Git push to relay → releases both from purgatory | ||
| 1363 | push_to_relay(git_temp_dir.path(), &relay.domain(), &npub, identifier) | ||
| 1364 | .expect("Failed to push git data to relay"); | ||
| 1365 | |||
| 1366 | // Brief wait for push processing | ||
| 1367 | tokio::time::sleep(Duration::from_millis(500)).await; | ||
| 1368 | |||
| 1369 | (announcement, git_temp_dir) | ||
| 1370 | } | ||
| 1371 | |||
| 1102 | /// Unified sync test helper that automatically determines sync mode. | 1372 | /// Unified sync test helper that automatically determines sync mode. |
| 1103 | /// | 1373 | /// |
| 1104 | /// This function sets up a complete sync test environment by determining whether | 1374 | /// This function sets up a complete sync test environment by determining whether |
| @@ -1158,9 +1428,8 @@ pub async fn run_sync_test(historic_events: &[Event], live_events: &[Event]) -> | |||
| 1158 | 1428 | ||
| 1159 | // 3. Create local git repo with a commit | 1429 | // 3. Create local git repo with a commit |
| 1160 | let git_temp_dir = tempfile::tempdir().expect("Failed to create temp dir for git repo"); | 1430 | let git_temp_dir = tempfile::tempdir().expect("Failed to create temp dir for git repo"); |
| 1161 | let commit_hash = | 1431 | let commit_hash = create_test_repo_with_commit(git_temp_dir.path(), CommitVariant::StateTest) |
| 1162 | create_test_repo_with_commit(git_temp_dir.path(), CommitVariant::StateTest) | 1432 | .expect("Failed to create test git repo"); |
| 1163 | .expect("Failed to create test git repo"); | ||
| 1164 | 1433 | ||
| 1165 | // 4. Create keys and build URLs | 1434 | // 4. Create keys and build URLs |
| 1166 | let keys = Keys::generate(); | 1435 | let keys = Keys::generate(); |
| @@ -1172,10 +1441,7 @@ pub async fn run_sync_test(historic_events: &[Event], live_events: &[Event]) -> | |||
| 1172 | // Clone URLs: source relay HTTP endpoint is where git data lives | 1441 | // Clone URLs: source relay HTTP endpoint is where git data lives |
| 1173 | // The syncing relay's purgatory will fetch from source's clone URL | 1442 | // The syncing relay's purgatory will fetch from source's clone URL |
| 1174 | let clone_url_source = format!("http://{}/{}/{}.git", source.domain(), npub, "test-repo"); | 1443 | let clone_url_source = format!("http://{}/{}/{}.git", source.domain(), npub, "test-repo"); |
| 1175 | let clone_url_syncing = format!( | 1444 | let clone_url_syncing = format!("http://{}/{}/{}.git", syncing_domain, npub, "test-repo"); |
| 1176 | "http://{}/{}/{}.git", | ||
| 1177 | syncing_domain, npub, "test-repo" | ||
| 1178 | ); | ||
| 1179 | 1445 | ||
| 1180 | let clone_urls = vec![clone_url_source.clone(), clone_url_syncing.clone()]; | 1446 | let clone_urls = vec![clone_url_source.clone(), clone_url_syncing.clone()]; |
| 1181 | let relay_urls = vec![ | 1447 | let relay_urls = vec![ |
diff --git a/tests/sync/discovery.rs b/tests/sync/discovery.rs index 8ed80b5..5fcda69 100644 --- a/tests/sync/discovery.rs +++ b/tests/sync/discovery.rs | |||
| @@ -62,29 +62,26 @@ async fn test_discovers_layer3_via_layer2() { | |||
| 62 | // 3. Create test keys | 62 | // 3. Create test keys |
| 63 | let keys = Keys::generate(); | 63 | let keys = Keys::generate(); |
| 64 | 64 | ||
| 65 | // 4. Create a repository announcement that lists BOTH relays | 65 | // 4. Set up repository announcement on relay_a with git data |
| 66 | let announcement = create_repo_announcement( | 66 | // (purgatory requires git data before announcements are accepted) |
| 67 | &keys, | 67 | let repo_id = "test-repo-discovery"; |
| 68 | &[&relay_a.domain(), &relay_b.domain()], | 68 | let domains = vec![relay_a.domain(), relay_b.domain()]; |
| 69 | "test-repo-discovery", | 69 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); |
| 70 | ); | 70 | |
| 71 | let (announcement, _git_dir_a) = | ||
| 72 | setup_announcement_on_relay(&relay_a, &keys, &domain_refs, repo_id).await; | ||
| 71 | let announcement_id = announcement.id; | 73 | let announcement_id = announcement.id; |
| 72 | |||
| 73 | println!( | 74 | println!( |
| 74 | "Created announcement {} (kind {})", | 75 | "Announcement {} set up on relay_a with git data", |
| 75 | announcement_id, | 76 | announcement_id |
| 76 | announcement.kind.as_u16() | ||
| 77 | ); | 77 | ); |
| 78 | for tag in announcement.tags.iter() { | ||
| 79 | println!(" Tag: {:?}", tag.as_slice()); | ||
| 80 | } | ||
| 81 | 78 | ||
| 82 | // 5. Build the repo coordinate for the 'a' tag in the patch | 79 | // 5. Build the repo coordinate for the 'a' tag in the patch |
| 83 | let repo_coord = format!( | 80 | let repo_coord = format!( |
| 84 | "{}:{}:{}", | 81 | "{}:{}:{}", |
| 85 | Kind::GitRepoAnnouncement.as_u16(), | 82 | Kind::GitRepoAnnouncement.as_u16(), |
| 86 | keys.public_key().to_hex(), | 83 | keys.public_key().to_hex(), |
| 87 | "test-repo-discovery" | 84 | repo_id |
| 88 | ); | 85 | ); |
| 89 | 86 | ||
| 90 | // 6. Create a patch event (Layer 2) that references the announcement | 87 | // 6. Create a patch event (Layer 2) that references the announcement |
| @@ -97,22 +94,13 @@ async fn test_discovers_layer3_via_layer2() { | |||
| 97 | let patch_id = patch.id; | 94 | let patch_id = patch.id; |
| 98 | 95 | ||
| 99 | println!("Created patch {} (kind {})", patch_id, patch.kind.as_u16()); | 96 | println!("Created patch {} (kind {})", patch_id, patch.kind.as_u16()); |
| 100 | for tag in patch.tags.iter() { | ||
| 101 | println!(" Tag: {:?}", tag.as_slice()); | ||
| 102 | } | ||
| 103 | 97 | ||
| 104 | // 7. Send announcement and patch to relay_a ONLY | 98 | // 7. Send patch to relay_a |
| 105 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | 99 | let client_a = TestClient::new(relay_a.url(), keys.clone()) |
| 106 | .await | 100 | .await |
| 107 | .expect("Failed to connect to relay_a"); | 101 | .expect("Failed to connect to relay_a"); |
| 108 | 102 | ||
| 109 | client_a | 103 | client_a |
| 110 | .send_event(&announcement) | ||
| 111 | .await | ||
| 112 | .expect("Failed to send announcement to relay_a"); | ||
| 113 | println!("Announcement sent to relay_a"); | ||
| 114 | |||
| 115 | client_a | ||
| 116 | .send_event(&patch) | 104 | .send_event(&patch) |
| 117 | .await | 105 | .await |
| 118 | .expect("Failed to send patch to relay_a"); | 106 | .expect("Failed to send patch to relay_a"); |
| @@ -120,18 +108,10 @@ async fn test_discovers_layer3_via_layer2() { | |||
| 120 | 108 | ||
| 121 | client_a.disconnect().await; | 109 | client_a.disconnect().await; |
| 122 | 110 | ||
| 123 | // 8. Send announcement to relay_b directly (triggers discovery of relay_a) | 111 | // 8. Set up announcement on relay_b (triggers discovery of relay_a) |
| 124 | let client_b = TestClient::new(relay_b.url(), keys.clone()) | 112 | let (_announcement_b, _git_dir_b) = |
| 125 | .await | 113 | setup_announcement_on_relay(&relay_b, &keys, &domain_refs, repo_id).await; |
| 126 | .expect("Failed to connect to relay_b"); | 114 | println!("Announcement set up on relay_b (should trigger discovery of relay_a)"); |
| 127 | |||
| 128 | client_b | ||
| 129 | .send_event(&announcement) | ||
| 130 | .await | ||
| 131 | .expect("Failed to send announcement to relay_b"); | ||
| 132 | println!("Announcement sent to relay_b (should trigger discovery of relay_a)"); | ||
| 133 | |||
| 134 | client_b.disconnect().await; | ||
| 135 | 115 | ||
| 136 | // 9. Wait for relay_b to discover relay_a and sync the patch | 116 | // 9. Wait for relay_b to discover relay_a and sync the patch |
| 137 | println!("Waiting 3s for relay_b to discover relay_a and sync patch..."); | 117 | println!("Waiting 3s for relay_b to discover relay_a and sync patch..."); |
| @@ -197,19 +177,20 @@ async fn test_relay_discovery_via_announcements_with_historic_sync() { | |||
| 197 | // 3. Create test keys | 177 | // 3. Create test keys |
| 198 | let keys = Keys::generate(); | 178 | let keys = Keys::generate(); |
| 199 | 179 | ||
| 200 | // 4. Create the event chain on relay_a: | 180 | // 4. Set up repository on relay_a with git data and a Layer 2 issue |
| 201 | 181 | ||
| 202 | // Layer 1: Repository announcement | 182 | // Layer 1: Set up announcement with git data |
| 203 | let announcement = create_repo_announcement( | 183 | let domains = vec![relay_a.domain(), relay_b.domain()]; |
| 204 | &keys, | 184 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); |
| 205 | &[&relay_a.domain(), &relay_b.domain()], | 185 | let repo_id = "test-repo-chain"; |
| 206 | "test-repo-chain", | 186 | |
| 207 | ); | 187 | let (announcement, _git_dir_a) = |
| 188 | setup_announcement_on_relay(&relay_a, &keys, &domain_refs, repo_id).await; | ||
| 208 | let announcement_id = announcement.id; | 189 | let announcement_id = announcement.id; |
| 209 | println!("Created announcement {} (Layer 1)", announcement_id); | 190 | println!("Announcement {} set up on relay_a with git data (Layer 1)", announcement_id); |
| 210 | 191 | ||
| 211 | // Build repo coordinate for Layer 2 reference | 192 | // Build repo coordinate for Layer 2 reference |
| 212 | let repo_coord = repo_coord(&keys, "test-repo-chain"); | 193 | let repo_coord = repo_coord(&keys, repo_id); |
| 213 | 194 | ||
| 214 | // Layer 2: Issue referencing the repo | 195 | // Layer 2: Issue referencing the repo |
| 215 | let issue = build_layer2_issue_event(&keys, &repo_coord, "Test issue for chain discovery") | 196 | let issue = build_layer2_issue_event(&keys, &repo_coord, "Test issue for chain discovery") |
| @@ -217,35 +198,23 @@ async fn test_relay_discovery_via_announcements_with_historic_sync() { | |||
| 217 | let issue_id = issue.id; | 198 | let issue_id = issue.id; |
| 218 | println!("Created issue {} (Layer 2)", issue_id); | 199 | println!("Created issue {} (Layer 2)", issue_id); |
| 219 | 200 | ||
| 220 | // 5. Send all events to relay_a | 201 | // 5. Send issue to relay_a |
| 221 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | 202 | let client_a = TestClient::new(relay_a.url(), keys.clone()) |
| 222 | .await | 203 | .await |
| 223 | .expect("Failed to connect to relay_a"); | 204 | .expect("Failed to connect to relay_a"); |
| 224 | 205 | ||
| 225 | client_a | 206 | client_a |
| 226 | .send_event(&announcement) | ||
| 227 | .await | ||
| 228 | .expect("Failed to send announcement"); | ||
| 229 | client_a | ||
| 230 | .send_event(&issue) | 207 | .send_event(&issue) |
| 231 | .await | 208 | .await |
| 232 | .expect("Failed to send issue"); | 209 | .expect("Failed to send issue"); |
| 233 | 210 | ||
| 234 | println!("Events sent to relay_a"); | 211 | println!("Issue sent to relay_a"); |
| 235 | client_a.disconnect().await; | 212 | client_a.disconnect().await; |
| 236 | 213 | ||
| 237 | // 6. Send only the announcement to relay_b (triggers discovery) | 214 | // 6. Set up announcement on relay_b (triggers discovery of relay_a) |
| 238 | let client_b = TestClient::new(relay_b.url(), keys.clone()) | 215 | let (_announcement_b, _git_dir_b) = |
| 239 | .await | 216 | setup_announcement_on_relay(&relay_b, &keys, &domain_refs, repo_id).await; |
| 240 | .expect("Failed to connect to relay_b"); | 217 | println!("Announcement set up on relay_b (should trigger discovery of relay_a)"); |
| 241 | |||
| 242 | client_b | ||
| 243 | .send_event(&announcement) | ||
| 244 | .await | ||
| 245 | .expect("Failed to send announcement to relay_b"); | ||
| 246 | println!("Announcement sent to relay_b (should trigger discovery)"); | ||
| 247 | |||
| 248 | client_b.disconnect().await; | ||
| 249 | 218 | ||
| 250 | // 7. Wait for sync | 219 | // 7. Wait for sync |
| 251 | println!("Waiting 3s for Layer 2 sync..."); | 220 | println!("Waiting 3s for Layer 2 sync..."); |
| @@ -327,65 +296,32 @@ async fn test_recursive_relay_discovery_via_announcements_with_historic_sync() { | |||
| 327 | let keys_x = Keys::generate(); | 296 | let keys_x = Keys::generate(); |
| 328 | let keys_y = Keys::generate(); | 297 | let keys_y = Keys::generate(); |
| 329 | 298 | ||
| 330 | // 3. Create announcement_x on relay_b (lists all three relays: A+B+C) | 299 | // 3. Set up announcement_x on relay_b (lists all three relays: A+B+C) with git data |
| 331 | let announcement_x = create_repo_announcement( | 300 | let domains_x = vec![relay_a.domain(), relay_b.domain(), relay_c.domain()]; |
| 332 | &keys_x, | 301 | let domain_refs_x: Vec<&str> = domains_x.iter().map(|s| s.as_str()).collect(); |
| 333 | &[&relay_a.domain(), &relay_b.domain(), &relay_c.domain()], | ||
| 334 | "repo-x-all-relays", | ||
| 335 | ); | ||
| 336 | let announcement_x_id = announcement_x.id; | ||
| 337 | println!("Created announcement_x {} listing A+B+C", announcement_x_id); | ||
| 338 | for tag in announcement_x.tags.iter() { | ||
| 339 | println!(" Tag: {:?}", tag.as_slice()); | ||
| 340 | } | ||
| 341 | |||
| 342 | // 4. Create announcement_y on relay_c (lists only A+C, NOT B) | ||
| 343 | let announcement_y = create_repo_announcement( | ||
| 344 | &keys_y, | ||
| 345 | &[&relay_a.domain(), &relay_c.domain()], | ||
| 346 | "repo-y-ac-only", | ||
| 347 | ); | ||
| 348 | let announcement_y_id = announcement_y.id; | ||
| 349 | println!( | ||
| 350 | "Created announcement_y {} listing A+C only", | ||
| 351 | announcement_y_id | ||
| 352 | ); | ||
| 353 | for tag in announcement_y.tags.iter() { | ||
| 354 | println!(" Tag: {:?}", tag.as_slice()); | ||
| 355 | } | ||
| 356 | |||
| 357 | // 5. Send announcement_x to relay_b only | ||
| 358 | let client_b = TestClient::new(relay_b.url(), keys_x.clone()) | ||
| 359 | .await | ||
| 360 | .expect("Failed to connect to relay_b"); | ||
| 361 | |||
| 362 | client_b | ||
| 363 | .send_event(&announcement_x) | ||
| 364 | .await | ||
| 365 | .expect("Failed to send announcement_x to relay_b"); | ||
| 366 | println!("announcement_x sent to relay_b"); | ||
| 367 | |||
| 368 | client_b.disconnect().await; | ||
| 369 | 302 | ||
| 370 | // 6. Send announcement_y to relay_c only | 303 | let (announcement_x, _git_dir_b) = |
| 371 | let client_c = TestClient::new(relay_c.url(), keys_y.clone()) | 304 | setup_announcement_on_relay(&relay_b, &keys_x, &domain_refs_x, "repo-x-all-relays").await; |
| 372 | .await | 305 | let announcement_x_id = announcement_x.id; |
| 373 | .expect("Failed to connect to relay_c"); | 306 | println!("announcement_x {} set up on relay_b with git data (listing A+B+C)", announcement_x_id); |
| 374 | 307 | ||
| 375 | client_c | 308 | // 4. Set up announcement_y on relay_c (lists only A+C, NOT B) with git data |
| 376 | .send_event(&announcement_y) | 309 | let domains_y = vec![relay_a.domain(), relay_c.domain()]; |
| 377 | .await | 310 | let domain_refs_y: Vec<&str> = domains_y.iter().map(|s| s.as_str()).collect(); |
| 378 | .expect("Failed to send announcement_y to relay_c"); | ||
| 379 | println!("announcement_y sent to relay_c"); | ||
| 380 | 311 | ||
| 381 | client_c.disconnect().await; | 312 | let (announcement_y, _git_dir_c) = |
| 313 | setup_announcement_on_relay(&relay_c, &keys_y, &domain_refs_y, "repo-y-ac-only").await; | ||
| 314 | let announcement_y_id = announcement_y.id; | ||
| 315 | println!("announcement_y {} set up on relay_c with git data (listing A+C only)", announcement_y_id); | ||
| 382 | 316 | ||
| 383 | // 7. Wait for relay_a to: | 317 | // 7. Wait for relay_a to: |
| 384 | // - Sync from bootstrap relay_b (gets announcement_x) | 318 | // - Sync from bootstrap relay_b (gets announcement_x) |
| 385 | // - Discover relay_c from announcement_x's relays tag | 319 | // - Discover relay_c from announcement_x's relays tag |
| 386 | // - Connect to relay_c and sync announcement_y | 320 | // - Connect to relay_c and sync announcement_y |
| 387 | println!("Waiting 5s for recursive relay discovery..."); | 321 | // With purgatory, each relay needs to: sync announcement → purgatory → sync state event → |
| 388 | tokio::time::sleep(Duration::from_secs(5)).await; | 322 | // immediate purgatory sync → fetch git data → promote. Allow extra time for this. |
| 323 | println!("Waiting 12s for recursive relay discovery (with purgatory flow)..."); | ||
| 324 | tokio::time::sleep(Duration::from_secs(12)).await; | ||
| 389 | 325 | ||
| 390 | // 8. Verify announcement_x was synced to relay_a (from bootstrap relay_b) | 326 | // 8. Verify announcement_x was synced to relay_a (from bootstrap relay_b) |
| 391 | let filter_x = Filter::new() | 327 | let filter_x = Filter::new() |
diff --git a/tests/sync/historic_sync.rs b/tests/sync/historic_sync.rs index aec2819..723b776 100644 --- a/tests/sync/historic_sync.rs +++ b/tests/sync/historic_sync.rs | |||
| @@ -224,34 +224,24 @@ async fn test_history_sync_without_negentropy() { | |||
| 224 | // Create keys | 224 | // Create keys |
| 225 | let keys = Keys::generate(); | 225 | let keys = Keys::generate(); |
| 226 | 226 | ||
| 227 | // Create announcement listing BOTH relay domains | 227 | // Set up announcement on source with git data |
| 228 | // This event will exist on source BEFORE syncing relay ever connects | 228 | // (purgatory requires git data before announcements are accepted) |
| 229 | let announcement = create_repo_announcement( | 229 | let domains = vec![source.domain(), syncing_domain.clone()]; |
| 230 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); | ||
| 231 | let (announcement, _git_dir) = setup_announcement_on_relay( | ||
| 232 | &source, | ||
| 230 | &keys, | 233 | &keys, |
| 231 | &[&source.domain(), &syncing_domain], | 234 | &domain_refs, |
| 232 | "test-repo-history-no-negentropy", | 235 | "test-repo-history-no-negentropy", |
| 233 | ); | 236 | ) |
| 237 | .await; | ||
| 234 | let announcement_id = announcement.id; | 238 | let announcement_id = announcement.id; |
| 235 | 239 | ||
| 236 | println!( | 240 | println!( |
| 237 | "Created announcement {} (kind {})", | 241 | "Announcement {} set up on source with git data (event exists BEFORE syncing relay connects)", |
| 238 | announcement_id, | 242 | announcement_id |
| 239 | announcement.kind.as_u16() | ||
| 240 | ); | 243 | ); |
| 241 | 244 | ||
| 242 | // Send announcement to source (event now exists BEFORE syncing relay connects) | ||
| 243 | let client = TestClient::new(source.url(), keys.clone()) | ||
| 244 | .await | ||
| 245 | .expect("Failed to connect to source"); | ||
| 246 | |||
| 247 | client | ||
| 248 | .send_event(&announcement) | ||
| 249 | .await | ||
| 250 | .expect("Failed to send announcement to source"); | ||
| 251 | println!("Announcement sent to source (event exists BEFORE syncing relay connects)"); | ||
| 252 | |||
| 253 | client.disconnect().await; | ||
| 254 | |||
| 255 | // Wait to ensure event is stored | 245 | // Wait to ensure event is stored |
| 256 | tokio::time::sleep(Duration::from_millis(500)).await; | 246 | tokio::time::sleep(Duration::from_millis(500)).await; |
| 257 | 247 | ||
diff --git a/tests/sync/live_sync.rs b/tests/sync/live_sync.rs index 8ee3119..4289004 100644 --- a/tests/sync/live_sync.rs +++ b/tests/sync/live_sync.rs | |||
| @@ -56,43 +56,24 @@ async fn test_live_sync_layer2_events() { | |||
| 56 | // 3. Create test keys | 56 | // 3. Create test keys |
| 57 | let keys = Keys::generate(); | 57 | let keys = Keys::generate(); |
| 58 | 58 | ||
| 59 | // 4. Create a repository announcement that lists BOTH relays | 59 | // 4. Create a repository announcement on both relays with git data |
| 60 | // (purgatory requires git data before announcements are accepted) | ||
| 60 | let repo_id = "test-repo-live-l2"; | 61 | let repo_id = "test-repo-live-l2"; |
| 61 | let announcement = | 62 | let domains = vec![relay_a.domain(), relay_b.domain()]; |
| 62 | create_repo_announcement(&keys, &[&relay_a.domain(), &relay_b.domain()], repo_id); | 63 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); |
| 63 | 64 | ||
| 64 | println!( | 65 | let (_announcement, _git_dir_a) = |
| 65 | "Created announcement {} (kind {})", | 66 | setup_announcement_on_relay(&relay_a, &keys, &domain_refs, repo_id).await; |
| 66 | announcement.id, | 67 | println!("Announcement set up on relay_a with git data"); |
| 67 | announcement.kind.as_u16() | ||
| 68 | ); | ||
| 69 | |||
| 70 | // 5. Send announcement to relay_a | ||
| 71 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | ||
| 72 | .await | ||
| 73 | .expect("Failed to connect to relay_a"); | ||
| 74 | |||
| 75 | client_a | ||
| 76 | .send_event(&announcement) | ||
| 77 | .await | ||
| 78 | .expect("Failed to send announcement to relay_a"); | ||
| 79 | println!("Announcement sent to relay_a"); | ||
| 80 | |||
| 81 | // 6. Send announcement to relay_b (triggers discovery of relay_a) | ||
| 82 | let client_b = TestClient::new(relay_b.url(), keys.clone()) | ||
| 83 | .await | ||
| 84 | .expect("Failed to connect to relay_b"); | ||
| 85 | 68 | ||
| 86 | client_b | 69 | let (_announcement_b, _git_dir_b) = |
| 87 | .send_event(&announcement) | 70 | setup_announcement_on_relay(&relay_b, &keys, &domain_refs, repo_id).await; |
| 88 | .await | 71 | println!("Announcement set up on relay_b with git data (triggers discovery)"); |
| 89 | .expect("Failed to send announcement to relay_b"); | ||
| 90 | println!("Announcement sent to relay_b (triggers discovery)"); | ||
| 91 | 72 | ||
| 92 | // 7. Wait for discovery to complete | 73 | // 5. Wait for discovery to complete |
| 93 | tokio::time::sleep(Duration::from_secs(1)).await; | 74 | tokio::time::sleep(Duration::from_secs(1)).await; |
| 94 | 75 | ||
| 95 | // 8. Create and send a Layer 2 issue event (using helper) | 76 | // 6. Create and send a Layer 2 issue event (using helper) |
| 96 | let repo_coordinate = repo_coord(&keys, repo_id); | 77 | let repo_coordinate = repo_coord(&keys, repo_id); |
| 97 | let issue = build_layer2_issue_event(&keys, &repo_coordinate, "Test Issue for Live Sync") | 78 | let issue = build_layer2_issue_event(&keys, &repo_coordinate, "Test Issue for Live Sync") |
| 98 | .expect("Failed to create issue event"); | 79 | .expect("Failed to create issue event"); |
| @@ -104,6 +85,10 @@ async fn test_live_sync_layer2_events() { | |||
| 104 | } | 85 | } |
| 105 | 86 | ||
| 106 | // Send issue to relay_a only | 87 | // Send issue to relay_a only |
| 88 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | ||
| 89 | .await | ||
| 90 | .expect("Failed to connect to relay_a"); | ||
| 91 | |||
| 107 | client_a | 92 | client_a |
| 108 | .send_event(&issue) | 93 | .send_event(&issue) |
| 109 | .await | 94 | .await |
| @@ -111,7 +96,6 @@ async fn test_live_sync_layer2_events() { | |||
| 111 | println!("Issue sent to relay_a"); | 96 | println!("Issue sent to relay_a"); |
| 112 | 97 | ||
| 113 | client_a.disconnect().await; | 98 | client_a.disconnect().await; |
| 114 | client_b.disconnect().await; | ||
| 115 | 99 | ||
| 116 | // 9. Wait and verify event syncs to relay_b | 100 | // 9. Wait and verify event syncs to relay_b |
| 117 | let filter = Filter::new() | 101 | let filter = Filter::new() |
| @@ -166,30 +150,19 @@ async fn test_live_sync_layer3_events() { | |||
| 166 | 150 | ||
| 167 | let keys = Keys::generate(); | 151 | let keys = Keys::generate(); |
| 168 | 152 | ||
| 169 | // 2. Create and send repository announcement to both relays | 153 | // 2. Create and send repository announcement to both relays with git data |
| 154 | // (purgatory requires git data before announcements are accepted) | ||
| 170 | let repo_id = "test-repo-live-l3"; | 155 | let repo_id = "test-repo-live-l3"; |
| 171 | let announcement = | 156 | let domains = vec![relay_a.domain(), relay_b.domain()]; |
| 172 | create_repo_announcement(&keys, &[&relay_a.domain(), &relay_b.domain()], repo_id); | 157 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); |
| 173 | 158 | ||
| 174 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | 159 | let (_announcement, _git_dir_a) = |
| 175 | .await | 160 | setup_announcement_on_relay(&relay_a, &keys, &domain_refs, repo_id).await; |
| 176 | .expect("Failed to connect to relay_a"); | 161 | println!("Announcement set up on relay_a with git data"); |
| 177 | 162 | ||
| 178 | let client_b = TestClient::new(relay_b.url(), keys.clone()) | 163 | let (_announcement_b, _git_dir_b) = |
| 179 | .await | 164 | setup_announcement_on_relay(&relay_b, &keys, &domain_refs, repo_id).await; |
| 180 | .expect("Failed to connect to relay_b"); | 165 | println!("Announcement set up on relay_b with git data (triggers discovery)"); |
| 181 | |||
| 182 | client_a | ||
| 183 | .send_event(&announcement) | ||
| 184 | .await | ||
| 185 | .expect("Failed to send announcement to relay_a"); | ||
| 186 | println!("Announcement sent to relay_a"); | ||
| 187 | |||
| 188 | client_b | ||
| 189 | .send_event(&announcement) | ||
| 190 | .await | ||
| 191 | .expect("Failed to send announcement to relay_b"); | ||
| 192 | println!("Announcement sent to relay_b (triggers discovery)"); | ||
| 193 | 166 | ||
| 194 | // 3. Wait for discovery | 167 | // 3. Wait for discovery |
| 195 | tokio::time::sleep(Duration::from_secs(1)).await; | 168 | tokio::time::sleep(Duration::from_secs(1)).await; |
| @@ -200,6 +173,10 @@ async fn test_live_sync_layer3_events() { | |||
| 200 | .expect("Failed to create issue"); | 173 | .expect("Failed to create issue"); |
| 201 | let issue_id = issue.id; | 174 | let issue_id = issue.id; |
| 202 | 175 | ||
| 176 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | ||
| 177 | .await | ||
| 178 | .expect("Failed to connect to relay_a"); | ||
| 179 | |||
| 203 | client_a | 180 | client_a |
| 204 | .send_event(&issue) | 181 | .send_event(&issue) |
| 205 | .await | 182 | .await |
| @@ -243,7 +220,6 @@ async fn test_live_sync_layer3_events() { | |||
| 243 | println!("Issue synced to relay_b: {}", issue_synced); | 220 | println!("Issue synced to relay_b: {}", issue_synced); |
| 244 | 221 | ||
| 245 | client_a.disconnect().await; | 222 | client_a.disconnect().await; |
| 246 | client_b.disconnect().await; | ||
| 247 | 223 | ||
| 248 | // 7. Wait and verify comment syncs to relay_b | 224 | // 7. Wait and verify comment syncs to relay_b |
| 249 | let comment_filter = Filter::new() | 225 | let comment_filter = Filter::new() |
| @@ -343,29 +319,17 @@ async fn test_live_sync_event_ordering() { | |||
| 343 | 319 | ||
| 344 | let keys = Keys::generate(); | 320 | let keys = Keys::generate(); |
| 345 | 321 | ||
| 346 | // 2. Create and send repository announcement to both relays | 322 | // 2. Create and send repository announcement to both relays with git data |
| 323 | // (purgatory requires git data before announcements are accepted) | ||
| 347 | let repo_id = "test-repo-ordering"; | 324 | let repo_id = "test-repo-ordering"; |
| 348 | let announcement = | 325 | let domains = vec![relay_a.domain(), relay_b.domain()]; |
| 349 | create_repo_announcement(&keys, &[&relay_a.domain(), &relay_b.domain()], repo_id); | 326 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); |
| 350 | 327 | ||
| 351 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | 328 | let (_announcement, _git_dir_a) = |
| 352 | .await | 329 | setup_announcement_on_relay(&relay_a, &keys, &domain_refs, repo_id).await; |
| 353 | .expect("Failed to connect to relay_a"); | 330 | let (_announcement_b, _git_dir_b) = |
| 354 | 331 | setup_announcement_on_relay(&relay_b, &keys, &domain_refs, repo_id).await; | |
| 355 | let client_b = TestClient::new(relay_b.url(), keys.clone()) | 332 | println!("Announcements set up on both relays with git data"); |
| 356 | .await | ||
| 357 | .expect("Failed to connect to relay_b"); | ||
| 358 | |||
| 359 | client_a | ||
| 360 | .send_event(&announcement) | ||
| 361 | .await | ||
| 362 | .expect("Failed to send announcement to relay_a"); | ||
| 363 | |||
| 364 | client_b | ||
| 365 | .send_event(&announcement) | ||
| 366 | .await | ||
| 367 | .expect("Failed to send announcement to relay_b"); | ||
| 368 | println!("Announcements sent to both relays"); | ||
| 369 | 333 | ||
| 370 | // 3. Wait for discovery | 334 | // 3. Wait for discovery |
| 371 | tokio::time::sleep(Duration::from_secs(1)).await; | 335 | tokio::time::sleep(Duration::from_secs(1)).await; |
| @@ -375,6 +339,10 @@ async fn test_live_sync_event_ordering() { | |||
| 375 | let mut issue_ids = Vec::new(); | 339 | let mut issue_ids = Vec::new(); |
| 376 | let mut expected_order_timestamps = Vec::new(); | 340 | let mut expected_order_timestamps = Vec::new(); |
| 377 | 341 | ||
| 342 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | ||
| 343 | .await | ||
| 344 | .expect("Failed to connect to relay_a"); | ||
| 345 | |||
| 378 | for i in 1..=3 { | 346 | for i in 1..=3 { |
| 379 | let issue = build_layer2_issue_event( | 347 | let issue = build_layer2_issue_event( |
| 380 | &keys, | 348 | &keys, |
| @@ -402,7 +370,6 @@ async fn test_live_sync_event_ordering() { | |||
| 402 | } | 370 | } |
| 403 | 371 | ||
| 404 | client_a.disconnect().await; | 372 | client_a.disconnect().await; |
| 405 | client_b.disconnect().await; | ||
| 406 | 373 | ||
| 407 | // 5. Wait for all events to sync | 374 | // 5. Wait for all events to sync |
| 408 | tokio::time::sleep(Duration::from_secs(3)).await; | 375 | tokio::time::sleep(Duration::from_secs(3)).await; |
diff --git a/tests/sync/maintainer_reprocessing.rs b/tests/sync/maintainer_reprocessing.rs index df1bf78..266a437 100644 --- a/tests/sync/maintainer_reprocessing.rs +++ b/tests/sync/maintainer_reprocessing.rs | |||
| @@ -7,7 +7,10 @@ use std::time::Duration; | |||
| 7 | 7 | ||
| 8 | use nostr_sdk::prelude::*; | 8 | use nostr_sdk::prelude::*; |
| 9 | 9 | ||
| 10 | use crate::common::{sync_helpers::*, TestRelay}; | 10 | use crate::common::{ |
| 11 | sync_helpers::*, | ||
| 12 | TestRelay, | ||
| 13 | }; | ||
| 11 | 14 | ||
| 12 | /// Test that maintainer announcements are re-processed immediately when owner announcement accepted | 15 | /// Test that maintainer announcements are re-processed immediately when owner announcement accepted |
| 13 | /// | 16 | /// |
| @@ -37,10 +40,12 @@ async fn test_maintainer_announcement_reprocessed_immediately() { | |||
| 37 | 40 | ||
| 38 | let start = std::time::Instant::now(); | 41 | let start = std::time::Instant::now(); |
| 39 | 42 | ||
| 40 | // Step 1: Send maintainer announcement to relay_a (will be rejected - doesn't list relay_b) | 43 | // Step 1: Send maintainer announcement to relay_a (will be rejected by relay_b - doesn't list relay_b) |
| 41 | let client_a = TestClient::new(relay_a.url(), maintainer_keys.clone()) | 44 | // Use HTTP clone URL pointing to relay_a's git endpoint so it can be released from purgatory |
| 42 | .await | 45 | let maintainer_npub = maintainer_keys |
| 43 | .expect("Failed to connect to relay_a"); | 46 | .public_key() |
| 47 | .to_bech32() | ||
| 48 | .expect("Failed to get npub"); | ||
| 44 | 49 | ||
| 45 | let maintainer_announcement = | 50 | let maintainer_announcement = |
| 46 | EventBuilder::new(Kind::GitRepoAnnouncement, "Maintainer's repository") | 51 | EventBuilder::new(Kind::GitRepoAnnouncement, "Maintainer's repository") |
| @@ -48,27 +53,50 @@ async fn test_maintainer_announcement_reprocessed_immediately() { | |||
| 48 | Tag::identifier(identifier), | 53 | Tag::identifier(identifier), |
| 49 | Tag::custom( | 54 | Tag::custom( |
| 50 | TagKind::custom("clone"), | 55 | TagKind::custom("clone"), |
| 51 | vec![format!("https://{}/{}.git", relay_a.domain(), identifier)], | 56 | vec![format!( |
| 57 | "http://{}/{}/{}.git", | ||
| 58 | relay_a.domain(), | ||
| 59 | maintainer_npub, | ||
| 60 | identifier | ||
| 61 | )], | ||
| 52 | ), | 62 | ), |
| 53 | Tag::custom(TagKind::custom("relays"), vec![relay_a.url().to_string()]), | 63 | Tag::custom(TagKind::custom("relays"), vec![relay_a.url().to_string()]), |
| 54 | ]) | 64 | ]) |
| 55 | .sign_with_keys(&maintainer_keys) | 65 | .sign_with_keys(&maintainer_keys) |
| 56 | .unwrap(); | 66 | .unwrap(); |
| 57 | 67 | ||
| 58 | client_a.send_event(&maintainer_announcement).await.unwrap(); | 68 | send_to_relay(&relay_a, &maintainer_announcement) |
| 69 | .await | ||
| 70 | .unwrap(); | ||
| 59 | println!("✓ Maintainer announcement sent to relay_a"); | 71 | println!("✓ Maintainer announcement sent to relay_a"); |
| 60 | 72 | ||
| 61 | // Step 2: Send owner announcement to relay_b (lists relay_a + maintainer) | 73 | // Push git data for maintainer's repo to relay_a → releases maintainer announcement from purgatory |
| 62 | let client_b = TestClient::new(relay_b.url(), owner_keys.clone()) | 74 | let _git_dir_maintainer = push_git_data_to_relay( |
| 63 | .await | 75 | &relay_a, |
| 64 | .expect("Failed to connect to relay_b"); | 76 | &maintainer_keys, |
| 77 | identifier, | ||
| 78 | &[&relay_a.domain()], | ||
| 79 | ) | ||
| 80 | .await; | ||
| 81 | println!("✓ Maintainer git data pushed to relay_a (announcement released from purgatory)"); | ||
| 82 | |||
| 83 | // Step 2: Set up owner announcement on relay_b (lists relay_a + maintainer) with git data | ||
| 84 | let owner_npub = owner_keys | ||
| 85 | .public_key() | ||
| 86 | .to_bech32() | ||
| 87 | .expect("Failed to get npub"); | ||
| 65 | 88 | ||
| 66 | let owner_announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "Owner's repository") | 89 | let owner_announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "Owner's repository") |
| 67 | .tags(vec![ | 90 | .tags(vec![ |
| 68 | Tag::identifier(identifier), | 91 | Tag::identifier(identifier), |
| 69 | Tag::custom( | 92 | Tag::custom( |
| 70 | TagKind::custom("clone"), | 93 | TagKind::custom("clone"), |
| 71 | vec![format!("https://{}/{}.git", relay_b.domain(), identifier)], | 94 | vec![format!( |
| 95 | "http://{}/{}/{}.git", | ||
| 96 | relay_b.domain(), | ||
| 97 | owner_npub, | ||
| 98 | identifier | ||
| 99 | )], | ||
| 72 | ), | 100 | ), |
| 73 | Tag::custom( | 101 | Tag::custom( |
| 74 | TagKind::custom("relays"), | 102 | TagKind::custom("relays"), |
| @@ -82,9 +110,14 @@ async fn test_maintainer_announcement_reprocessed_immediately() { | |||
| 82 | .sign_with_keys(&owner_keys) | 110 | .sign_with_keys(&owner_keys) |
| 83 | .unwrap(); | 111 | .unwrap(); |
| 84 | 112 | ||
| 85 | client_b.send_event(&owner_announcement).await.unwrap(); | 113 | send_to_relay(&relay_b, &owner_announcement).await.unwrap(); |
| 86 | println!("✓ Owner announcement sent to relay_b"); | 114 | println!("✓ Owner announcement sent to relay_b"); |
| 87 | 115 | ||
| 116 | // Push git data for owner's repo to relay_b → releases owner announcement from purgatory | ||
| 117 | let _git_dir_owner = | ||
| 118 | push_git_data_to_relay(&relay_b, &owner_keys, identifier, &[&relay_b.domain()]).await; | ||
| 119 | println!("✓ Owner git data pushed to relay_b (announcement released from purgatory)"); | ||
| 120 | |||
| 88 | // Step 3: Wait for sync and re-processing (relay_b discovers relay_a, syncs, re-processes) | 121 | // Step 3: Wait for sync and re-processing (relay_b discovers relay_a, syncs, re-processes) |
| 89 | tokio::time::sleep(Duration::from_secs(3)).await; | 122 | tokio::time::sleep(Duration::from_secs(3)).await; |
| 90 | 123 | ||
| @@ -114,15 +147,13 @@ async fn test_maintainer_announcement_reprocessed_immediately() { | |||
| 114 | 147 | ||
| 115 | // Step 5: Verify it happened quickly (not 24 hours!) | 148 | // Step 5: Verify it happened quickly (not 24 hours!) |
| 116 | assert!( | 149 | assert!( |
| 117 | elapsed.as_secs() < 10, | 150 | elapsed.as_secs() < 15, |
| 118 | "Re-processing should happen in <10 seconds, took {:?}", | 151 | "Re-processing should happen in <15 seconds, took {:?}", |
| 119 | elapsed | 152 | elapsed |
| 120 | ); | 153 | ); |
| 121 | 154 | ||
| 122 | println!("✅ Maintainer announcement re-processed in {:?}", elapsed); | 155 | println!("✅ Maintainer announcement re-processed in {:?}", elapsed); |
| 123 | 156 | ||
| 124 | client_a.disconnect().await; | ||
| 125 | client_b.disconnect().await; | ||
| 126 | relay_a.stop().await; | 157 | relay_a.stop().await; |
| 127 | relay_b.stop().await; | 158 | relay_b.stop().await; |
| 128 | } | 159 | } |
| @@ -253,15 +284,18 @@ async fn test_multiple_maintainers_all_reprocessed() { | |||
| 253 | 284 | ||
| 254 | let identifier = "multi-maintainer-repo"; | 285 | let identifier = "multi-maintainer-repo"; |
| 255 | 286 | ||
| 256 | // Step 1: Send three maintainer announcements to relay_a | 287 | // Step 1: Send three maintainer announcements to relay_a with git data |
| 257 | let client_a = TestClient::new(relay_a.url(), maintainer1_keys.clone()) | 288 | // (purgatory requires git data before announcements are accepted) |
| 258 | .await | 289 | let mut git_dirs_maintainers = Vec::new(); |
| 259 | .expect("Failed to connect to relay_a"); | ||
| 260 | |||
| 261 | for (idx, maintainer_keys) in [&maintainer1_keys, &maintainer2_keys, &maintainer3_keys] | 290 | for (idx, maintainer_keys) in [&maintainer1_keys, &maintainer2_keys, &maintainer3_keys] |
| 262 | .iter() | 291 | .iter() |
| 263 | .enumerate() | 292 | .enumerate() |
| 264 | { | 293 | { |
| 294 | let m_npub = maintainer_keys | ||
| 295 | .public_key() | ||
| 296 | .to_bech32() | ||
| 297 | .expect("Failed to get npub"); | ||
| 298 | |||
| 265 | let announcement = EventBuilder::new( | 299 | let announcement = EventBuilder::new( |
| 266 | Kind::GitRepoAnnouncement, | 300 | Kind::GitRepoAnnouncement, |
| 267 | format!("Maintainer {} repository", idx + 1), | 301 | format!("Maintainer {} repository", idx + 1), |
| @@ -270,28 +304,45 @@ async fn test_multiple_maintainers_all_reprocessed() { | |||
| 270 | Tag::identifier(identifier), | 304 | Tag::identifier(identifier), |
| 271 | Tag::custom( | 305 | Tag::custom( |
| 272 | TagKind::custom("clone"), | 306 | TagKind::custom("clone"), |
| 273 | vec![format!("https://{}/{}.git", relay_a.domain(), identifier)], | 307 | vec![format!( |
| 308 | "http://{}/{}/{}.git", | ||
| 309 | relay_a.domain(), | ||
| 310 | m_npub, | ||
| 311 | identifier | ||
| 312 | )], | ||
| 274 | ), | 313 | ), |
| 275 | Tag::custom(TagKind::custom("relays"), vec![relay_a.url().to_string()]), | 314 | Tag::custom(TagKind::custom("relays"), vec![relay_a.url().to_string()]), |
| 276 | ]) | 315 | ]) |
| 277 | .sign_with_keys(maintainer_keys) | 316 | .sign_with_keys(maintainer_keys) |
| 278 | .unwrap(); | 317 | .unwrap(); |
| 279 | 318 | ||
| 280 | client_a.send_event(&announcement).await.unwrap(); | 319 | send_to_relay(&relay_a, &announcement).await.unwrap(); |
| 320 | |||
| 321 | // Push git data to release each maintainer's announcement from purgatory | ||
| 322 | let git_dir = | ||
| 323 | push_git_data_to_relay(&relay_a, maintainer_keys, identifier, &[&relay_a.domain()]) | ||
| 324 | .await; | ||
| 325 | git_dirs_maintainers.push(git_dir); | ||
| 281 | } | 326 | } |
| 282 | println!("✓ Three maintainer announcements sent to relay_a"); | 327 | println!("✓ Three maintainer announcements sent to relay_a with git data"); |
| 283 | 328 | ||
| 284 | // Step 2: Send owner announcement to relay_b (lists relay_a + all three maintainers) | 329 | // Step 2: Send owner announcement to relay_b (lists relay_a + all three maintainers) |
| 285 | let client_b = TestClient::new(relay_b.url(), owner_keys.clone()) | 330 | let owner_npub = owner_keys |
| 286 | .await | 331 | .public_key() |
| 287 | .expect("Failed to connect to relay_b"); | 332 | .to_bech32() |
| 333 | .expect("Failed to get npub"); | ||
| 288 | 334 | ||
| 289 | let owner_announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "Owner's repository") | 335 | let owner_announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "Owner's repository") |
| 290 | .tags(vec![ | 336 | .tags(vec![ |
| 291 | Tag::identifier(identifier), | 337 | Tag::identifier(identifier), |
| 292 | Tag::custom( | 338 | Tag::custom( |
| 293 | TagKind::custom("clone"), | 339 | TagKind::custom("clone"), |
| 294 | vec![format!("https://{}/{}.git", relay_b.domain(), identifier)], | 340 | vec![format!( |
| 341 | "http://{}/{}/{}.git", | ||
| 342 | relay_b.domain(), | ||
| 343 | owner_npub, | ||
| 344 | identifier | ||
| 345 | )], | ||
| 295 | ), | 346 | ), |
| 296 | Tag::custom( | 347 | Tag::custom( |
| 297 | TagKind::custom("relays"), | 348 | TagKind::custom("relays"), |
| @@ -309,9 +360,14 @@ async fn test_multiple_maintainers_all_reprocessed() { | |||
| 309 | .sign_with_keys(&owner_keys) | 360 | .sign_with_keys(&owner_keys) |
| 310 | .unwrap(); | 361 | .unwrap(); |
| 311 | 362 | ||
| 312 | client_b.send_event(&owner_announcement).await.unwrap(); | 363 | send_to_relay(&relay_b, &owner_announcement).await.unwrap(); |
| 313 | println!("✓ Owner announcement sent to relay_b"); | 364 | println!("✓ Owner announcement sent to relay_b"); |
| 314 | 365 | ||
| 366 | // Push git data for owner to relay_b → releases owner announcement from purgatory | ||
| 367 | let _git_dir_owner = | ||
| 368 | push_git_data_to_relay(&relay_b, &owner_keys, identifier, &[&relay_b.domain()]).await; | ||
| 369 | println!("✓ Owner git data pushed to relay_b (announcement released from purgatory)"); | ||
| 370 | |||
| 315 | // Step 3: Wait for sync and re-processing | 371 | // Step 3: Wait for sync and re-processing |
| 316 | tokio::time::sleep(Duration::from_secs(3)).await; | 372 | tokio::time::sleep(Duration::from_secs(3)).await; |
| 317 | 373 | ||
| @@ -333,8 +389,6 @@ async fn test_multiple_maintainers_all_reprocessed() { | |||
| 333 | 389 | ||
| 334 | println!("✅ All three maintainer announcements re-processed successfully"); | 390 | println!("✅ All three maintainer announcements re-processed successfully"); |
| 335 | 391 | ||
| 336 | client_a.disconnect().await; | ||
| 337 | client_b.disconnect().await; | ||
| 338 | relay_a.stop().await; | 392 | relay_a.stop().await; |
| 339 | relay_b.stop().await; | 393 | relay_b.stop().await; |
| 340 | } | 394 | } |
| @@ -356,12 +410,8 @@ async fn test_invalid_maintainer_pubkey_handled_gracefully() { | |||
| 356 | 410 | ||
| 357 | let identifier = "invalid-maintainer-repo"; | 411 | let identifier = "invalid-maintainer-repo"; |
| 358 | 412 | ||
| 359 | // Create client using TestClient helper | ||
| 360 | let client = TestClient::new(relay.url(), owner_keys.clone()) | ||
| 361 | .await | ||
| 362 | .expect("Failed to connect to relay"); | ||
| 363 | |||
| 364 | // Step 1: Send maintainer announcement (will be rejected - doesn't list our relay) | 413 | // Step 1: Send maintainer announcement (will be rejected - doesn't list our relay) |
| 414 | // This one uses example.com clone URL - it goes to purgatory on relay, never promoted | ||
| 365 | let maintainer_announcement = | 415 | let maintainer_announcement = |
| 366 | EventBuilder::new(Kind::GitRepoAnnouncement, "Maintainer's repository") | 416 | EventBuilder::new(Kind::GitRepoAnnouncement, "Maintainer's repository") |
| 367 | .tags(vec![ | 417 | .tags(vec![ |
| @@ -378,17 +428,28 @@ async fn test_invalid_maintainer_pubkey_handled_gracefully() { | |||
| 378 | .sign_with_keys(&maintainer_keys) | 428 | .sign_with_keys(&maintainer_keys) |
| 379 | .unwrap(); | 429 | .unwrap(); |
| 380 | 430 | ||
| 381 | // Send maintainer announcement - expect it to be rejected | 431 | // Send maintainer announcement - expect it to be rejected (purgatory / policy) |
| 382 | let _ = client.send_event(&maintainer_announcement).await; | 432 | send_to_relay(&relay, &maintainer_announcement).await.ok(); |
| 383 | tokio::time::sleep(Duration::from_millis(200)).await; | 433 | tokio::time::sleep(Duration::from_millis(200)).await; |
| 384 | 434 | ||
| 385 | // Step 2: Send owner announcement with INVALID maintainer hex | 435 | // Step 2: Set up owner announcement with INVALID maintainer hex and git data |
| 436 | // Use HTTP clone URL to relay's git endpoint so it can be released from purgatory | ||
| 437 | let owner_npub = owner_keys | ||
| 438 | .public_key() | ||
| 439 | .to_bech32() | ||
| 440 | .expect("Failed to get npub"); | ||
| 441 | |||
| 386 | let owner_announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "Owner's repository") | 442 | let owner_announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "Owner's repository") |
| 387 | .tags(vec![ | 443 | .tags(vec![ |
| 388 | Tag::identifier(identifier), | 444 | Tag::identifier(identifier), |
| 389 | Tag::custom( | 445 | Tag::custom( |
| 390 | TagKind::custom("clone"), | 446 | TagKind::custom("clone"), |
| 391 | vec![format!("https://{}/{}.git", relay.domain(), identifier)], | 447 | vec![format!( |
| 448 | "http://{}/{}/{}.git", | ||
| 449 | relay.domain(), | ||
| 450 | owner_npub, | ||
| 451 | identifier | ||
| 452 | )], | ||
| 392 | ), | 453 | ), |
| 393 | Tag::custom(TagKind::custom("relays"), vec![relay.url().to_string()]), | 454 | Tag::custom(TagKind::custom("relays"), vec![relay.url().to_string()]), |
| 394 | Tag::custom( | 455 | Tag::custom( |
| @@ -399,7 +460,14 @@ async fn test_invalid_maintainer_pubkey_handled_gracefully() { | |||
| 399 | .sign_with_keys(&owner_keys) | 460 | .sign_with_keys(&owner_keys) |
| 400 | .unwrap(); | 461 | .unwrap(); |
| 401 | 462 | ||
| 402 | client.send_event(&owner_announcement).await.unwrap(); | 463 | send_to_relay(&relay, &owner_announcement).await.unwrap(); |
| 464 | |||
| 465 | // Push git data to relay → releases owner announcement from purgatory | ||
| 466 | let _git_dir = | ||
| 467 | push_git_data_to_relay(&relay, &owner_keys, identifier, &[&relay.domain()]).await; | ||
| 468 | println!("✓ Owner git data pushed to relay (announcement released from purgatory)"); | ||
| 469 | |||
| 470 | // Wait for processing | ||
| 403 | tokio::time::sleep(Duration::from_millis(500)).await; | 471 | tokio::time::sleep(Duration::from_millis(500)).await; |
| 404 | 472 | ||
| 405 | // Step 3: Verify owner announcement accepted, maintainer not re-processed | 473 | // Step 3: Verify owner announcement accepted, maintainer not re-processed |
| @@ -429,6 +497,5 @@ async fn test_invalid_maintainer_pubkey_handled_gracefully() { | |||
| 429 | 497 | ||
| 430 | println!("✅ Invalid maintainer pubkey handled gracefully without panic"); | 498 | println!("✅ Invalid maintainer pubkey handled gracefully without panic"); |
| 431 | 499 | ||
| 432 | client.disconnect().await; | ||
| 433 | relay.stop().await; | 500 | relay.stop().await; |
| 434 | } | 501 | } |
diff --git a/tests/sync/metrics.rs b/tests/sync/metrics.rs index e8c75c7..e973bbb 100644 --- a/tests/sync/metrics.rs +++ b/tests/sync/metrics.rs | |||
| @@ -16,8 +16,8 @@ use nostr_sdk::prelude::*; | |||
| 16 | 16 | ||
| 17 | use crate::common::{ | 17 | use crate::common::{ |
| 18 | sync_helpers::{ | 18 | sync_helpers::{ |
| 19 | create_repo_announcement, fetch_metrics, wait_for_sync_connection, MetricsTestHarness, | 19 | create_repo_announcement, fetch_metrics, setup_announcement_on_relay, |
| 20 | ParsedMetrics, TestClient, | 20 | wait_for_sync_connection, MetricsTestHarness, ParsedMetrics, TestClient, |
| 21 | }, | 21 | }, |
| 22 | TestRelay, | 22 | TestRelay, |
| 23 | }; | 23 | }; |
| @@ -224,16 +224,17 @@ async fn test_startup_sync_event_count() { | |||
| 224 | // 3. Create test keys | 224 | // 3. Create test keys |
| 225 | let keys = Keys::generate(); | 225 | let keys = Keys::generate(); |
| 226 | 226 | ||
| 227 | // 4. Create an announcement that lists BOTH relays (required for discovery) | 227 | // 4. Set up announcement on SOURCE relay with git data |
| 228 | let announcement = create_repo_announcement( | 228 | // (purgatory requires git data before announcements are accepted) |
| 229 | &keys, | 229 | let repo_id = "test-repo-metrics"; |
| 230 | &[&source_relay.domain(), &syncing_relay.domain()], | 230 | let domains = vec![source_relay.domain(), syncing_relay.domain()]; |
| 231 | "test-repo-metrics", | 231 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); |
| 232 | ); | 232 | |
| 233 | let (announcement, _git_dir_source) = | ||
| 234 | setup_announcement_on_relay(&source_relay, &keys, &domain_refs, repo_id).await; | ||
| 233 | println!( | 235 | println!( |
| 234 | "Created announcement {} (kind {})", | 236 | "Announcement {} set up on source relay with git data", |
| 235 | announcement.id, | 237 | announcement.id |
| 236 | announcement.kind.as_u16() | ||
| 237 | ); | 238 | ); |
| 238 | 239 | ||
| 239 | // 5. Build the repo coordinate for the 'a' tag in the patches | 240 | // 5. Build the repo coordinate for the 'a' tag in the patches |
| @@ -241,7 +242,7 @@ async fn test_startup_sync_event_count() { | |||
| 241 | "{}:{}:{}", | 242 | "{}:{}:{}", |
| 242 | Kind::GitRepoAnnouncement.as_u16(), | 243 | Kind::GitRepoAnnouncement.as_u16(), |
| 243 | keys.public_key().to_hex(), | 244 | keys.public_key().to_hex(), |
| 244 | "test-repo-metrics" | 245 | repo_id |
| 245 | ); | 246 | ); |
| 246 | 247 | ||
| 247 | // 6. Create 3 patch events (Layer 2) that reference the announcement | 248 | // 6. Create 3 patch events (Layer 2) that reference the announcement |
| @@ -257,17 +258,11 @@ async fn test_startup_sync_event_count() { | |||
| 257 | .collect(); | 258 | .collect(); |
| 258 | println!("Created {} patches", patches.len()); | 259 | println!("Created {} patches", patches.len()); |
| 259 | 260 | ||
| 260 | // 7. Send announcement + patches to SOURCE relay ONLY | 261 | // 7. Send patches to SOURCE relay |
| 261 | let source_client = TestClient::new(source_relay.url(), keys.clone()) | 262 | let source_client = TestClient::new(source_relay.url(), keys.clone()) |
| 262 | .await | 263 | .await |
| 263 | .expect("Failed to connect to source relay"); | 264 | .expect("Failed to connect to source relay"); |
| 264 | 265 | ||
| 265 | source_client | ||
| 266 | .send_event(&announcement) | ||
| 267 | .await | ||
| 268 | .expect("Failed to send announcement to source"); | ||
| 269 | println!("Announcement sent to source relay"); | ||
| 270 | |||
| 271 | for patch in &patches { | 266 | for patch in &patches { |
| 272 | source_client | 267 | source_client |
| 273 | .send_event(patch) | 268 | .send_event(patch) |
| @@ -277,17 +272,10 @@ async fn test_startup_sync_event_count() { | |||
| 277 | println!("Patches sent to source relay"); | 272 | println!("Patches sent to source relay"); |
| 278 | source_client.disconnect().await; | 273 | source_client.disconnect().await; |
| 279 | 274 | ||
| 280 | // 8. Send announcement to SYNCING relay (triggers discovery of source relay) | 275 | // 8. Set up announcement on SYNCING relay (triggers discovery of source relay) |
| 281 | let syncing_client = TestClient::new(syncing_relay.url(), keys.clone()) | 276 | let (_announcement_syncing, _git_dir_syncing) = |
| 282 | .await | 277 | setup_announcement_on_relay(&syncing_relay, &keys, &domain_refs, repo_id).await; |
| 283 | .expect("Failed to connect to syncing relay"); | 278 | println!("Announcement set up on syncing relay (triggers discovery of source)"); |
| 284 | |||
| 285 | syncing_client | ||
| 286 | .send_event(&announcement) | ||
| 287 | .await | ||
| 288 | .expect("Failed to send announcement to syncing relay"); | ||
| 289 | println!("Announcement sent to syncing relay (triggers discovery of source)"); | ||
| 290 | syncing_client.disconnect().await; | ||
| 291 | 279 | ||
| 292 | // 9. Wait for discovery + sync to complete | 280 | // 9. Wait for discovery + sync to complete |
| 293 | println!("Waiting 5s for discovery and sync..."); | 281 | println!("Waiting 5s for discovery and sync..."); |
| @@ -404,18 +392,35 @@ async fn test_connection_failure_increments_counter() { | |||
| 404 | /// Test that live sync events are counted in metrics. | 392 | /// Test that live sync events are counted in metrics. |
| 405 | /// | 393 | /// |
| 406 | /// This test validates that events received via live subscription | 394 | /// This test validates that events received via live subscription |
| 407 | /// (after sync connection is established) are counted separately | 395 | /// (after sync connection is established) are counted in metrics. |
| 408 | /// from startup/bootstrap events. | 396 | /// Uses Layer 2 patch events (not announcements) to avoid purgatory, |
| 397 | /// since Layer 2 events are accepted directly to the DB. | ||
| 409 | #[tokio::test] | 398 | #[tokio::test] |
| 410 | async fn test_live_sync_event_count() { | 399 | async fn test_live_sync_event_count() { |
| 411 | let mut harness = MetricsTestHarness::with_sources(1).await; | ||
| 412 | |||
| 413 | // Pre-allocate syncing relay port to include in announcements | 400 | // Pre-allocate syncing relay port to include in announcements |
| 414 | let sync_port = TestRelay::find_free_port(); | 401 | let sync_port = TestRelay::find_free_port(); |
| 415 | let sync_domain = format!("127.0.0.1:{}", sync_port); | 402 | let sync_domain = format!("127.0.0.1:{}", sync_port); |
| 416 | 403 | ||
| 404 | // Start source relay | ||
| 405 | let source_relay = TestRelay::start().await; | ||
| 406 | println!("Source relay started at {}", source_relay.url()); | ||
| 407 | |||
| 408 | // Set up announcement on source relay BEFORE starting syncing relay | ||
| 409 | // This allows discovery when syncing relay connects | ||
| 410 | let keys = Keys::generate(); | ||
| 411 | let repo_id = "live-metrics-repo"; | ||
| 412 | let domains = vec![source_relay.domain(), sync_domain.clone()]; | ||
| 413 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); | ||
| 414 | |||
| 415 | let (_announcement, _git_dir) = | ||
| 416 | setup_announcement_on_relay(&source_relay, &keys, &domain_refs, repo_id).await; | ||
| 417 | println!("Announcement set up on source relay with git data"); | ||
| 418 | |||
| 417 | // Start syncing relay with pre-allocated port | 419 | // Start syncing relay with pre-allocated port |
| 418 | harness.start_syncing_relay_on_port(0, sync_port).await; | 420 | let syncing_relay = |
| 421 | TestRelay::start_on_port_with_options(sync_port, Some(source_relay.url().to_string()), false) | ||
| 422 | .await; | ||
| 423 | println!("Syncing relay started at {}", syncing_relay.url()); | ||
| 419 | 424 | ||
| 420 | // Wait for sync connection to be fully established with EOSE received | 425 | // Wait for sync connection to be fully established with EOSE received |
| 421 | // This ensures we're in "live" mode before submitting test events | 426 | // This ensures we're in "live" mode before submitting test events |
| @@ -424,33 +429,61 @@ async fn test_live_sync_event_count() { | |||
| 424 | .await | 429 | .await |
| 425 | .expect("Sync connection should be established"); | 430 | .expect("Sync connection should be established"); |
| 426 | 431 | ||
| 427 | // Additional small delay to ensure EOSE has been processed | 432 | // Additional delay to ensure purgatory promotion completes on syncing relay |
| 428 | tokio::time::sleep(Duration::from_millis(500)).await; | 433 | tokio::time::sleep(Duration::from_secs(4)).await; |
| 429 | 434 | ||
| 430 | // Now add events - these should be "live" not "startup" | 435 | // Now add Layer 2 patch events (not announcements) - these are accepted immediately |
| 431 | // Include BOTH domains so events are accepted by both relays | 436 | // (Layer 2 events are accepted directly to DB, no purgatory) |
| 432 | let keys = Keys::generate(); | 437 | let repo_coord_str = format!( |
| 433 | let events: Vec<_> = (0..2) | 438 | "{}:{}:{}", |
| 434 | .map(|i| { | 439 | Kind::GitRepoAnnouncement.as_u16(), |
| 435 | create_repo_announcement( | 440 | keys.public_key().to_hex(), |
| 436 | &keys, | 441 | repo_id |
| 437 | &[&harness.source_domain(0), &sync_domain], | 442 | ); |
| 438 | &format!("live-{}", i), | 443 | |
| 439 | ) | 444 | let patch1 = create_event_referencing_repo( |
| 440 | }) | 445 | &keys, |
| 441 | .collect(); | 446 | &repo_coord_str, |
| 442 | harness.submit_events(0, &events).await.unwrap(); | 447 | Kind::GitPatch.as_u16(), |
| 448 | "Live test patch 1", | ||
| 449 | ); | ||
| 450 | let patch2 = create_event_referencing_repo( | ||
| 451 | &keys, | ||
| 452 | &repo_coord_str, | ||
| 453 | Kind::GitPatch.as_u16(), | ||
| 454 | "Live test patch 2", | ||
| 455 | ); | ||
| 456 | |||
| 457 | // Send patches to source AFTER sync connection established (live mode) | ||
| 458 | let client = TestClient::new(source_relay.url(), keys.clone()) | ||
| 459 | .await | ||
| 460 | .expect("Failed to connect to source"); | ||
| 461 | client.send_event(&patch1).await.expect("Failed to send patch 1"); | ||
| 462 | client.send_event(&patch2).await.expect("Failed to send patch 2"); | ||
| 463 | client.disconnect().await; | ||
| 464 | println!("Two patches sent to source relay (live mode)"); | ||
| 443 | 465 | ||
| 444 | // Wait for live events to be processed and metrics updated | 466 | // Wait for live events to be processed and metrics updated |
| 445 | tokio::time::sleep(Duration::from_secs(4)).await; | 467 | tokio::time::sleep(Duration::from_secs(4)).await; |
| 446 | let metrics = harness.get_metrics().await.unwrap(); | 468 | |
| 469 | // Fetch metrics from syncing relay | ||
| 470 | let raw_metrics = fetch_metrics(&sync_url) | ||
| 471 | .await | ||
| 472 | .expect("Failed to fetch metrics"); | ||
| 473 | let metrics = ParsedMetrics::parse(&raw_metrics); | ||
| 447 | 474 | ||
| 448 | let synced_count = metrics.events_synced_total(); | 475 | let synced_count = metrics.events_synced_total(); |
| 449 | println!("Events synced total: {:?}", synced_count); | 476 | println!("Events synced total: {:?}", synced_count); |
| 450 | 477 | ||
| 451 | assert_eq!(synced_count, Some(2), "Should have 2 synced events"); | 478 | // Cleanup |
| 479 | syncing_relay.stop().await; | ||
| 480 | source_relay.stop().await; | ||
| 452 | 481 | ||
| 453 | harness.stop_all().await; | 482 | assert!( |
| 483 | synced_count.is_some() && synced_count.unwrap() >= 2, | ||
| 484 | "Should have synced at least 2 events, got {:?}", | ||
| 485 | synced_count | ||
| 486 | ); | ||
| 454 | } | 487 | } |
| 455 | 488 | ||
| 456 | /// Test that relay connected status is tracked in metrics. | 489 | /// Test that relay connected status is tracked in metrics. |
diff --git a/tests/sync/tag_variations.rs b/tests/sync/tag_variations.rs index 46b1203..021ad0e 100644 --- a/tests/sync/tag_variations.rs +++ b/tests/sync/tag_variations.rs | |||
| @@ -55,30 +55,19 @@ async fn test_layer2_sync_with_lowercase_a_tag() { | |||
| 55 | 55 | ||
| 56 | let keys = Keys::generate(); | 56 | let keys = Keys::generate(); |
| 57 | 57 | ||
| 58 | // 2. Create and send repository announcement to both relays | 58 | // 2. Create and send repository announcement to both relays with git data |
| 59 | // (purgatory requires git data before announcements are accepted) | ||
| 59 | let repo_id = "test-repo-tag-8a"; | 60 | let repo_id = "test-repo-tag-8a"; |
| 60 | let announcement = | 61 | let domains = vec![relay_a.domain(), relay_b.domain()]; |
| 61 | create_repo_announcement(&keys, &[&relay_a.domain(), &relay_b.domain()], repo_id); | 62 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); |
| 62 | 63 | ||
| 63 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | 64 | let (_announcement, _git_dir_a) = |
| 64 | .await | 65 | setup_announcement_on_relay(&relay_a, &keys, &domain_refs, repo_id).await; |
| 65 | .expect("Failed to connect to relay_a"); | 66 | println!("Announcement set up on relay_a with git data"); |
| 66 | |||
| 67 | let client_b = TestClient::new(relay_b.url(), keys.clone()) | ||
| 68 | .await | ||
| 69 | .expect("Failed to connect to relay_b"); | ||
| 70 | 67 | ||
| 71 | client_a | 68 | let (_announcement_b, _git_dir_b) = |
| 72 | .send_event(&announcement) | 69 | setup_announcement_on_relay(&relay_b, &keys, &domain_refs, repo_id).await; |
| 73 | .await | 70 | println!("Announcement set up on relay_b with git data (triggers discovery)"); |
| 74 | .expect("Failed to send announcement to relay_a"); | ||
| 75 | println!("Announcement sent to relay_a"); | ||
| 76 | |||
| 77 | client_b | ||
| 78 | .send_event(&announcement) | ||
| 79 | .await | ||
| 80 | .expect("Failed to send announcement to relay_b"); | ||
| 81 | println!("Announcement sent to relay_b (triggers discovery)"); | ||
| 82 | 71 | ||
| 83 | // 3. Wait for discovery | 72 | // 3. Wait for discovery |
| 84 | tokio::time::sleep(Duration::from_secs(1)).await; | 73 | tokio::time::sleep(Duration::from_secs(1)).await; |
| @@ -95,9 +84,10 @@ async fn test_layer2_sync_with_lowercase_a_tag() { | |||
| 95 | issue_id, | 84 | issue_id, |
| 96 | issue.kind.as_u16() | 85 | issue.kind.as_u16() |
| 97 | ); | 86 | ); |
| 98 | for tag in issue.tags.iter() { | 87 | |
| 99 | println!(" Tag: {:?}", tag.as_slice()); | 88 | let client_a = TestClient::new(relay_a.url(), keys.clone()) |
| 100 | } | 89 | .await |
| 90 | .expect("Failed to connect to relay_a"); | ||
| 101 | 91 | ||
| 102 | client_a | 92 | client_a |
| 103 | .send_event(&issue) | 93 | .send_event(&issue) |
| @@ -106,7 +96,6 @@ async fn test_layer2_sync_with_lowercase_a_tag() { | |||
| 106 | println!("Issue sent to relay_a"); | 96 | println!("Issue sent to relay_a"); |
| 107 | 97 | ||
| 108 | client_a.disconnect().await; | 98 | client_a.disconnect().await; |
| 109 | client_b.disconnect().await; | ||
| 110 | 99 | ||
| 111 | // 5. Wait and verify event syncs to relay_b | 100 | // 5. Wait and verify event syncs to relay_b |
| 112 | let filter = Filter::new() | 101 | let filter = Filter::new() |
| @@ -154,30 +143,18 @@ async fn test_layer2_sync_with_uppercase_a_tag() { | |||
| 154 | 143 | ||
| 155 | let keys = Keys::generate(); | 144 | let keys = Keys::generate(); |
| 156 | 145 | ||
| 157 | // 2. Create and send repository announcement to both relays | 146 | // 2. Create and send repository announcement to both relays with git data |
| 158 | let repo_id = "test-repo-tag-8b"; | 147 | let repo_id = "test-repo-tag-8b"; |
| 159 | let announcement = | 148 | let domains = vec![relay_a.domain(), relay_b.domain()]; |
| 160 | create_repo_announcement(&keys, &[&relay_a.domain(), &relay_b.domain()], repo_id); | 149 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); |
| 161 | |||
| 162 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | ||
| 163 | .await | ||
| 164 | .expect("Failed to connect to relay_a"); | ||
| 165 | 150 | ||
| 166 | let client_b = TestClient::new(relay_b.url(), keys.clone()) | 151 | let (_announcement, _git_dir_a) = |
| 167 | .await | 152 | setup_announcement_on_relay(&relay_a, &keys, &domain_refs, repo_id).await; |
| 168 | .expect("Failed to connect to relay_b"); | 153 | println!("Announcement set up on relay_a with git data"); |
| 169 | 154 | ||
| 170 | client_a | 155 | let (_announcement_b, _git_dir_b) = |
| 171 | .send_event(&announcement) | 156 | setup_announcement_on_relay(&relay_b, &keys, &domain_refs, repo_id).await; |
| 172 | .await | 157 | println!("Announcement set up on relay_b with git data (triggers discovery)"); |
| 173 | .expect("Failed to send announcement to relay_a"); | ||
| 174 | println!("Announcement sent to relay_a"); | ||
| 175 | |||
| 176 | client_b | ||
| 177 | .send_event(&announcement) | ||
| 178 | .await | ||
| 179 | .expect("Failed to send announcement to relay_b"); | ||
| 180 | println!("Announcement sent to relay_b (triggers discovery)"); | ||
| 181 | 158 | ||
| 182 | // 3. Wait for discovery | 159 | // 3. Wait for discovery |
| 183 | tokio::time::sleep(Duration::from_secs(1)).await; | 160 | tokio::time::sleep(Duration::from_secs(1)).await; |
| @@ -197,9 +174,10 @@ async fn test_layer2_sync_with_uppercase_a_tag() { | |||
| 197 | issue_id, | 174 | issue_id, |
| 198 | issue.kind.as_u16() | 175 | issue.kind.as_u16() |
| 199 | ); | 176 | ); |
| 200 | for tag in issue.tags.iter() { | 177 | |
| 201 | println!(" Tag: {:?}", tag.as_slice()); | 178 | let client_a = TestClient::new(relay_a.url(), keys.clone()) |
| 202 | } | 179 | .await |
| 180 | .expect("Failed to connect to relay_a"); | ||
| 203 | 181 | ||
| 204 | client_a | 182 | client_a |
| 205 | .send_event(&issue) | 183 | .send_event(&issue) |
| @@ -208,7 +186,6 @@ async fn test_layer2_sync_with_uppercase_a_tag() { | |||
| 208 | println!("Issue sent to relay_a"); | 186 | println!("Issue sent to relay_a"); |
| 209 | 187 | ||
| 210 | client_a.disconnect().await; | 188 | client_a.disconnect().await; |
| 211 | client_b.disconnect().await; | ||
| 212 | 189 | ||
| 213 | // 5. Wait and verify event syncs to relay_b | 190 | // 5. Wait and verify event syncs to relay_b |
| 214 | let filter = Filter::new() | 191 | let filter = Filter::new() |
| @@ -255,30 +232,18 @@ async fn test_layer2_sync_with_q_tag() { | |||
| 255 | 232 | ||
| 256 | let keys = Keys::generate(); | 233 | let keys = Keys::generate(); |
| 257 | 234 | ||
| 258 | // 2. Create and send repository announcement to both relays | 235 | // 2. Create and send repository announcement to both relays with git data |
| 259 | let repo_id = "test-repo-tag-8c"; | 236 | let repo_id = "test-repo-tag-8c"; |
| 260 | let announcement = | 237 | let domains = vec![relay_a.domain(), relay_b.domain()]; |
| 261 | create_repo_announcement(&keys, &[&relay_a.domain(), &relay_b.domain()], repo_id); | 238 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); |
| 262 | 239 | ||
| 263 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | 240 | let (_announcement, _git_dir_a) = |
| 264 | .await | 241 | setup_announcement_on_relay(&relay_a, &keys, &domain_refs, repo_id).await; |
| 265 | .expect("Failed to connect to relay_a"); | 242 | println!("Announcement set up on relay_a with git data"); |
| 266 | 243 | ||
| 267 | let client_b = TestClient::new(relay_b.url(), keys.clone()) | 244 | let (_announcement_b, _git_dir_b) = |
| 268 | .await | 245 | setup_announcement_on_relay(&relay_b, &keys, &domain_refs, repo_id).await; |
| 269 | .expect("Failed to connect to relay_b"); | 246 | println!("Announcement set up on relay_b with git data (triggers discovery)"); |
| 270 | |||
| 271 | client_a | ||
| 272 | .send_event(&announcement) | ||
| 273 | .await | ||
| 274 | .expect("Failed to send announcement to relay_a"); | ||
| 275 | println!("Announcement sent to relay_a"); | ||
| 276 | |||
| 277 | client_b | ||
| 278 | .send_event(&announcement) | ||
| 279 | .await | ||
| 280 | .expect("Failed to send announcement to relay_b"); | ||
| 281 | println!("Announcement sent to relay_b (triggers discovery)"); | ||
| 282 | 247 | ||
| 283 | // 3. Wait for discovery | 248 | // 3. Wait for discovery |
| 284 | tokio::time::sleep(Duration::from_secs(1)).await; | 249 | tokio::time::sleep(Duration::from_secs(1)).await; |
| @@ -294,9 +259,10 @@ async fn test_layer2_sync_with_q_tag() { | |||
| 294 | issue_id, | 259 | issue_id, |
| 295 | issue.kind.as_u16() | 260 | issue.kind.as_u16() |
| 296 | ); | 261 | ); |
| 297 | for tag in issue.tags.iter() { | 262 | |
| 298 | println!(" Tag: {:?}", tag.as_slice()); | 263 | let client_a = TestClient::new(relay_a.url(), keys.clone()) |
| 299 | } | 264 | .await |
| 265 | .expect("Failed to connect to relay_a"); | ||
| 300 | 266 | ||
| 301 | client_a | 267 | client_a |
| 302 | .send_event(&issue) | 268 | .send_event(&issue) |
| @@ -305,7 +271,6 @@ async fn test_layer2_sync_with_q_tag() { | |||
| 305 | println!("Issue sent to relay_a"); | 271 | println!("Issue sent to relay_a"); |
| 306 | 272 | ||
| 307 | client_a.disconnect().await; | 273 | client_a.disconnect().await; |
| 308 | client_b.disconnect().await; | ||
| 309 | 274 | ||
| 310 | // 5. Wait and verify event syncs to relay_b | 275 | // 5. Wait and verify event syncs to relay_b |
| 311 | let filter = Filter::new() | 276 | let filter = Filter::new() |
| @@ -362,30 +327,18 @@ async fn test_layer3_sync_with_lowercase_e_tag() { | |||
| 362 | 327 | ||
| 363 | let keys = Keys::generate(); | 328 | let keys = Keys::generate(); |
| 364 | 329 | ||
| 365 | // 2. Create and send repository announcement to both relays | 330 | // 2. Create and send repository announcement to both relays with git data |
| 366 | let repo_id = "test-repo-tag-9a"; | 331 | let repo_id = "test-repo-tag-9a"; |
| 367 | let announcement = | 332 | let domains = vec![relay_a.domain(), relay_b.domain()]; |
| 368 | create_repo_announcement(&keys, &[&relay_a.domain(), &relay_b.domain()], repo_id); | 333 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); |
| 369 | 334 | ||
| 370 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | 335 | let (_announcement, _git_dir_a) = |
| 371 | .await | 336 | setup_announcement_on_relay(&relay_a, &keys, &domain_refs, repo_id).await; |
| 372 | .expect("Failed to connect to relay_a"); | 337 | println!("Announcement set up on relay_a with git data"); |
| 373 | |||
| 374 | let client_b = TestClient::new(relay_b.url(), keys.clone()) | ||
| 375 | .await | ||
| 376 | .expect("Failed to connect to relay_b"); | ||
| 377 | 338 | ||
| 378 | client_a | 339 | let (_announcement_b, _git_dir_b) = |
| 379 | .send_event(&announcement) | 340 | setup_announcement_on_relay(&relay_b, &keys, &domain_refs, repo_id).await; |
| 380 | .await | 341 | println!("Announcement set up on relay_b with git data (triggers discovery)"); |
| 381 | .expect("Failed to send announcement to relay_a"); | ||
| 382 | println!("Announcement sent to relay_a"); | ||
| 383 | |||
| 384 | client_b | ||
| 385 | .send_event(&announcement) | ||
| 386 | .await | ||
| 387 | .expect("Failed to send announcement to relay_b"); | ||
| 388 | println!("Announcement sent to relay_b (triggers discovery)"); | ||
| 389 | 342 | ||
| 390 | // 3. Wait for discovery | 343 | // 3. Wait for discovery |
| 391 | tokio::time::sleep(Duration::from_secs(1)).await; | 344 | tokio::time::sleep(Duration::from_secs(1)).await; |
| @@ -396,6 +349,10 @@ async fn test_layer3_sync_with_lowercase_e_tag() { | |||
| 396 | .expect("Failed to create issue"); | 349 | .expect("Failed to create issue"); |
| 397 | let issue_id = issue.id; | 350 | let issue_id = issue.id; |
| 398 | 351 | ||
| 352 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | ||
| 353 | .await | ||
| 354 | .expect("Failed to connect to relay_a"); | ||
| 355 | |||
| 399 | client_a | 356 | client_a |
| 400 | .send_event(&issue) | 357 | .send_event(&issue) |
| 401 | .await | 358 | .await |
| @@ -410,11 +367,6 @@ async fn test_layer3_sync_with_lowercase_e_tag() { | |||
| 410 | assert!(issue_synced, "Layer 2 issue should sync first"); | 367 | assert!(issue_synced, "Layer 2 issue should sync first"); |
| 411 | 368 | ||
| 412 | // Wait for Layer 3 subscriptions to be established | 369 | // Wait for Layer 3 subscriptions to be established |
| 413 | // After issue syncs, relay_b's SelfSubscriber needs time to: | ||
| 414 | // 1. Receive the synced issue via notify_event broadcast | ||
| 415 | // 2. Batch timer to tick (up to 200ms in tests) | ||
| 416 | // 3. Process batch and create Layer 3 filters | ||
| 417 | // 4. Subscribe to relay_a with Layer 3 filters | ||
| 418 | tokio::time::sleep(Duration::from_millis(500)).await; | 370 | tokio::time::sleep(Duration::from_millis(500)).await; |
| 419 | 371 | ||
| 420 | // 6. Create and send Layer 3 reply with lowercase 'e' tag (kind 1) | 372 | // 6. Create and send Layer 3 reply with lowercase 'e' tag (kind 1) |
| @@ -427,9 +379,6 @@ async fn test_layer3_sync_with_lowercase_e_tag() { | |||
| 427 | reply_id, | 379 | reply_id, |
| 428 | reply.kind.as_u16() | 380 | reply.kind.as_u16() |
| 429 | ); | 381 | ); |
| 430 | for tag in reply.tags.iter() { | ||
| 431 | println!(" Tag: {:?}", tag.as_slice()); | ||
| 432 | } | ||
| 433 | 382 | ||
| 434 | client_a | 383 | client_a |
| 435 | .send_event(&reply) | 384 | .send_event(&reply) |
| @@ -438,7 +387,6 @@ async fn test_layer3_sync_with_lowercase_e_tag() { | |||
| 438 | println!("Layer 3 reply {} sent to relay_a", reply_id); | 387 | println!("Layer 3 reply {} sent to relay_a", reply_id); |
| 439 | 388 | ||
| 440 | client_a.disconnect().await; | 389 | client_a.disconnect().await; |
| 441 | client_b.disconnect().await; | ||
| 442 | 390 | ||
| 443 | // 7. Wait and verify reply syncs to relay_b | 391 | // 7. Wait and verify reply syncs to relay_b |
| 444 | let reply_filter = Filter::new() | 392 | let reply_filter = Filter::new() |
| @@ -486,30 +434,18 @@ async fn test_layer3_sync_with_uppercase_e_tag() { | |||
| 486 | 434 | ||
| 487 | let keys = Keys::generate(); | 435 | let keys = Keys::generate(); |
| 488 | 436 | ||
| 489 | // 2. Create and send repository announcement to both relays | 437 | // 2. Create and send repository announcement to both relays with git data |
| 490 | let repo_id = "test-repo-tag-9b"; | 438 | let repo_id = "test-repo-tag-9b"; |
| 491 | let announcement = | 439 | let domains = vec![relay_a.domain(), relay_b.domain()]; |
| 492 | create_repo_announcement(&keys, &[&relay_a.domain(), &relay_b.domain()], repo_id); | 440 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); |
| 493 | |||
| 494 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | ||
| 495 | .await | ||
| 496 | .expect("Failed to connect to relay_a"); | ||
| 497 | 441 | ||
| 498 | let client_b = TestClient::new(relay_b.url(), keys.clone()) | 442 | let (_announcement, _git_dir_a) = |
| 499 | .await | 443 | setup_announcement_on_relay(&relay_a, &keys, &domain_refs, repo_id).await; |
| 500 | .expect("Failed to connect to relay_b"); | 444 | println!("Announcement set up on relay_a with git data"); |
| 501 | 445 | ||
| 502 | client_a | 446 | let (_announcement_b, _git_dir_b) = |
| 503 | .send_event(&announcement) | 447 | setup_announcement_on_relay(&relay_b, &keys, &domain_refs, repo_id).await; |
| 504 | .await | 448 | println!("Announcement set up on relay_b with git data (triggers discovery)"); |
| 505 | .expect("Failed to send announcement to relay_a"); | ||
| 506 | println!("Announcement sent to relay_a"); | ||
| 507 | |||
| 508 | client_b | ||
| 509 | .send_event(&announcement) | ||
| 510 | .await | ||
| 511 | .expect("Failed to send announcement to relay_b"); | ||
| 512 | println!("Announcement sent to relay_b (triggers discovery)"); | ||
| 513 | 449 | ||
| 514 | // 3. Wait for discovery | 450 | // 3. Wait for discovery |
| 515 | tokio::time::sleep(Duration::from_secs(1)).await; | 451 | tokio::time::sleep(Duration::from_secs(1)).await; |
| @@ -520,6 +456,10 @@ async fn test_layer3_sync_with_uppercase_e_tag() { | |||
| 520 | .expect("Failed to create issue"); | 456 | .expect("Failed to create issue"); |
| 521 | let issue_id = issue.id; | 457 | let issue_id = issue.id; |
| 522 | 458 | ||
| 459 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | ||
| 460 | .await | ||
| 461 | .expect("Failed to connect to relay_a"); | ||
| 462 | |||
| 523 | client_a | 463 | client_a |
| 524 | .send_event(&issue) | 464 | .send_event(&issue) |
| 525 | .await | 465 | .await |
| @@ -534,11 +474,6 @@ async fn test_layer3_sync_with_uppercase_e_tag() { | |||
| 534 | assert!(issue_synced, "Layer 2 issue should sync first"); | 474 | assert!(issue_synced, "Layer 2 issue should sync first"); |
| 535 | 475 | ||
| 536 | // Wait for Layer 3 subscriptions to be established | 476 | // Wait for Layer 3 subscriptions to be established |
| 537 | // After issue syncs, relay_b's SelfSubscriber needs time to: | ||
| 538 | // 1. Receive the synced issue via notify_event broadcast | ||
| 539 | // 2. Batch timer to tick (up to 200ms in tests) | ||
| 540 | // 3. Process batch and create Layer 3 filters | ||
| 541 | // 4. Subscribe to relay_a with Layer 3 filters | ||
| 542 | tokio::time::sleep(Duration::from_millis(500)).await; | 477 | tokio::time::sleep(Duration::from_millis(500)).await; |
| 543 | 478 | ||
| 544 | // 6. Create and send Layer 3 comment with uppercase 'E' tag (kind 1111) | 479 | // 6. Create and send Layer 3 comment with uppercase 'E' tag (kind 1111) |
| @@ -552,9 +487,6 @@ async fn test_layer3_sync_with_uppercase_e_tag() { | |||
| 552 | comment_id, | 487 | comment_id, |
| 553 | comment.kind.as_u16() | 488 | comment.kind.as_u16() |
| 554 | ); | 489 | ); |
| 555 | for tag in comment.tags.iter() { | ||
| 556 | println!(" Tag: {:?}", tag.as_slice()); | ||
| 557 | } | ||
| 558 | 490 | ||
| 559 | client_a | 491 | client_a |
| 560 | .send_event(&comment) | 492 | .send_event(&comment) |
| @@ -563,7 +495,6 @@ async fn test_layer3_sync_with_uppercase_e_tag() { | |||
| 563 | println!("Layer 3 comment {} sent to relay_a", comment_id); | 495 | println!("Layer 3 comment {} sent to relay_a", comment_id); |
| 564 | 496 | ||
| 565 | client_a.disconnect().await; | 497 | client_a.disconnect().await; |
| 566 | client_b.disconnect().await; | ||
| 567 | 498 | ||
| 568 | // 7. Wait and verify comment syncs to relay_b | 499 | // 7. Wait and verify comment syncs to relay_b |
| 569 | let comment_filter = Filter::new() | 500 | let comment_filter = Filter::new() |
| @@ -614,30 +545,18 @@ async fn test_layer3_sync_with_q_tag() { | |||
| 614 | 545 | ||
| 615 | let keys = Keys::generate(); | 546 | let keys = Keys::generate(); |
| 616 | 547 | ||
| 617 | // 2. Create and send repository announcement to both relays | 548 | // 2. Create and send repository announcement to both relays with git data |
| 618 | let repo_id = "test-repo-tag-9c"; | 549 | let repo_id = "test-repo-tag-9c"; |
| 619 | let announcement = | 550 | let domains = vec![relay_a.domain(), relay_b.domain()]; |
| 620 | create_repo_announcement(&keys, &[&relay_a.domain(), &relay_b.domain()], repo_id); | 551 | let domain_refs: Vec<&str> = domains.iter().map(|s| s.as_str()).collect(); |
| 621 | 552 | ||
| 622 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | 553 | let (_announcement, _git_dir_a) = |
| 623 | .await | 554 | setup_announcement_on_relay(&relay_a, &keys, &domain_refs, repo_id).await; |
| 624 | .expect("Failed to connect to relay_a"); | 555 | println!("Announcement set up on relay_a with git data"); |
| 625 | 556 | ||
| 626 | let client_b = TestClient::new(relay_b.url(), keys.clone()) | 557 | let (_announcement_b, _git_dir_b) = |
| 627 | .await | 558 | setup_announcement_on_relay(&relay_b, &keys, &domain_refs, repo_id).await; |
| 628 | .expect("Failed to connect to relay_b"); | 559 | println!("Announcement set up on relay_b with git data (triggers discovery)"); |
| 629 | |||
| 630 | client_a | ||
| 631 | .send_event(&announcement) | ||
| 632 | .await | ||
| 633 | .expect("Failed to send announcement to relay_a"); | ||
| 634 | println!("Announcement sent to relay_a"); | ||
| 635 | |||
| 636 | client_b | ||
| 637 | .send_event(&announcement) | ||
| 638 | .await | ||
| 639 | .expect("Failed to send announcement to relay_b"); | ||
| 640 | println!("Announcement sent to relay_b (triggers discovery)"); | ||
| 641 | 560 | ||
| 642 | // 3. Wait for discovery | 561 | // 3. Wait for discovery |
| 643 | tokio::time::sleep(Duration::from_secs(1)).await; | 562 | tokio::time::sleep(Duration::from_secs(1)).await; |
| @@ -648,6 +567,10 @@ async fn test_layer3_sync_with_q_tag() { | |||
| 648 | .expect("Failed to create issue"); | 567 | .expect("Failed to create issue"); |
| 649 | let issue_id = issue.id; | 568 | let issue_id = issue.id; |
| 650 | 569 | ||
| 570 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | ||
| 571 | .await | ||
| 572 | .expect("Failed to connect to relay_a"); | ||
| 573 | |||
| 651 | client_a | 574 | client_a |
| 652 | .send_event(&issue) | 575 | .send_event(&issue) |
| 653 | .await | 576 | .await |
| @@ -662,11 +585,6 @@ async fn test_layer3_sync_with_q_tag() { | |||
| 662 | assert!(issue_synced, "Layer 2 issue should sync first"); | 585 | assert!(issue_synced, "Layer 2 issue should sync first"); |
| 663 | 586 | ||
| 664 | // Wait for Layer 3 subscriptions to be established | 587 | // Wait for Layer 3 subscriptions to be established |
| 665 | // After issue syncs, relay_b's SelfSubscriber needs time to: | ||
| 666 | // 1. Receive the synced issue via notify_event broadcast | ||
| 667 | // 2. Batch timer to tick (up to 200ms in tests) | ||
| 668 | // 3. Process batch and create Layer 3 filters | ||
| 669 | // 4. Subscribe to relay_a with Layer 3 filters | ||
| 670 | tokio::time::sleep(Duration::from_millis(500)).await; | 588 | tokio::time::sleep(Duration::from_millis(500)).await; |
| 671 | 589 | ||
| 672 | // 6. Create and send Layer 3 quote with 'q' tag (kind 1) | 590 | // 6. Create and send Layer 3 quote with 'q' tag (kind 1) |
| @@ -679,9 +597,6 @@ async fn test_layer3_sync_with_q_tag() { | |||
| 679 | quote_id, | 597 | quote_id, |
| 680 | quote.kind.as_u16() | 598 | quote.kind.as_u16() |
| 681 | ); | 599 | ); |
| 682 | for tag in quote.tags.iter() { | ||
| 683 | println!(" Tag: {:?}", tag.as_slice()); | ||
| 684 | } | ||
| 685 | 600 | ||
| 686 | client_a | 601 | client_a |
| 687 | .send_event("e) | 602 | .send_event("e) |
| @@ -690,7 +605,6 @@ async fn test_layer3_sync_with_q_tag() { | |||
| 690 | println!("Layer 3 quote {} sent to relay_a", quote_id); | 605 | println!("Layer 3 quote {} sent to relay_a", quote_id); |
| 691 | 606 | ||
| 692 | client_a.disconnect().await; | 607 | client_a.disconnect().await; |
| 693 | client_b.disconnect().await; | ||
| 694 | 608 | ||
| 695 | // 7. Wait and verify quote syncs to relay_b | 609 | // 7. Wait and verify quote syncs to relay_b |
| 696 | let quote_filter = Filter::new() | 610 | let quote_filter = Filter::new() |