upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2024-02-13 06:27:34 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2024-02-13 06:27:34 +0000
commit3112576195aef212622d27ad9164336796c1953e (patch)
tree0e897bcb8309c2d4c8f33d7c1590c2dcc0577508
parent9cd3e43b899b23b7f6e75276fa3d19bf9550f8fd (diff)
feat(prs-create)!: pr to nip34-like cover letter
up the pr event type to a nip34-like cover letter format this sets the building blocks in place to enable simplier clients to use the 'cover letter' feature in `git format-patch` to create the experience as a pr event
-rw-r--r--src/git.rs3
-rw-r--r--src/sub_commands/prs/create.rs240
-rw-r--r--src/sub_commands/prs/list.rs28
-rw-r--r--src/sub_commands/pull.rs10
-rw-r--r--src/sub_commands/push.rs13
-rw-r--r--test_utils/src/lib.rs2
-rw-r--r--tests/prs_create.rs219
7 files changed, 375 insertions, 140 deletions
diff --git a/src/git.rs b/src/git.rs
index 067ce24..24afe76 100644
--- a/src/git.rs
+++ b/src/git.rs
@@ -1260,7 +1260,7 @@ mod tests {
1260 &git_repo, 1260 &git_repo,
1261 &git_repo.get_root_commit()?, 1261 &git_repo.get_root_commit()?,
1262 &oid_to_sha1(&original_oid), 1262 &oid_to_sha1(&original_oid),
1263 nostr::EventId::all_zeros(), 1263 Some(nostr::EventId::all_zeros()),
1264 &TEST_KEY_1_KEYS, 1264 &TEST_KEY_1_KEYS,
1265 &RepoRef::try_from(generate_repo_ref_event()).unwrap(), 1265 &RepoRef::try_from(generate_repo_ref_event()).unwrap(),
1266 None, 1266 None,
@@ -1418,6 +1418,7 @@ mod tests {
1418 let git_repo = Repo::from_path(&original_repo.dir)?; 1418 let git_repo = Repo::from_path(&original_repo.dir)?;
1419 1419
1420 let mut events = generate_pr_and_patch_events( 1420 let mut events = generate_pr_and_patch_events(
1421 // Some(("test".to_string(), "test".to_string())),
1421 "title", 1422 "title",
1422 "description", 1423 "description",
1423 &git_repo, 1424 &git_repo,
diff --git a/src/sub_commands/prs/create.rs b/src/sub_commands/prs/create.rs
index 8506303..83a3942 100644
--- a/src/sub_commands/prs/create.rs
+++ b/src/sub_commands/prs/create.rs
@@ -21,10 +21,10 @@ use crate::{
21#[derive(Debug, clap::Args)] 21#[derive(Debug, clap::Args)]
22pub struct SubCommandArgs { 22pub struct SubCommandArgs {
23 #[clap(short, long)] 23 #[clap(short, long)]
24 /// title of pull request (defaults to first line of first commit) 24 /// optional cover letter title
25 title: Option<String>, 25 title: Option<String>,
26 #[clap(short, long)] 26 #[clap(short, long)]
27 /// optional description 27 /// optional cover letter description
28 description: Option<String>, 28 description: Option<String>,
29 #[clap(long)] 29 #[clap(long)]
30 /// branch to get changes from (defaults to head) 30 /// branch to get changes from (defaults to head)
@@ -34,6 +34,7 @@ pub struct SubCommandArgs {
34 to_branch: Option<String>, 34 to_branch: Option<String>,
35} 35}
36 36
37#[allow(clippy::too_many_lines)]
37pub async fn launch( 38pub async fn launch(
38 cli_args: &Cli, 39 cli_args: &Cli,
39 _pr_args: &super::SubCommandArgs, 40 _pr_args: &super::SubCommandArgs,
@@ -91,6 +92,21 @@ pub async fn launch(
91 .input(PromptInputParms::default().with_prompt("description (Optional)"))?, 92 .input(PromptInputParms::default().with_prompt("description (Optional)"))?,
92 }; 93 };
93 94
95 // let cover_letter_title_description = if let Some(title) = title {
96 // Some((
97 // title,
98 // if let Some(t) = &args.description {
99 // t.clone()
100 // } else {
101 // Interactor::default()
102 // .input(PromptInputParms::default().with_prompt("cover letter
103 // description"))? .clone()
104 // },
105 // ))
106 // } else {
107 // None
108 // };
109
94 #[cfg(not(test))] 110 #[cfg(not(test))]
95 let mut client = Client::default(); 111 let mut client = Client::default();
96 #[cfg(test)] 112 #[cfg(test)]
@@ -111,8 +127,15 @@ pub async fn launch(
111 ) 127 )
112 .await?; 128 .await?;
113 129
114 let events = 130 let events = generate_pr_and_patch_events(
115 generate_pr_and_patch_events(&title, &description, &git_repo, &ahead, &keys, &repo_ref)?; 131 // cover_letter_title_description,
132 &title,
133 &description,
134 &git_repo,
135 &ahead,
136 &keys,
137 &repo_ref,
138 )?;
116 139
117 println!( 140 println!(
118 "posting 1 pull request with {} commits...", 141 "posting 1 pull request with {} commits...",
@@ -308,6 +331,7 @@ pub static PATCH_KIND: u64 = 1617;
308pub fn generate_pr_and_patch_events( 331pub fn generate_pr_and_patch_events(
309 title: &str, 332 title: &str,
310 description: &str, 333 description: &str,
334 // cover_letter_title_description: Option<(String, String)>,
311 git_repo: &Repo, 335 git_repo: &Repo,
312 commits: &Vec<Sha1Hash>, 336 commits: &Vec<Sha1Hash>,
313 keys: &nostr::Keys, 337 keys: &nostr::Keys,
@@ -317,40 +341,57 @@ pub fn generate_pr_and_patch_events(
317 .get_root_commit() 341 .get_root_commit()
318 .context("failed to get root commit of the repository")?; 342 .context("failed to get root commit of the repository")?;
319 343
320 let mut pr_tags = vec![ 344 let mut events = vec![];
321 Tag::Reference(format!("r-{root_commit}")),
322 Tag::Name(title.to_string()),
323 Tag::Description(description.to_string()),
324 ];
325
326 if let Ok(branch_name) = git_repo.get_checked_out_branch_name() {
327 pr_tags.push(Tag::Generic(
328 TagKind::Custom("branch-name".to_string()),
329 vec![branch_name],
330 ));
331 }
332 345
333 let pr_event = EventBuilder::new( 346 // if let Some((title, description)) = cover_letter_title_description {
347 if !title.is_empty() {
348 events.push(EventBuilder::new(
334 nostr::event::Kind::Custom(PR_KIND), 349 nostr::event::Kind::Custom(PR_KIND),
335 format!("{title}\r\n\r\n{description}"), 350 format!(
336 pr_tags, 351 "From {} Mon Sep 17 00:00:00 2001\nSubject: [PATCH 0/{}] {title}\n\n{description}",
337 // TODO: add Repo event as root 352 commits.last().unwrap(),
338 // TODO: people tag maintainers 353 commits.len()
339 // TODO: add relay tags 354 ),
355 [
356 vec![
357 // TODO: why not tag all maintainer identifiers?
358 Tag::A {
359 kind: nostr::Kind::Custom(REPO_REF_KIND),
360 public_key: *repo_ref.maintainers.first()
361 .context("repo reference should always have at least one maintainer - the issuer of the repo event")
362 ?,
363 identifier: repo_ref.identifier.to_string(),
364 relay_url: repo_ref.relays.first().map(nostr::UncheckedUrl::from).clone(),
365 },
366 Tag::Reference(format!("{root_commit}")),
367 Tag::Hashtag("cover-letter".to_string()),
368 Tag::Hashtag("root".to_string()),
369 ],
370 if let Ok(branch_name) = git_repo.get_checked_out_branch_name() {
371 vec![Tag::Generic(
372 TagKind::Custom("branch-name".to_string()),
373 vec![branch_name],
374 )]
375 } else {
376 vec![]
377 },
378 repo_ref.maintainers
379 .iter()
380 .map(|pk| Tag::public_key(*pk))
381 .collect(),
382 ].concat(),
340 ) 383 )
341 .to_event(keys) 384 .to_event(keys)
342 .context("failed to create pr event")?; 385 .context("failed to create cover-letter event")?);
343 386 }
344 let pr_event_id = pr_event.id;
345 387
346 let mut events = vec![pr_event];
347 for (i, commit) in commits.iter().enumerate() { 388 for (i, commit) in commits.iter().enumerate() {
348 events.push( 389 events.push(
349 generate_patch_event( 390 generate_patch_event(
350 git_repo, 391 git_repo,
351 &root_commit, 392 &root_commit,
352 commit, 393 commit,
353 pr_event_id, 394 events.first().map(|event| event.id),
354 keys, 395 keys,
355 repo_ref, 396 repo_ref,
356 events.last().map(nostr::Event::id), 397 events.last().map(nostr::Event::id),
@@ -366,12 +407,45 @@ pub fn generate_pr_and_patch_events(
366 Ok(events) 407 Ok(events)
367} 408}
368 409
410pub struct CoverLetter {
411 pub title: String,
412 pub description: String,
413 pub branch_name: Option<String>,
414}
415
416fn event_is_cover_letter(event: &nostr::Event) -> bool {
417 event.kind.as_u64().eq(&PR_KIND) && event.iter_tags().any(|t| t.as_vec()[1].eq("cover-letter"))
418}
419pub fn event_to_cover_letter(event: &nostr::Event) -> Result<CoverLetter> {
420 if !event_is_cover_letter(event) {
421 bail!("event is not a cover letter")
422 }
423 let title_index = event
424 .content
425 .find("] ")
426 .context("event is not formatted as a cover letter patch")?
427 + 2;
428 let description_index = event.content[title_index..]
429 .find('\n')
430 .unwrap_or(event.content.len() - 1 - title_index)
431 + title_index;
432
433 Ok(CoverLetter {
434 title: event.content[title_index..description_index].to_string(),
435 description: event.content[description_index..].trim().to_string(),
436 branch_name: event
437 .iter_tags()
438 .find(|t| t.as_vec()[0].eq("branch-name"))
439 .map(|tag| tag.as_vec()[1].clone()),
440 })
441}
442
369#[allow(clippy::too_many_arguments)] 443#[allow(clippy::too_many_arguments)]
370pub fn generate_patch_event( 444pub fn generate_patch_event(
371 git_repo: &Repo, 445 git_repo: &Repo,
372 root_commit: &Sha1Hash, 446 root_commit: &Sha1Hash,
373 commit: &Sha1Hash, 447 commit: &Sha1Hash,
374 thread_event_id: nostr::EventId, 448 thread_event_id: Option<nostr::EventId>,
375 keys: &nostr::Keys, 449 keys: &nostr::Keys,
376 repo_ref: &RepoRef, 450 repo_ref: &RepoRef,
377 parent_patch_event_id: Option<nostr::EventId>, 451 parent_patch_event_id: Option<nostr::EventId>,
@@ -404,10 +478,13 @@ pub fn generate_patch_event(
404 // the commit id is correct 478 // the commit id is correct
405 Tag::Reference(commit.to_string()), 479 Tag::Reference(commit.to_string()),
406 480
407 Tag::Event { 481 if let Some(thread_event_id) = thread_event_id { Tag::Event {
408 event_id: thread_event_id, 482 event_id: thread_event_id,
409 relay_url: relay_hint.clone(), 483 relay_url: relay_hint.clone(),
410 marker: Some(Marker::Root), 484 marker: Some(Marker::Root),
485 } }
486 else {
487 Tag::Hashtag("root".to_string())
411 }, 488 },
412 ], 489 ],
413 if let Some(id) = parent_patch_event_id { 490 if let Some(id) = parent_patch_event_id {
@@ -686,4 +763,109 @@ mod tests {
686 Ok(()) 763 Ok(())
687 } 764 }
688 } 765 }
766
767 mod event_to_cover_letter {
768 use super::*;
769
770 fn generate_cover_letter(title: &str, description: &str) -> Result<nostr::Event> {
771 Ok(nostr::event::EventBuilder::new(
772 nostr::event::Kind::Custom(PR_KIND),
773 format!("From ea897e987ea9a7a98e7a987e97987ea98e7a3334 Mon Sep 17 00:00:00 2001\nSubject: [PATCH 0/2] {title}\n\n{description}"),
774 [
775 Tag::Hashtag("cover-letter".to_string()),
776 Tag::Hashtag("root".to_string()),
777 ],
778 )
779 .to_event(&nostr::Keys::generate())?)
780 }
781
782 #[test]
783 fn basic_title() -> Result<()> {
784 assert_eq!(
785 event_to_cover_letter(&generate_cover_letter("the title", "description here")?)?
786 .title,
787 "the title",
788 );
789 Ok(())
790 }
791
792 #[test]
793 fn basic_description() -> Result<()> {
794 assert_eq!(
795 event_to_cover_letter(&generate_cover_letter("the title", "description here")?)?
796 .description,
797 "description here",
798 );
799 Ok(())
800 }
801
802 #[test]
803 fn description_trimmed() -> Result<()> {
804 assert_eq!(
805 event_to_cover_letter(&generate_cover_letter(
806 "the title",
807 " \n \ndescription here\n\n "
808 )?)?
809 .description,
810 "description here",
811 );
812 Ok(())
813 }
814
815 #[test]
816 fn multi_line_description() -> Result<()> {
817 assert_eq!(
818 event_to_cover_letter(&generate_cover_letter(
819 "the title",
820 "description here\n\nmore here\nmore"
821 )?)?
822 .description,
823 "description here\n\nmore here\nmore",
824 );
825 Ok(())
826 }
827
828 #[test]
829 fn new_lines_in_title_forms_part_of_description() -> Result<()> {
830 assert_eq!(
831 event_to_cover_letter(&generate_cover_letter(
832 "the title\nwith new line",
833 "description here\n\nmore here\nmore"
834 )?)?
835 .title,
836 "the title",
837 );
838 assert_eq!(
839 event_to_cover_letter(&generate_cover_letter(
840 "the title\nwith new line",
841 "description here\n\nmore here\nmore"
842 )?)?
843 .description,
844 "with new line\n\ndescription here\n\nmore here\nmore",
845 );
846 Ok(())
847 }
848
849 mod blank_description {
850 use super::*;
851
852 #[test]
853 fn title_correct() -> Result<()> {
854 assert_eq!(
855 event_to_cover_letter(&generate_cover_letter("the title", "")?)?.title,
856 "the title",
857 );
858 Ok(())
859 }
860
861 #[test]
862 fn description_is_empty_string() -> Result<()> {
863 assert_eq!(
864 event_to_cover_letter(&generate_cover_letter("the title", "")?)?.description,
865 "",
866 );
867 Ok(())
868 }
869 }
870 }
689} 871}
diff --git a/src/sub_commands/prs/list.rs b/src/sub_commands/prs/list.rs
index 88b325b..bc85eed 100644
--- a/src/sub_commands/prs/list.rs
+++ b/src/sub_commands/prs/list.rs
@@ -8,8 +8,8 @@ use crate::{
8 cli_interactor::{Interactor, InteractorPrompt, PromptChoiceParms, PromptConfirmParms}, 8 cli_interactor::{Interactor, InteractorPrompt, PromptChoiceParms, PromptConfirmParms},
9 client::Connect, 9 client::Connect,
10 git::{Repo, RepoActions}, 10 git::{Repo, RepoActions},
11 repo_ref, 11 repo_ref::{self},
12 sub_commands::prs::create::{PATCH_KIND, PR_KIND}, 12 sub_commands::prs::create::{event_to_cover_letter, PATCH_KIND, PR_KIND},
13 Cli, 13 Cli,
14}; 14};
15 15
@@ -20,6 +20,7 @@ pub struct SubCommandArgs {
20 open_only: bool, 20 open_only: bool,
21} 21}
22 22
23#[allow(clippy::too_many_lines)]
23pub async fn launch( 24pub async fn launch(
24 _cli_args: &Cli, 25 _cli_args: &Cli,
25 _pr_args: &super::SubCommandArgs, 26 _pr_args: &super::SubCommandArgs,
@@ -56,7 +57,7 @@ pub async fn launch(
56 vec![ 57 vec![
57 nostr::Filter::default() 58 nostr::Filter::default()
58 .kind(nostr::Kind::Custom(PR_KIND)) 59 .kind(nostr::Kind::Custom(PR_KIND))
59 .reference(format!("r-{root_commit}")), 60 .reference(format!("{root_commit}")),
60 ], 61 ],
61 ) 62 )
62 .await? 63 .await?
@@ -65,7 +66,7 @@ pub async fn launch(
65 e.kind.as_u64() == PR_KIND 66 e.kind.as_u64() == PR_KIND
66 && e.tags 67 && e.tags
67 .iter() 68 .iter()
68 .any(|t| t.as_vec().len() > 1 && t.as_vec()[1].eq(&format!("r-{root_commit}"))) 69 .any(|t| t.as_vec().len() > 1 && t.as_vec()[1].eq(&format!("{root_commit}")))
69 }) 70 })
70 .map(std::borrow::ToOwned::to_owned) 71 .map(std::borrow::ToOwned::to_owned)
71 .collect(); 72 .collect();
@@ -92,8 +93,8 @@ pub async fn launch(
92 pr_events 93 pr_events
93 .iter() 94 .iter()
94 .map(|e| { 95 .map(|e| {
95 if let Ok(name) = tag_value(e, "name") { 96 if let Ok(cl) = event_to_cover_letter(e) {
96 name 97 cl.title
97 } else { 98 } else {
98 e.id.to_string() 99 e.id.to_string()
99 } 100 }
@@ -131,7 +132,19 @@ pub async fn launch(
131 let most_recent_pr_patch_chain = get_most_recent_patch_with_ancestors(commits_events) 132 let most_recent_pr_patch_chain = get_most_recent_patch_with_ancestors(commits_events)
132 .context("cannot get most recent patch for PR")?; 133 .context("cannot get most recent patch for PR")?;
133 134
134 let branch_name = tag_value(&pr_events[selected_index], "branch-name")?; 135 let branch_name: String = if let Ok(cl) = event_to_cover_letter(&pr_events[selected_index]) {
136 if let Some(name) = cl.branch_name {
137 name
138 } else {
139 cl.title
140 .replace(' ', "-")
141 .chars()
142 .filter(|c| c.is_ascii_alphanumeric() || c.eq(&'/'))
143 .collect()
144 }
145 } else {
146 bail!("Placeholder not a cover letter")
147 };
135 148
136 let applied = git_repo 149 let applied = git_repo
137 .apply_patch_chain(&branch_name, most_recent_pr_patch_chain) 150 .apply_patch_chain(&branch_name, most_recent_pr_patch_chain)
@@ -145,7 +158,6 @@ pub async fn launch(
145 applied.len(), 158 applied.len(),
146 ); 159 );
147 } 160 }
148
149 Ok(()) 161 Ok(())
150} 162}
151 163
diff --git a/src/sub_commands/pull.rs b/src/sub_commands/pull.rs
index c426510..f3ae81f 100644
--- a/src/sub_commands/pull.rs
+++ b/src/sub_commands/pull.rs
@@ -8,6 +8,7 @@ use crate::{
8 client::Connect, 8 client::Connect,
9 git::{Repo, RepoActions}, 9 git::{Repo, RepoActions},
10 repo_ref, 10 repo_ref,
11 repo_ref::REPO_REF_KIND,
11 sub_commands::prs::{ 12 sub_commands::prs::{
12 create::{PATCH_KIND, PR_KIND}, 13 create::{PATCH_KIND, PR_KIND},
13 list::{get_most_recent_patch_with_ancestors, tag_value}, 14 list::{get_most_recent_patch_with_ancestors, tag_value},
@@ -53,7 +54,12 @@ pub async fn launch() -> Result<()> {
53 vec![ 54 vec![
54 nostr::Filter::default() 55 nostr::Filter::default()
55 .kind(nostr::Kind::Custom(PR_KIND)) 56 .kind(nostr::Kind::Custom(PR_KIND))
56 .reference(format!("r-{root_commit}")), 57 .identifiers(
58 repo_ref
59 .maintainers
60 .iter()
61 .map(|m| format!("{REPO_REF_KIND}:{m}:{}", repo_ref.identifier)),
62 ),
57 ], 63 ],
58 ) 64 )
59 .await? 65 .await?
@@ -62,7 +68,7 @@ pub async fn launch() -> Result<()> {
62 e.kind.as_u64() == PR_KIND 68 e.kind.as_u64() == PR_KIND
63 && e.tags 69 && e.tags
64 .iter() 70 .iter()
65 .any(|t| t.as_vec().len() > 1 && t.as_vec()[1].eq(&format!("r-{root_commit}"))) 71 .any(|t| t.as_vec().len() > 1 && t.as_vec()[1].eq(&format!("{root_commit}")))
66 && tag_value(e, "branch-name") 72 && tag_value(e, "branch-name")
67 .unwrap_or_default() 73 .unwrap_or_default()
68 .eq(&branch_name) 74 .eq(&branch_name)
diff --git a/src/sub_commands/push.rs b/src/sub_commands/push.rs
index 037d512..61d5d46 100644
--- a/src/sub_commands/push.rs
+++ b/src/sub_commands/push.rs
@@ -9,7 +9,7 @@ use crate::{
9 client::Connect, 9 client::Connect,
10 git::{str_to_sha1, Repo, RepoActions}, 10 git::{str_to_sha1, Repo, RepoActions},
11 login, 11 login,
12 repo_ref::{self, RepoRef}, 12 repo_ref::{self, RepoRef, REPO_REF_KIND},
13 sub_commands::prs::{ 13 sub_commands::prs::{
14 create::{generate_patch_event, send_events, PATCH_KIND, PR_KIND}, 14 create::{generate_patch_event, send_events, PATCH_KIND, PR_KIND},
15 list::{get_most_recent_patch_with_ancestors, tag_value}, 15 list::{get_most_recent_patch_with_ancestors, tag_value},
@@ -106,7 +106,7 @@ pub async fn launch(cli_args: &Cli) -> Result<()> {
106 &git_repo, 106 &git_repo,
107 &root_commit, 107 &root_commit,
108 commit, 108 commit,
109 pr_event.id, 109 Some(pr_event.id),
110 &keys, 110 &keys,
111 &repo_ref, 111 &repo_ref,
112 patch_events.last().map(nostr::Event::id), 112 patch_events.last().map(nostr::Event::id),
@@ -146,7 +146,12 @@ async fn fetch_pr_and_most_recent_patch_chain(
146 vec![ 146 vec![
147 nostr::Filter::default() 147 nostr::Filter::default()
148 .kind(nostr::Kind::Custom(PR_KIND)) 148 .kind(nostr::Kind::Custom(PR_KIND))
149 .reference(format!("r-{root_commit}")), 149 .identifiers(
150 repo_ref
151 .maintainers
152 .iter()
153 .map(|m| format!("{REPO_REF_KIND}:{m}:{}", repo_ref.identifier)),
154 ),
150 ], 155 ],
151 ) 156 )
152 .await? 157 .await?
@@ -155,7 +160,7 @@ async fn fetch_pr_and_most_recent_patch_chain(
155 e.kind.as_u64() == PR_KIND 160 e.kind.as_u64() == PR_KIND
156 && e.tags 161 && e.tags
157 .iter() 162 .iter()
158 .any(|t| t.as_vec().len() > 1 && t.as_vec()[1].eq(&format!("r-{root_commit}"))) 163 .any(|t| t.as_vec().len() > 1 && t.as_vec()[1].eq(&format!("{root_commit}")))
159 && tag_value(e, "branch-name") 164 && tag_value(e, "branch-name")
160 .unwrap_or_default() 165 .unwrap_or_default()
161 .eq(branch_name) 166 .eq(branch_name)
diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs
index 94ada76..8128e98 100644
--- a/test_utils/src/lib.rs
+++ b/test_utils/src/lib.rs
@@ -21,6 +21,8 @@ pub static TEST_KEY_1_SK_HEX: &str =
21 "08608a436aee4c07ea5c36f85cb17c58f52b3ad7094f9318cc777771f0bf218b"; 21 "08608a436aee4c07ea5c36f85cb17c58f52b3ad7094f9318cc777771f0bf218b";
22pub static TEST_KEY_1_NPUB: &str = 22pub static TEST_KEY_1_NPUB: &str =
23 "npub175lyhnt6nn00qjw0v3navw9pxgv43txnku0tpxprl4h6mvpr6a5qlphudg"; 23 "npub175lyhnt6nn00qjw0v3navw9pxgv43txnku0tpxprl4h6mvpr6a5qlphudg";
24pub static TEST_KEY_1_PUBKEY_HEX: &str =
25 "f53e4bcd7a9cdef049cf6467d638a1321958acd3b71eb09823fd6fadb023d768";
24pub static TEST_KEY_1_DISPLAY_NAME: &str = "bob"; 26pub static TEST_KEY_1_DISPLAY_NAME: &str = "bob";
25pub static TEST_KEY_1_ENCRYPTED: &str = "ncryptsec1qyq607h3cykxc3f2a44u89cdk336fptccn3fm5pf3nmf93d3c86qpunc7r6klwcn6lyszjy72wxwqq9aljg4pm6atvjrds9e248yhv76xfnt464265kgnjsvg8rlg06wg4sp9uljzfpu8zuaztcvfn2j8ggdrg8mldh850cy75efsyqqansert9wqmn4e6khpgvfz7h5le9"; 27pub static TEST_KEY_1_ENCRYPTED: &str = "ncryptsec1qyq607h3cykxc3f2a44u89cdk336fptccn3fm5pf3nmf93d3c86qpunc7r6klwcn6lyszjy72wxwqq9aljg4pm6atvjrds9e248yhv76xfnt464265kgnjsvg8rlg06wg4sp9uljzfpu8zuaztcvfn2j8ggdrg8mldh850cy75efsyqqansert9wqmn4e6khpgvfz7h5le9";
26pub static TEST_KEY_1_ENCRYPTED_WEAK: &str = "ncryptsec1qy8ke0tjqnn8wt3w6lnc86c27ry3qrptxctjfcgruryxy0at238kwyjwsswd7z88thysruzw3awlrsxjvw5uptcd7vt70ft9rtkx00m8cgy3khm4hxa5d2gfnc6athnfruy2eyl6pkas8k34jg85z7xjqqadzfzh9rp0fzxqtw0tvxksac3n8yc98uksvuf93e0lcvqy8j6"; 28pub static TEST_KEY_1_ENCRYPTED_WEAK: &str = "ncryptsec1qy8ke0tjqnn8wt3w6lnc86c27ry3qrptxctjfcgruryxy0at238kwyjwsswd7z88thysruzw3awlrsxjvw5uptcd7vt70ft9rtkx00m8cgy3khm4hxa5d2gfnc6athnfruy2eyl6pkas8k34jg85z7xjqqadzfzh9rp0fzxqtw0tvxksac3n8yc98uksvuf93e0lcvqy8j6";
diff --git a/tests/prs_create.rs b/tests/prs_create.rs
index 8db45d7..6272ccd 100644
--- a/tests/prs_create.rs
+++ b/tests/prs_create.rs
@@ -141,6 +141,15 @@ fn cli_message_creating_patches() -> Result<()> {
141 Ok(()) 141 Ok(())
142} 142}
143 143
144fn is_cover_letter(event: &nostr::Event) -> bool {
145 event.kind.as_u64().eq(&PR_KIND) && event.iter_tags().any(|t| t.as_vec()[1].eq("cover-letter"))
146}
147
148fn is_patch(event: &nostr::Event) -> bool {
149 event.kind.as_u64().eq(&PATCH_KIND)
150 && !event.iter_tags().any(|t| t.as_vec()[1].eq("cover-letter"))
151}
152
144mod sends_pr_and_2_patches_to_3_relays { 153mod sends_pr_and_2_patches_to_3_relays {
145 use futures::join; 154 use futures::join;
146 use test_utils::relay::Relay; 155 use test_utils::relay::Relay;
@@ -258,11 +267,7 @@ mod sends_pr_and_2_patches_to_3_relays {
258 let (_, _, r53, r55, r56) = prep_run_create_pr().await?; 267 let (_, _, r53, r55, r56) = prep_run_create_pr().await?;
259 for relay in [&r53, &r55, &r56] { 268 for relay in [&r53, &r55, &r56] {
260 assert_eq!( 269 assert_eq!(
261 relay 270 relay.events.iter().filter(|e| is_cover_letter(e)).count(),
262 .events
263 .iter()
264 .filter(|e| e.kind.as_u64().eq(&PR_KIND))
265 .count(),
266 1, 271 1,
267 ); 272 );
268 } 273 }
@@ -275,11 +280,7 @@ mod sends_pr_and_2_patches_to_3_relays {
275 let (_, _, r53, r55, _) = prep_run_create_pr().await?; 280 let (_, _, r53, r55, _) = prep_run_create_pr().await?;
276 for relay in [&r53, &r55] { 281 for relay in [&r53, &r55] {
277 assert_eq!( 282 assert_eq!(
278 relay 283 relay.events.iter().filter(|e| is_cover_letter(e)).count(),
279 .events
280 .iter()
281 .filter(|e| e.kind.as_u64().eq(&PR_KIND))
282 .count(),
283 1, 284 1,
284 ); 285 );
285 } 286 }
@@ -292,12 +293,8 @@ mod sends_pr_and_2_patches_to_3_relays {
292 let (_, _, _, r55, r56) = prep_run_create_pr().await?; 293 let (_, _, _, r55, r56) = prep_run_create_pr().await?;
293 for relay in [&r55, &r56] { 294 for relay in [&r55, &r56] {
294 assert_eq!( 295 assert_eq!(
295 relay 296 relay.events.iter().filter(|e| is_cover_letter(e)).count(),
296 .events 297 1
297 .iter()
298 .filter(|e| e.kind.as_u64().eq(&PR_KIND))
299 .count(),
300 1,
301 ); 298 );
302 } 299 }
303 Ok(()) 300 Ok(())
@@ -309,11 +306,7 @@ mod sends_pr_and_2_patches_to_3_relays {
309 let (r51, r52, _, _, _) = prep_run_create_pr().await?; 306 let (r51, r52, _, _, _) = prep_run_create_pr().await?;
310 for relay in [&r51, &r52] { 307 for relay in [&r51, &r52] {
311 assert_eq!( 308 assert_eq!(
312 relay 309 relay.events.iter().filter(|e| is_cover_letter(e)).count(),
313 .events
314 .iter()
315 .filter(|e| e.kind.as_u64().eq(&PR_KIND))
316 .count(),
317 0, 310 0,
318 ); 311 );
319 } 312 }
@@ -325,14 +318,7 @@ mod sends_pr_and_2_patches_to_3_relays {
325 async fn only_2_patch_kind_events_sent_to_each_relay() -> Result<()> { 318 async fn only_2_patch_kind_events_sent_to_each_relay() -> Result<()> {
326 let (_, _, r53, r55, r56) = prep_run_create_pr().await?; 319 let (_, _, r53, r55, r56) = prep_run_create_pr().await?;
327 for relay in [&r53, &r55, &r56] { 320 for relay in [&r53, &r55, &r56] {
328 assert_eq!( 321 assert_eq!(relay.events.iter().filter(|e| is_patch(e)).count(), 2,);
329 relay
330 .events
331 .iter()
332 .filter(|e| e.kind.as_u64().eq(&PATCH_KIND))
333 .count(),
334 2,
335 );
336 } 322 }
337 Ok(()) 323 Ok(())
338 } 324 }
@@ -343,11 +329,8 @@ mod sends_pr_and_2_patches_to_3_relays {
343 { 329 {
344 let (_, _, r53, r55, r56) = prep_run_create_pr().await?; 330 let (_, _, r53, r55, r56) = prep_run_create_pr().await?;
345 for relay in [&r53, &r55, &r56] { 331 for relay in [&r53, &r55, &r56] {
346 let patch_events: Vec<&nostr::Event> = relay 332 let patch_events: Vec<&nostr::Event> =
347 .events 333 relay.events.iter().filter(|e| is_patch(e)).collect();
348 .iter()
349 .filter(|e| e.kind.as_u64().eq(&PATCH_KIND))
350 .collect();
351 334
352 assert_eq!( 335 assert_eq!(
353 patch_events[0].content, 336 patch_events[0].content,
@@ -410,21 +393,19 @@ mod sends_pr_and_2_patches_to_3_relays {
410 393
411 #[tokio::test] 394 #[tokio::test]
412 #[serial] 395 #[serial]
413 async fn pr_tags_repo_commit() -> Result<()> { 396 async fn root_commit_as_r() -> Result<()> {
414 let (_, _, r53, r55, r56) = prep_run_create_pr().await?; 397 let (_, _, r53, r55, r56) = prep_run_create_pr().await?;
415 let root_commit = GitTestRepo::default().initial_commit()?;
416
417 for relay in [&r53, &r55, &r56] { 398 for relay in [&r53, &r55, &r56] {
418 let pr_event: &nostr::Event = relay 399 let pr_event: &nostr::Event =
419 .events 400 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
420 .iter()
421 .find(|e| e.kind.as_u64().eq(&PR_KIND))
422 .unwrap();
423 401
424 // root commit 'r' tag 402 assert_eq!(
425 assert!( 403 pr_event
426 pr_event.tags.iter().any(|t| t.as_vec()[0].eq("r") 404 .iter_tags()
427 && t.as_vec()[1].eq(&format!("r-{}", root_commit))) 405 .find(|t| t.as_vec()[0].eq("r"))
406 .unwrap()
407 .as_vec()[1],
408 "9ee507fc4357d7ee16a5d8901bedcd103f23c17d"
428 ); 409 );
429 } 410 }
430 Ok(()) 411 Ok(())
@@ -432,23 +413,55 @@ mod sends_pr_and_2_patches_to_3_relays {
432 413
433 #[tokio::test] 414 #[tokio::test]
434 #[serial] 415 #[serial]
435 async fn pr_tags_title_as_name() -> Result<()> { 416 async fn a_tag_for_repo_event() -> Result<()> {
436 let (_, _, r53, r55, r56) = prep_run_create_pr().await?; 417 let (_, _, r53, r55, r56) = prep_run_create_pr().await?;
437 for relay in [&r53, &r55, &r56] { 418 for relay in [&r53, &r55, &r56] {
438 let pr_event: &nostr::Event = relay 419 let pr_event: &nostr::Event =
439 .events 420 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
440 .iter() 421 assert!(pr_event.iter_tags().any(|t| t.as_vec()[0].eq("a")
441 .find(|e| e.kind.as_u64().eq(&PR_KIND)) 422 && t.as_vec()[1].eq(&format!(
442 .unwrap(); 423 "{REPOSITORY_KIND}:{TEST_KEY_1_PUBKEY_HEX}:{}",
424 generate_repo_ref_event().identifier().unwrap()
425 ))));
426 }
427 Ok(())
428 }
443 429
444 assert_eq!( 430 #[tokio::test]
431 #[serial]
432 async fn p_tags_for_maintainers() -> Result<()> {
433 let maintainers = &generate_repo_ref_event()
434 .iter_tags()
435 .find(|t| t.as_vec()[0].eq(&"maintainers"))
436 .unwrap()
437 .as_vec()
438 .clone()[1..];
439 let (_, _, r53, r55, r56) = prep_run_create_pr().await?;
440 for relay in [&r53, &r55, &r56] {
441 for m in maintainers {
442 let pr_event: &nostr::Event =
443 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
444 assert!(
445 pr_event
446 .iter_tags()
447 .any(|t| { t.as_vec()[0].eq("p") && t.as_vec()[1].eq(m) })
448 );
449 }
450 }
451 Ok(())
452 }
453
454 #[tokio::test]
455 #[serial]
456 async fn t_tag_cover_letter() -> Result<()> {
457 let (_, _, r53, r55, r56) = prep_run_create_pr().await?;
458 for relay in [&r53, &r55, &r56] {
459 let pr_event: &nostr::Event =
460 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
461 assert!(
445 pr_event 462 pr_event
446 .tags 463 .iter_tags()
447 .iter() 464 .any(|t| { t.as_vec()[0].eq("t") && t.as_vec()[1].eq(&"cover-letter") })
448 .find(|t| t.as_vec()[0].eq("name"))
449 .unwrap()
450 .as_vec()[1],
451 "exampletitle"
452 ); 465 );
453 } 466 }
454 Ok(()) 467 Ok(())
@@ -456,23 +469,15 @@ mod sends_pr_and_2_patches_to_3_relays {
456 469
457 #[tokio::test] 470 #[tokio::test]
458 #[serial] 471 #[serial]
459 async fn pr_tags_description() -> Result<()> { 472 async fn t_tag_root() -> Result<()> {
460 let (_, _, r53, r55, r56) = prep_run_create_pr().await?; 473 let (_, _, r53, r55, r56) = prep_run_create_pr().await?;
461 for relay in [&r53, &r55, &r56] { 474 for relay in [&r53, &r55, &r56] {
462 let pr_event: &nostr::Event = relay 475 let pr_event: &nostr::Event =
463 .events 476 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
464 .iter() 477 assert!(
465 .find(|e| e.kind.as_u64().eq(&PR_KIND))
466 .unwrap();
467
468 assert_eq!(
469 pr_event 478 pr_event
470 .tags 479 .iter_tags()
471 .iter() 480 .any(|t| { t.as_vec()[0].eq("t") && t.as_vec()[1].eq(&"root") })
472 .find(|t| t.as_vec()[0].eq("description"))
473 .unwrap()
474 .as_vec()[1],
475 "exampledescription"
476 ); 481 );
477 } 482 }
478 Ok(()) 483 Ok(())
@@ -483,17 +488,13 @@ mod sends_pr_and_2_patches_to_3_relays {
483 async fn pr_tags_branch_name() -> Result<()> { 488 async fn pr_tags_branch_name() -> Result<()> {
484 let (_, _, r53, r55, r56) = prep_run_create_pr().await?; 489 let (_, _, r53, r55, r56) = prep_run_create_pr().await?;
485 for relay in [&r53, &r55, &r56] { 490 for relay in [&r53, &r55, &r56] {
486 let pr_event: &nostr::Event = relay 491 let pr_event: &nostr::Event =
487 .events 492 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
488 .iter()
489 .find(|e| e.kind.as_u64().eq(&PR_KIND))
490 .unwrap();
491 493
492 // branch-name tag 494 // branch-name tag
493 assert_eq!( 495 assert_eq!(
494 pr_event 496 pr_event
495 .tags 497 .iter_tags()
496 .iter()
497 .find(|t| t.as_vec()[0].eq("branch-name")) 498 .find(|t| t.as_vec()[0].eq("branch-name"))
498 .unwrap() 499 .unwrap()
499 .as_vec()[1], 500 .as_vec()[1],
@@ -509,12 +510,7 @@ mod sends_pr_and_2_patches_to_3_relays {
509 510
510 async fn prep() -> Result<nostr::Event> { 511 async fn prep() -> Result<nostr::Event> {
511 let (_, _, r53, _, _) = prep_run_create_pr().await?; 512 let (_, _, r53, _, _) = prep_run_create_pr().await?;
512 Ok(r53 513 Ok(r53.events.iter().find(|e| is_patch(e)).unwrap().clone())
513 .events
514 .iter()
515 .find(|e| e.kind.as_u64().eq(&PATCH_KIND))
516 .unwrap()
517 .clone())
518 } 514 }
519 515
520 #[tokio::test] 516 #[tokio::test]
@@ -561,6 +557,39 @@ mod sends_pr_and_2_patches_to_3_relays {
561 557
562 #[tokio::test] 558 #[tokio::test]
563 #[serial] 559 #[serial]
560 async fn p_tags_for_maintainers() -> Result<()> {
561 let maintainers = &generate_repo_ref_event()
562 .iter_tags()
563 .find(|t| t.as_vec()[0].eq(&"maintainers"))
564 .unwrap()
565 .as_vec()
566 .clone()[1..];
567 for m in maintainers {
568 assert!(
569 prep()
570 .await?
571 .iter_tags()
572 .any(|t| { t.as_vec()[0].eq("p") && t.as_vec()[1].eq(m) })
573 );
574 }
575 Ok(())
576 }
577
578 #[tokio::test]
579 #[serial]
580 async fn a_tag_for_repo_event() -> Result<()> {
581 assert!(prep().await?.tags.iter().any(|t| {
582 t.as_vec()[0].eq("a")
583 && t.as_vec()[1].eq(&format!(
584 "{REPOSITORY_KIND}:{TEST_KEY_1_PUBKEY_HEX}:{}",
585 generate_repo_ref_event().identifier().unwrap()
586 ))
587 }));
588 Ok(())
589 }
590
591 #[tokio::test]
592 #[serial]
564 async fn description_with_commit_message() -> Result<()> { 593 async fn description_with_commit_message() -> Result<()> {
565 assert_eq!( 594 assert_eq!(
566 prep() 595 prep()
@@ -612,18 +641,11 @@ mod sends_pr_and_2_patches_to_3_relays {
612 async fn patch_tags_pr_event_as_root() -> Result<()> { 641 async fn patch_tags_pr_event_as_root() -> Result<()> {
613 let (_, _, r53, r55, r56) = prep_run_create_pr().await?; 642 let (_, _, r53, r55, r56) = prep_run_create_pr().await?;
614 for relay in [&r53, &r55, &r56] { 643 for relay in [&r53, &r55, &r56] {
615 let patch_events: Vec<&nostr::Event> = relay 644 let patch_events: Vec<&nostr::Event> =
616 .events 645 relay.events.iter().filter(|e| is_patch(e)).collect();
617 .iter()
618 .filter(|e| e.kind.as_u64().eq(&PATCH_KIND))
619 .collect();
620 646
621 let most_recent_patch = patch_events[0]; 647 let most_recent_patch = patch_events[0];
622 let pr_event = relay 648 let pr_event = relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
623 .events
624 .iter()
625 .find(|e| e.kind.as_u64().eq(&PR_KIND))
626 .unwrap();
627 649
628 let root_event_tag = most_recent_patch 650 let root_event_tag = most_recent_patch
629 .tags 651 .tags
@@ -892,3 +914,8 @@ mod sends_pr_and_2_patches_to_3_relays {
892 } 914 }
893 } 915 }
894} 916}
917
918mod without_cover_letter {
919 use super::*;
920 // TODO
921}