diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2024-02-21 15:01:11 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2024-02-21 15:01:11 +0000 |
| commit | ea5aa6993d4c906c1703563ddc304c324c4ae079 (patch) | |
| tree | 061ef2f644b099661c0b0b5c74a88ab8f6d549c6 | |
| parent | 125ead4bb64d9e4a76266aabe5e826fc23551edc (diff) | |
feat(send): in-reply-to arg for revised proposal
send a revised version of a proposal using the new in-replyto argument
suppliments existing 'root' tag with 'root-revision'
e 'reply' tag to the original proposal
| -rw-r--r-- | src/git.rs | 2 | ||||
| -rw-r--r-- | src/sub_commands/push.rs | 1 | ||||
| -rw-r--r-- | src/sub_commands/send.rs | 96 | ||||
| -rw-r--r-- | tests/send.rs | 521 |
4 files changed, 478 insertions, 142 deletions
| @@ -1313,6 +1313,7 @@ mod tests { | |||
| 1313 | None, | 1313 | None, |
| 1314 | None, | 1314 | None, |
| 1315 | None, | 1315 | None, |
| 1316 | &None, | ||
| 1316 | ) | 1317 | ) |
| 1317 | } | 1318 | } |
| 1318 | fn test_patch_applies_to_repository(patch_event: nostr::Event) -> Result<()> { | 1319 | fn test_patch_applies_to_repository(patch_event: nostr::Event) -> Result<()> { |
| @@ -1473,6 +1474,7 @@ mod tests { | |||
| 1473 | &vec![oid_to_sha1(&oid1), oid_to_sha1(&oid2), oid_to_sha1(&oid3)], | 1474 | &vec![oid_to_sha1(&oid1), oid_to_sha1(&oid2), oid_to_sha1(&oid3)], |
| 1474 | &TEST_KEY_1_KEYS, | 1475 | &TEST_KEY_1_KEYS, |
| 1475 | &RepoRef::try_from(generate_repo_ref_event()).unwrap(), | 1476 | &RepoRef::try_from(generate_repo_ref_event()).unwrap(), |
| 1477 | &None, | ||
| 1476 | )?; | 1478 | )?; |
| 1477 | 1479 | ||
| 1478 | events.reverse(); | 1480 | events.reverse(); |
diff --git a/src/sub_commands/push.rs b/src/sub_commands/push.rs index 2500e9f..73bdb38 100644 --- a/src/sub_commands/push.rs +++ b/src/sub_commands/push.rs | |||
| @@ -119,6 +119,7 @@ pub async fn launch(cli_args: &Cli) -> Result<()> { | |||
| 119 | patch_events.last().map(nostr::Event::id), | 119 | patch_events.last().map(nostr::Event::id), |
| 120 | None, | 120 | None, |
| 121 | None, | 121 | None, |
| 122 | &None, | ||
| 122 | ) | 123 | ) |
| 123 | .context("cannot make patch event from commit")?, | 124 | .context("cannot make patch event from commit")?, |
| 124 | ); | 125 | ); |
diff --git a/src/sub_commands/send.rs b/src/sub_commands/send.rs index 105f87a..c9c81ee 100644 --- a/src/sub_commands/send.rs +++ b/src/sub_commands/send.rs | |||
| @@ -1,9 +1,12 @@ | |||
| 1 | use std::time::Duration; | 1 | use std::{str::FromStr, time::Duration}; |
| 2 | 2 | ||
| 3 | use anyhow::{bail, Context, Result}; | 3 | use anyhow::{bail, Context, Result}; |
| 4 | use futures::future::join_all; | 4 | use futures::future::join_all; |
| 5 | use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; | 5 | use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; |
| 6 | use nostr::{prelude::sha1::Hash as Sha1Hash, EventBuilder, Marker, Tag, TagKind}; | 6 | use nostr::{ |
| 7 | nips::nip19::Nip19, prelude::sha1::Hash as Sha1Hash, EventBuilder, FromBech32, Marker, Tag, | ||
| 8 | TagKind, UncheckedUrl, | ||
| 9 | }; | ||
| 7 | 10 | ||
| 8 | use super::list::tag_value; | 11 | use super::list::tag_value; |
| 9 | #[cfg(not(test))] | 12 | #[cfg(not(test))] |
| @@ -25,6 +28,10 @@ pub struct SubCommandArgs { | |||
| 25 | /// starting commit (commits since in current branch) or commit range, like | 28 | /// starting commit (commits since in current branch) or commit range, like |
| 26 | /// in `git format-patch` | 29 | /// in `git format-patch` |
| 27 | starting_commit: String, | 30 | starting_commit: String, |
| 31 | #[clap(long)] | ||
| 32 | /// nevent or event id of an existing proposal for which this is a new | ||
| 33 | /// version | ||
| 34 | in_reply_to: Option<String>, | ||
| 28 | /// optional cover letter title | 35 | /// optional cover letter title |
| 29 | #[clap(short, long)] | 36 | #[clap(short, long)] |
| 30 | title: Option<String>, | 37 | title: Option<String>, |
| @@ -161,6 +168,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | |||
| 161 | &commits, | 168 | &commits, |
| 162 | &keys, | 169 | &keys, |
| 163 | &repo_ref, | 170 | &repo_ref, |
| 171 | &args.in_reply_to, | ||
| 164 | )?; | 172 | )?; |
| 165 | 173 | ||
| 166 | println!( | 174 | println!( |
| @@ -390,6 +398,7 @@ pub fn generate_cover_letter_and_patch_events( | |||
| 390 | commits: &Vec<Sha1Hash>, | 398 | commits: &Vec<Sha1Hash>, |
| 391 | keys: &nostr::Keys, | 399 | keys: &nostr::Keys, |
| 392 | repo_ref: &RepoRef, | 400 | repo_ref: &RepoRef, |
| 401 | in_reply_to: &Option<String>, | ||
| 393 | ) -> Result<Vec<nostr::Event>> { | 402 | ) -> Result<Vec<nostr::Event>> { |
| 394 | let root_commit = git_repo | 403 | let root_commit = git_repo |
| 395 | .get_root_commit() | 404 | .get_root_commit() |
| @@ -418,8 +427,19 @@ pub fn generate_cover_letter_and_patch_events( | |||
| 418 | }, | 427 | }, |
| 419 | Tag::Reference(format!("{root_commit}")), | 428 | Tag::Reference(format!("{root_commit}")), |
| 420 | Tag::Hashtag("cover-letter".to_string()), | 429 | Tag::Hashtag("cover-letter".to_string()), |
| 421 | Tag::Hashtag("root".to_string()), | ||
| 422 | ], | 430 | ], |
| 431 | if let Some(event_ref) = in_reply_to.clone() { | ||
| 432 | vec![ | ||
| 433 | Tag::Hashtag("root".to_string()), | ||
| 434 | Tag::Hashtag("revision-root".to_string()), | ||
| 435 | // TODO check if id is for a root proposal (perhaps its for an issue?) | ||
| 436 | e_tag_from_nip19(&event_ref,"proposal",nostr::Marker::Reply)?, | ||
| 437 | ] | ||
| 438 | } else { | ||
| 439 | vec![ | ||
| 440 | Tag::Hashtag("root".to_string()), | ||
| 441 | ] | ||
| 442 | }, | ||
| 423 | // this is not strictly needed but makes for prettier branch names | 443 | // this is not strictly needed but makes for prettier branch names |
| 424 | // eventually a prefix will be needed of the event id to stop 2 proposals with the same name colliding | 444 | // eventually a prefix will be needed of the event id to stop 2 proposals with the same name colliding |
| 425 | // a change like this, or the removal of this tag will require the actual branch name to be tracked | 445 | // a change like this, or the removal of this tag will require the actual branch name to be tracked |
| @@ -466,6 +486,7 @@ pub fn generate_cover_letter_and_patch_events( | |||
| 466 | } else { | 486 | } else { |
| 467 | None | 487 | None |
| 468 | }, | 488 | }, |
| 489 | in_reply_to, | ||
| 469 | ) | 490 | ) |
| 470 | .context("failed to generate patch event")?, | 491 | .context("failed to generate patch event")?, |
| 471 | ); | 492 | ); |
| @@ -473,6 +494,51 @@ pub fn generate_cover_letter_and_patch_events( | |||
| 473 | Ok(events) | 494 | Ok(events) |
| 474 | } | 495 | } |
| 475 | 496 | ||
| 497 | fn e_tag_from_nip19( | ||
| 498 | reference: &str, | ||
| 499 | reference_name: &str, | ||
| 500 | marker: nostr::Marker, | ||
| 501 | ) -> Result<nostr::Tag> { | ||
| 502 | let mut bech32 = reference.to_string(); | ||
| 503 | loop { | ||
| 504 | if bech32.is_empty() { | ||
| 505 | bech32 = Interactor::default().input( | ||
| 506 | PromptInputParms::default().with_prompt(&format!("{reference_name} nevent")), | ||
| 507 | )?; | ||
| 508 | } | ||
| 509 | |||
| 510 | if let Ok(nip19) = Nip19::from_bech32(bech32.clone()) { | ||
| 511 | match nip19 { | ||
| 512 | Nip19::Event(n) => { | ||
| 513 | break Ok(nostr::Tag::Event { | ||
| 514 | event_id: n.event_id, | ||
| 515 | relay_url: n.relays.first().map(UncheckedUrl::new), | ||
| 516 | marker: Some(marker), | ||
| 517 | }); | ||
| 518 | } | ||
| 519 | Nip19::EventId(id) => { | ||
| 520 | break Ok(nostr::Tag::Event { | ||
| 521 | event_id: id, | ||
| 522 | relay_url: None, | ||
| 523 | marker: Some(marker), | ||
| 524 | }); | ||
| 525 | } | ||
| 526 | _ => {} | ||
| 527 | } | ||
| 528 | } | ||
| 529 | if let Ok(id) = nostr::EventId::from_str(&bech32) { | ||
| 530 | break Ok(nostr::Tag::Event { | ||
| 531 | event_id: id, | ||
| 532 | relay_url: None, | ||
| 533 | marker: Some(marker), | ||
| 534 | }); | ||
| 535 | } | ||
| 536 | println!("not a valid {reference_name} event reference"); | ||
| 537 | |||
| 538 | bech32 = String::new(); | ||
| 539 | } | ||
| 540 | } | ||
| 541 | |||
| 476 | pub struct CoverLetter { | 542 | pub struct CoverLetter { |
| 477 | pub title: String, | 543 | pub title: String, |
| 478 | pub description: String, | 544 | pub description: String, |
| @@ -565,6 +631,7 @@ pub fn generate_patch_event( | |||
| 565 | parent_patch_event_id: Option<nostr::EventId>, | 631 | parent_patch_event_id: Option<nostr::EventId>, |
| 566 | series_count: Option<(u64, u64)>, | 632 | series_count: Option<(u64, u64)>, |
| 567 | branch_name: Option<String>, | 633 | branch_name: Option<String>, |
| 634 | in_reply_to: &Option<String>, | ||
| 568 | ) -> Result<nostr::Event> { | 635 | ) -> Result<nostr::Event> { |
| 569 | let commit_parent = git_repo | 636 | let commit_parent = git_repo |
| 570 | .get_commit_parent(commit) | 637 | .get_commit_parent(commit) |
| @@ -592,16 +659,27 @@ pub fn generate_patch_event( | |||
| 592 | // code that makes it into the main branch, assuming | 659 | // code that makes it into the main branch, assuming |
| 593 | // the commit id is correct | 660 | // the commit id is correct |
| 594 | Tag::Reference(commit.to_string()), | 661 | Tag::Reference(commit.to_string()), |
| 662 | ], | ||
| 595 | 663 | ||
| 596 | if let Some(thread_event_id) = thread_event_id { Tag::Event { | 664 | if let Some(thread_event_id) = thread_event_id { |
| 665 | vec![Tag::Event { | ||
| 597 | event_id: thread_event_id, | 666 | event_id: thread_event_id, |
| 598 | relay_url: relay_hint.clone(), | 667 | relay_url: relay_hint.clone(), |
| 599 | marker: Some(Marker::Root), | 668 | marker: Some(Marker::Root), |
| 600 | } } | 669 | }] |
| 601 | else { | 670 | } else if let Some(event_ref) = in_reply_to.clone() { |
| 602 | Tag::Hashtag("root".to_string()) | 671 | vec![ |
| 603 | }, | 672 | Tag::Hashtag("root".to_string()), |
| 604 | ], | 673 | Tag::Hashtag("revision-root".to_string()), |
| 674 | // TODO check if id is for a root proposal (perhaps its for an issue?) | ||
| 675 | e_tag_from_nip19(&event_ref,"proposal",nostr::Marker::Reply)?, | ||
| 676 | ] | ||
| 677 | } else { | ||
| 678 | vec![ | ||
| 679 | Tag::Hashtag("root".to_string()), | ||
| 680 | ] | ||
| 681 | }, | ||
| 682 | |||
| 605 | if let Some(id) = parent_patch_event_id { | 683 | if let Some(id) = parent_patch_event_id { |
| 606 | vec![Tag::Event { | 684 | vec![Tag::Event { |
| 607 | event_id: id, | 685 | event_id: id, |
diff --git a/tests/send.rs b/tests/send.rs index d8186bd..b5c803d 100644 --- a/tests/send.rs +++ b/tests/send.rs | |||
| @@ -1130,38 +1130,99 @@ mod sends_2_patches_without_cover_letter { | |||
| 1130 | } | 1130 | } |
| 1131 | Ok(()) | 1131 | Ok(()) |
| 1132 | } | 1132 | } |
| 1133 | mod specify_starting_commits { | 1133 | } |
| 1134 | use super::*; | 1134 | mod specify_starting_commits { |
| 1135 | fn cli_tester_create_proposal(git_repo: &GitTestRepo) -> CliTester { | 1135 | use super::*; |
| 1136 | let args = vec![ | 1136 | fn cli_tester_create_proposal(git_repo: &GitTestRepo) -> CliTester { |
| 1137 | "--nsec", | 1137 | let args = vec![ |
| 1138 | TEST_KEY_1_NSEC, | 1138 | "--nsec", |
| 1139 | "--password", | 1139 | TEST_KEY_1_NSEC, |
| 1140 | TEST_PASSWORD, | 1140 | "--password", |
| 1141 | "--disable-cli-spinners", | 1141 | TEST_PASSWORD, |
| 1142 | "send", | 1142 | "--disable-cli-spinners", |
| 1143 | "HEAD~3", | 1143 | "send", |
| 1144 | "--no-cover-letter", | 1144 | "HEAD~3", |
| 1145 | ]; | 1145 | "--no-cover-letter", |
| 1146 | CliTester::new_from_dir(&git_repo.dir, args) | 1146 | ]; |
| 1147 | } | 1147 | CliTester::new_from_dir(&git_repo.dir, args) |
| 1148 | fn expect_msgs_first(p: &mut CliTester) -> Result<()> { | 1148 | } |
| 1149 | p.expect("creating patch for 3 commits\r\n")?; | 1149 | fn expect_msgs_first(p: &mut CliTester) -> Result<()> { |
| 1150 | p.expect("searching for profile and relay updates...\r\n")?; | 1150 | p.expect("creating patch for 3 commits\r\n")?; |
| 1151 | p.expect("\r")?; | 1151 | p.expect("searching for profile and relay updates...\r\n")?; |
| 1152 | p.expect("logged in as fred\r\n")?; | 1152 | p.expect("\r")?; |
| 1153 | p.expect("posting 3 patches without a covering letter...\r\n")?; | 1153 | p.expect("logged in as fred\r\n")?; |
| 1154 | p.expect("posting 3 patches without a covering letter...\r\n")?; | ||
| 1155 | Ok(()) | ||
| 1156 | } | ||
| 1157 | async fn prep_run_create_proposal() -> Result<( | ||
| 1158 | Relay<'static>, | ||
| 1159 | Relay<'static>, | ||
| 1160 | Relay<'static>, | ||
| 1161 | Relay<'static>, | ||
| 1162 | Relay<'static>, | ||
| 1163 | )> { | ||
| 1164 | let git_repo = prep_git_repo()?; | ||
| 1165 | // fallback (51,52) user write (53, 55) repo (55, 56) | ||
| 1166 | let (mut r51, mut r52, mut r53, mut r55, mut r56) = ( | ||
| 1167 | Relay::new( | ||
| 1168 | 8051, | ||
| 1169 | None, | ||
| 1170 | Some(&|relay, client_id, subscription_id, _| -> Result<()> { | ||
| 1171 | relay.respond_events( | ||
| 1172 | client_id, | ||
| 1173 | &subscription_id, | ||
| 1174 | &vec![ | ||
| 1175 | generate_test_key_1_metadata_event("fred"), | ||
| 1176 | generate_test_key_1_relay_list_event(), | ||
| 1177 | ], | ||
| 1178 | )?; | ||
| 1179 | Ok(()) | ||
| 1180 | }), | ||
| 1181 | ), | ||
| 1182 | Relay::new(8052, None, None), | ||
| 1183 | Relay::new(8053, None, None), | ||
| 1184 | Relay::new( | ||
| 1185 | 8055, | ||
| 1186 | None, | ||
| 1187 | Some(&|relay, client_id, subscription_id, _| -> Result<()> { | ||
| 1188 | relay.respond_events( | ||
| 1189 | client_id, | ||
| 1190 | &subscription_id, | ||
| 1191 | &vec![generate_repo_ref_event()], | ||
| 1192 | )?; | ||
| 1193 | Ok(()) | ||
| 1194 | }), | ||
| 1195 | ), | ||
| 1196 | Relay::new(8056, None, None), | ||
| 1197 | ); | ||
| 1198 | |||
| 1199 | // // check relay had the right number of events | ||
| 1200 | let cli_tester_handle = std::thread::spawn(move || -> Result<()> { | ||
| 1201 | let mut p = cli_tester_create_proposal(&git_repo); | ||
| 1202 | p.expect_end_eventually()?; | ||
| 1203 | for p in [51, 52, 53, 55, 56] { | ||
| 1204 | relay::shutdown_relay(8000 + p)?; | ||
| 1205 | } | ||
| 1154 | Ok(()) | 1206 | Ok(()) |
| 1155 | } | 1207 | }); |
| 1156 | async fn prep_run_create_proposal() -> Result<( | 1208 | |
| 1157 | Relay<'static>, | 1209 | // launch relay |
| 1158 | Relay<'static>, | 1210 | let _ = join!( |
| 1159 | Relay<'static>, | 1211 | r51.listen_until_close(), |
| 1160 | Relay<'static>, | 1212 | r52.listen_until_close(), |
| 1161 | Relay<'static>, | 1213 | r53.listen_until_close(), |
| 1162 | )> { | 1214 | r55.listen_until_close(), |
| 1215 | r56.listen_until_close(), | ||
| 1216 | ); | ||
| 1217 | cli_tester_handle.join().unwrap()?; | ||
| 1218 | Ok((r51, r52, r53, r55, r56)) | ||
| 1219 | } | ||
| 1220 | mod cli_ouput { | ||
| 1221 | use super::*; | ||
| 1222 | |||
| 1223 | async fn run_test_async() -> Result<()> { | ||
| 1163 | let git_repo = prep_git_repo()?; | 1224 | let git_repo = prep_git_repo()?; |
| 1164 | // fallback (51,52) user write (53, 55) repo (55, 56) | 1225 | |
| 1165 | let (mut r51, mut r52, mut r53, mut r55, mut r56) = ( | 1226 | let (mut r51, mut r52, mut r53, mut r55, mut r56) = ( |
| 1166 | Relay::new( | 1227 | Relay::new( |
| 1167 | 8051, | 1228 | 8051, |
| @@ -1198,7 +1259,20 @@ mod sends_2_patches_without_cover_letter { | |||
| 1198 | // // check relay had the right number of events | 1259 | // // check relay had the right number of events |
| 1199 | let cli_tester_handle = std::thread::spawn(move || -> Result<()> { | 1260 | let cli_tester_handle = std::thread::spawn(move || -> Result<()> { |
| 1200 | let mut p = cli_tester_create_proposal(&git_repo); | 1261 | let mut p = cli_tester_create_proposal(&git_repo); |
| 1201 | p.expect_end_eventually()?; | 1262 | |
| 1263 | expect_msgs_first(&mut p)?; | ||
| 1264 | relay::expect_send_with_progress( | ||
| 1265 | &mut p, | ||
| 1266 | vec![ | ||
| 1267 | (" [my-relay] [repo-relay] ws://localhost:8055", true, ""), | ||
| 1268 | (" [my-relay] ws://localhost:8053", true, ""), | ||
| 1269 | (" [repo-relay] ws://localhost:8056", true, ""), | ||
| 1270 | (" [default] ws://localhost:8051", true, ""), | ||
| 1271 | (" [default] ws://localhost:8052", true, ""), | ||
| 1272 | ], | ||
| 1273 | 3, | ||
| 1274 | )?; | ||
| 1275 | p.expect_end_with_whitespace()?; | ||
| 1202 | for p in [51, 52, 53, 55, 56] { | 1276 | for p in [51, 52, 53, 55, 56] { |
| 1203 | relay::shutdown_relay(8000 + p)?; | 1277 | relay::shutdown_relay(8000 + p)?; |
| 1204 | } | 1278 | } |
| @@ -1214,146 +1288,327 @@ mod sends_2_patches_without_cover_letter { | |||
| 1214 | r56.listen_until_close(), | 1288 | r56.listen_until_close(), |
| 1215 | ); | 1289 | ); |
| 1216 | cli_tester_handle.join().unwrap()?; | 1290 | cli_tester_handle.join().unwrap()?; |
| 1217 | Ok((r51, r52, r53, r55, r56)) | 1291 | Ok(()) |
| 1218 | } | 1292 | } |
| 1219 | mod cli_ouput { | ||
| 1220 | use super::*; | ||
| 1221 | 1293 | ||
| 1222 | async fn run_test_async() -> Result<()> { | 1294 | #[tokio::test] |
| 1223 | let git_repo = prep_git_repo()?; | 1295 | #[serial] |
| 1296 | async fn check_cli_output() -> Result<()> { | ||
| 1297 | run_test_async().await?; | ||
| 1298 | Ok(()) | ||
| 1299 | } | ||
| 1300 | } | ||
| 1224 | 1301 | ||
| 1225 | let (mut r51, mut r52, mut r53, mut r55, mut r56) = ( | 1302 | #[tokio::test] |
| 1226 | Relay::new( | 1303 | #[serial] |
| 1227 | 8051, | 1304 | async fn three_patch_events() -> Result<()> { |
| 1228 | None, | 1305 | let (_, _, r53, r55, r56) = prep_run_create_proposal().await?; |
| 1229 | Some(&|relay, client_id, subscription_id, _| -> Result<()> { | 1306 | for relay in [&r53, &r55, &r56] { |
| 1230 | relay.respond_events( | 1307 | assert_eq!(relay.events.iter().filter(|e| is_patch(e)).count(), 3); |
| 1231 | client_id, | 1308 | } |
| 1232 | &subscription_id, | 1309 | Ok(()) |
| 1233 | &vec![ | 1310 | } |
| 1234 | generate_test_key_1_metadata_event("fred"), | ||
| 1235 | generate_test_key_1_relay_list_event(), | ||
| 1236 | ], | ||
| 1237 | )?; | ||
| 1238 | Ok(()) | ||
| 1239 | }), | ||
| 1240 | ), | ||
| 1241 | Relay::new(8052, None, None), | ||
| 1242 | Relay::new(8053, None, None), | ||
| 1243 | Relay::new( | ||
| 1244 | 8055, | ||
| 1245 | None, | ||
| 1246 | Some(&|relay, client_id, subscription_id, _| -> Result<()> { | ||
| 1247 | relay.respond_events( | ||
| 1248 | client_id, | ||
| 1249 | &subscription_id, | ||
| 1250 | &vec![generate_repo_ref_event()], | ||
| 1251 | )?; | ||
| 1252 | Ok(()) | ||
| 1253 | }), | ||
| 1254 | ), | ||
| 1255 | Relay::new(8056, None, None), | ||
| 1256 | ); | ||
| 1257 | 1311 | ||
| 1258 | // // check relay had the right number of events | 1312 | #[tokio::test] |
| 1259 | let cli_tester_handle = std::thread::spawn(move || -> Result<()> { | 1313 | #[serial] |
| 1260 | let mut p = cli_tester_create_proposal(&git_repo); | 1314 | async fn first_patch_is_ancestor_and_root_others_in_correct_order() -> Result<()> { |
| 1315 | let (_, _, r53, r55, r56) = prep_run_create_proposal().await?; | ||
| 1316 | for relay in [&r53, &r55, &r56] { | ||
| 1317 | let patch_events = relay | ||
| 1318 | .events | ||
| 1319 | .iter() | ||
| 1320 | .filter(|e| is_patch(e)) | ||
| 1321 | .collect::<Vec<&nostr::Event>>(); | ||
| 1261 | 1322 | ||
| 1262 | expect_msgs_first(&mut p)?; | 1323 | // first patch tagged as root |
| 1263 | relay::expect_send_with_progress( | 1324 | assert!( |
| 1264 | &mut p, | 1325 | patch_events[0] |
| 1265 | vec![ | 1326 | .iter_tags() |
| 1266 | (" [my-relay] [repo-relay] ws://localhost:8055", true, ""), | 1327 | .any(|t| t.as_vec()[0].eq("t") && t.as_vec()[1].eq("root")) |
| 1267 | (" [my-relay] ws://localhost:8053", true, ""), | 1328 | ); |
| 1268 | (" [repo-relay] ws://localhost:8056", true, ""), | 1329 | // first patch is ancestor |
| 1269 | (" [default] ws://localhost:8051", true, ""), | 1330 | assert_eq!( |
| 1270 | (" [default] ws://localhost:8052", true, ""), | 1331 | patch_events[0] |
| 1332 | .iter_tags() | ||
| 1333 | .find(|t| t.as_vec()[0].eq("commit")) | ||
| 1334 | .unwrap() | ||
| 1335 | .as_vec()[1], | ||
| 1336 | "431b84edc0d2fa118d63faa3c2db9c73d630a5ae" | ||
| 1337 | ); | ||
| 1338 | // second patch not tagged as root | ||
| 1339 | assert_eq!( | ||
| 1340 | patch_events[1] | ||
| 1341 | .iter_tags() | ||
| 1342 | .find(|t| t.as_vec()[0].eq("commit")) | ||
| 1343 | .unwrap() | ||
| 1344 | .as_vec()[1], | ||
| 1345 | "232efb37ebc67692c9e9ff58b83c0d3d63971a0a" | ||
| 1346 | ); | ||
| 1347 | // second patch not tagged as root | ||
| 1348 | assert_eq!( | ||
| 1349 | patch_events[2] | ||
| 1350 | .iter_tags() | ||
| 1351 | .find(|t| t.as_vec()[0].eq("commit")) | ||
| 1352 | .unwrap() | ||
| 1353 | .as_vec()[1], | ||
| 1354 | "fe973a840fba2a8ab37dd505c154854a69a6505c" | ||
| 1355 | ); | ||
| 1356 | } | ||
| 1357 | Ok(()) | ||
| 1358 | } | ||
| 1359 | } | ||
| 1360 | |||
| 1361 | mod specify_in_reply_to { | ||
| 1362 | use super::*; | ||
| 1363 | fn cli_tester_create_proposal(git_repo: &GitTestRepo) -> CliTester { | ||
| 1364 | let args = vec![ | ||
| 1365 | "--nsec", | ||
| 1366 | TEST_KEY_1_NSEC, | ||
| 1367 | "--password", | ||
| 1368 | TEST_PASSWORD, | ||
| 1369 | "--disable-cli-spinners", | ||
| 1370 | "send", | ||
| 1371 | "--in-reply-to", | ||
| 1372 | "nevent1qqsypm62fzw7qynvlc4gjl3tr0jw4vmh659nvr2cc5qyhdg92a5yy0qzypumuen7l8wthtz45p3ftn58pvrs9xlumvkuu2xet8egzkcklqtesxygzam", | ||
| 1373 | "--title", | ||
| 1374 | "exampletitle", | ||
| 1375 | "--description", | ||
| 1376 | "exampledescription", | ||
| 1377 | ]; | ||
| 1378 | CliTester::new_from_dir(&git_repo.dir, args) | ||
| 1379 | } | ||
| 1380 | |||
| 1381 | async fn prep_run_create_proposal() -> Result<( | ||
| 1382 | Relay<'static>, | ||
| 1383 | Relay<'static>, | ||
| 1384 | Relay<'static>, | ||
| 1385 | Relay<'static>, | ||
| 1386 | Relay<'static>, | ||
| 1387 | )> { | ||
| 1388 | let git_repo = prep_git_repo()?; | ||
| 1389 | // fallback (51,52) user write (53, 55) repo (55, 56) | ||
| 1390 | let (mut r51, mut r52, mut r53, mut r55, mut r56) = ( | ||
| 1391 | Relay::new( | ||
| 1392 | 8051, | ||
| 1393 | None, | ||
| 1394 | Some(&|relay, client_id, subscription_id, _| -> Result<()> { | ||
| 1395 | relay.respond_events( | ||
| 1396 | client_id, | ||
| 1397 | &subscription_id, | ||
| 1398 | &vec![ | ||
| 1399 | generate_test_key_1_metadata_event("fred"), | ||
| 1400 | generate_test_key_1_relay_list_event(), | ||
| 1271 | ], | 1401 | ], |
| 1272 | 3, | ||
| 1273 | )?; | 1402 | )?; |
| 1274 | p.expect_end_with_whitespace()?; | ||
| 1275 | for p in [51, 52, 53, 55, 56] { | ||
| 1276 | relay::shutdown_relay(8000 + p)?; | ||
| 1277 | } | ||
| 1278 | Ok(()) | 1403 | Ok(()) |
| 1279 | }); | 1404 | }), |
| 1280 | 1405 | ), | |
| 1281 | // launch relay | 1406 | Relay::new(8052, None, None), |
| 1282 | let _ = join!( | 1407 | Relay::new(8053, None, None), |
| 1283 | r51.listen_until_close(), | 1408 | Relay::new( |
| 1284 | r52.listen_until_close(), | 1409 | 8055, |
| 1285 | r53.listen_until_close(), | 1410 | None, |
| 1286 | r55.listen_until_close(), | 1411 | Some(&|relay, client_id, subscription_id, _| -> Result<()> { |
| 1287 | r56.listen_until_close(), | 1412 | relay.respond_events( |
| 1288 | ); | 1413 | client_id, |
| 1289 | cli_tester_handle.join().unwrap()?; | 1414 | &subscription_id, |
| 1290 | Ok(()) | 1415 | &vec![generate_repo_ref_event()], |
| 1416 | )?; | ||
| 1417 | Ok(()) | ||
| 1418 | }), | ||
| 1419 | ), | ||
| 1420 | Relay::new(8056, None, None), | ||
| 1421 | ); | ||
| 1422 | |||
| 1423 | // // check relay had the right number of events | ||
| 1424 | let cli_tester_handle = std::thread::spawn(move || -> Result<()> { | ||
| 1425 | let mut p = cli_tester_create_proposal(&git_repo); | ||
| 1426 | p.expect_end_eventually()?; | ||
| 1427 | for p in [51, 52, 53, 55, 56] { | ||
| 1428 | relay::shutdown_relay(8000 + p)?; | ||
| 1291 | } | 1429 | } |
| 1430 | Ok(()) | ||
| 1431 | }); | ||
| 1432 | |||
| 1433 | // launch relay | ||
| 1434 | let _ = join!( | ||
| 1435 | r51.listen_until_close(), | ||
| 1436 | r52.listen_until_close(), | ||
| 1437 | r53.listen_until_close(), | ||
| 1438 | r55.listen_until_close(), | ||
| 1439 | r56.listen_until_close(), | ||
| 1440 | ); | ||
| 1441 | cli_tester_handle.join().unwrap()?; | ||
| 1442 | Ok((r51, r52, r53, r55, r56)) | ||
| 1443 | } | ||
| 1444 | mod cli_ouput { | ||
| 1445 | use super::*; | ||
| 1292 | 1446 | ||
| 1293 | #[tokio::test] | 1447 | async fn run_test_async() -> Result<()> { |
| 1294 | #[serial] | 1448 | let git_repo = prep_git_repo()?; |
| 1295 | async fn check_cli_output() -> Result<()> { | 1449 | |
| 1296 | run_test_async().await?; | 1450 | let (mut r51, mut r52, mut r53, mut r55, mut r56) = ( |
| 1451 | Relay::new( | ||
| 1452 | 8051, | ||
| 1453 | None, | ||
| 1454 | Some(&|relay, client_id, subscription_id, _| -> Result<()> { | ||
| 1455 | relay.respond_events( | ||
| 1456 | client_id, | ||
| 1457 | &subscription_id, | ||
| 1458 | &vec![ | ||
| 1459 | generate_test_key_1_metadata_event("fred"), | ||
| 1460 | generate_test_key_1_relay_list_event(), | ||
| 1461 | ], | ||
| 1462 | )?; | ||
| 1463 | Ok(()) | ||
| 1464 | }), | ||
| 1465 | ), | ||
| 1466 | Relay::new(8052, None, None), | ||
| 1467 | Relay::new(8053, None, None), | ||
| 1468 | Relay::new( | ||
| 1469 | 8055, | ||
| 1470 | None, | ||
| 1471 | Some(&|relay, client_id, subscription_id, _| -> Result<()> { | ||
| 1472 | relay.respond_events( | ||
| 1473 | client_id, | ||
| 1474 | &subscription_id, | ||
| 1475 | &vec![generate_repo_ref_event()], | ||
| 1476 | )?; | ||
| 1477 | Ok(()) | ||
| 1478 | }), | ||
| 1479 | ), | ||
| 1480 | Relay::new(8056, None, None), | ||
| 1481 | ); | ||
| 1482 | |||
| 1483 | // // check relay had the right number of events | ||
| 1484 | let cli_tester_handle = std::thread::spawn(move || -> Result<()> { | ||
| 1485 | let mut p = cli_tester_create_proposal(&git_repo); | ||
| 1486 | |||
| 1487 | expect_msgs_first(&mut p, true)?; | ||
| 1488 | relay::expect_send_with_progress( | ||
| 1489 | &mut p, | ||
| 1490 | vec![ | ||
| 1491 | (" [my-relay] [repo-relay] ws://localhost:8055", true, ""), | ||
| 1492 | (" [my-relay] ws://localhost:8053", true, ""), | ||
| 1493 | (" [repo-relay] ws://localhost:8056", true, ""), | ||
| 1494 | (" [default] ws://localhost:8051", true, ""), | ||
| 1495 | (" [default] ws://localhost:8052", true, ""), | ||
| 1496 | ], | ||
| 1497 | 3, | ||
| 1498 | )?; | ||
| 1499 | p.expect_end_with_whitespace()?; | ||
| 1500 | for p in [51, 52, 53, 55, 56] { | ||
| 1501 | relay::shutdown_relay(8000 + p)?; | ||
| 1502 | } | ||
| 1297 | Ok(()) | 1503 | Ok(()) |
| 1298 | } | 1504 | }); |
| 1505 | |||
| 1506 | // launch relay | ||
| 1507 | let _ = join!( | ||
| 1508 | r51.listen_until_close(), | ||
| 1509 | r52.listen_until_close(), | ||
| 1510 | r53.listen_until_close(), | ||
| 1511 | r55.listen_until_close(), | ||
| 1512 | r56.listen_until_close(), | ||
| 1513 | ); | ||
| 1514 | cli_tester_handle.join().unwrap()?; | ||
| 1515 | Ok(()) | ||
| 1516 | } | ||
| 1517 | |||
| 1518 | #[tokio::test] | ||
| 1519 | #[serial] | ||
| 1520 | async fn check_cli_output() -> Result<()> { | ||
| 1521 | run_test_async().await?; | ||
| 1522 | Ok(()) | ||
| 1299 | } | 1523 | } |
| 1524 | } | ||
| 1525 | |||
| 1526 | mod cover_letter_tags { | ||
| 1527 | use super::*; | ||
| 1300 | 1528 | ||
| 1301 | #[tokio::test] | 1529 | #[tokio::test] |
| 1302 | #[serial] | 1530 | #[serial] |
| 1303 | async fn three_patch_events() -> Result<()> { | 1531 | async fn t_tag_root() -> Result<()> { |
| 1304 | let (_, _, r53, r55, r56) = prep_run_create_proposal().await?; | 1532 | let (_, _, r53, r55, r56) = prep_run_create_proposal().await?; |
| 1305 | for relay in [&r53, &r55, &r56] { | 1533 | for relay in [&r53, &r55, &r56] { |
| 1306 | assert_eq!(relay.events.iter().filter(|e| is_patch(e)).count(), 3); | 1534 | let cover_letter_event: &nostr::Event = |
| 1535 | relay.events.iter().find(|e| is_cover_letter(e)).unwrap(); | ||
| 1536 | assert!( | ||
| 1537 | cover_letter_event | ||
| 1538 | .iter_tags() | ||
| 1539 | .any(|t| { t.as_vec()[0].eq("t") && t.as_vec()[1].eq(&"root") }) | ||
| 1540 | ); | ||
| 1307 | } | 1541 | } |
| 1308 | Ok(()) | 1542 | Ok(()) |
| 1309 | } | 1543 | } |
| 1310 | 1544 | ||
| 1311 | #[tokio::test] | 1545 | #[tokio::test] |
| 1312 | #[serial] | 1546 | #[serial] |
| 1313 | async fn first_patch_is_ancestor_and_root_others_in_correct_order() -> Result<()> { | 1547 | async fn t_tag_revision_root() -> Result<()> { |
| 1314 | let (_, _, r53, r55, r56) = prep_run_create_proposal().await?; | 1548 | let (_, _, r53, r55, r56) = prep_run_create_proposal().await?; |
| 1315 | for relay in [&r53, &r55, &r56] { | 1549 | for relay in [&r53, &r55, &r56] { |
| 1316 | let patch_events = relay | 1550 | let cover_letter_event: &nostr::Event = |
| 1317 | .events | 1551 | relay.events.iter().find(|e| is_cover_letter(e)).unwrap(); |
| 1318 | .iter() | ||
| 1319 | .filter(|e| is_patch(e)) | ||
| 1320 | .collect::<Vec<&nostr::Event>>(); | ||
| 1321 | |||
| 1322 | // first patch tagged as root | ||
| 1323 | assert!( | 1552 | assert!( |
| 1324 | patch_events[0] | 1553 | cover_letter_event |
| 1325 | .iter_tags() | ||
| 1326 | .any(|t| t.as_vec()[0].eq("t") && t.as_vec()[1].eq("root")) | ||
| 1327 | ); | ||
| 1328 | // first patch is ancestor | ||
| 1329 | assert_eq!( | ||
| 1330 | patch_events[0] | ||
| 1331 | .iter_tags() | 1554 | .iter_tags() |
| 1332 | .find(|t| t.as_vec()[0].eq("commit")) | 1555 | .any(|t| { t.as_vec()[0].eq("t") && t.as_vec()[1].eq(&"revision-root") }) |
| 1333 | .unwrap() | ||
| 1334 | .as_vec()[1], | ||
| 1335 | "431b84edc0d2fa118d63faa3c2db9c73d630a5ae" | ||
| 1336 | ); | 1556 | ); |
| 1337 | // second patch not tagged as root | 1557 | } |
| 1558 | Ok(()) | ||
| 1559 | } | ||
| 1560 | |||
| 1561 | #[tokio::test] | ||
| 1562 | #[serial] | ||
| 1563 | async fn e_tag_in_reply_to_event_as_reply() -> Result<()> { | ||
| 1564 | let (_, _, r53, r55, r56) = prep_run_create_proposal().await?; | ||
| 1565 | for relay in [&r53, &r55, &r56] { | ||
| 1566 | let cover_letter_event: &nostr::Event = | ||
| 1567 | relay.events.iter().find(|e| is_cover_letter(e)).unwrap(); | ||
| 1338 | assert_eq!( | 1568 | assert_eq!( |
| 1339 | patch_events[1] | 1569 | cover_letter_event |
| 1340 | .iter_tags() | 1570 | .iter_tags() |
| 1341 | .find(|t| t.as_vec()[0].eq("commit")) | 1571 | .find(|t| { |
| 1572 | t.as_vec()[0].eq("e") | ||
| 1573 | && t.as_vec().len().eq(&4) | ||
| 1574 | && t.as_vec()[3].eq("reply") | ||
| 1575 | }) | ||
| 1342 | .unwrap() | 1576 | .unwrap() |
| 1343 | .as_vec()[1], | 1577 | .as_vec()[1], |
| 1344 | "232efb37ebc67692c9e9ff58b83c0d3d63971a0a" | 1578 | // id of state nevent |
| 1579 | "40ef4a489de0126cfe2a897e2b1be4eab377d50b360d58c5004bb5055768423c", | ||
| 1345 | ); | 1580 | ); |
| 1346 | // second patch not tagged as root | 1581 | } |
| 1582 | Ok(()) | ||
| 1583 | } | ||
| 1584 | } | ||
| 1585 | |||
| 1586 | #[tokio::test] | ||
| 1587 | #[serial] | ||
| 1588 | async fn patch_tags_cover_letter_event_as_root() -> Result<()> { | ||
| 1589 | let (_, _, r53, r55, r56) = prep_run_create_proposal().await?; | ||
| 1590 | for relay in [&r53, &r55, &r56] { | ||
| 1591 | let patch_events: Vec<&nostr::Event> = | ||
| 1592 | relay.events.iter().filter(|e| is_patch(e)).collect(); | ||
| 1593 | |||
| 1594 | let cover_letter_event = relay.events.iter().find(|e| is_cover_letter(e)).unwrap(); | ||
| 1595 | |||
| 1596 | for patch in patch_events { | ||
| 1347 | assert_eq!( | 1597 | assert_eq!( |
| 1348 | patch_events[2] | 1598 | patch |
| 1349 | .iter_tags() | 1599 | .tags |
| 1350 | .find(|t| t.as_vec()[0].eq("commit")) | 1600 | .iter() |
| 1601 | .find(|t| { | ||
| 1602 | t.as_vec()[0].eq("e") | ||
| 1603 | && t.as_vec().len().eq(&4) | ||
| 1604 | && t.as_vec()[3].eq("root") | ||
| 1605 | }) | ||
| 1351 | .unwrap() | 1606 | .unwrap() |
| 1352 | .as_vec()[1], | 1607 | .as_vec()[1], |
| 1353 | "fe973a840fba2a8ab37dd505c154854a69a6505c" | 1608 | cover_letter_event.id.to_string() |
| 1354 | ); | 1609 | ); |
| 1355 | } | 1610 | } |
| 1356 | Ok(()) | ||
| 1357 | } | 1611 | } |
| 1612 | Ok(()) | ||
| 1358 | } | 1613 | } |
| 1359 | } | 1614 | } |