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>2025-08-04 11:50:39 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2025-08-05 09:23:01 +0100
commitf48677bad3f3dabb80992806e0e4c8ad4d45c716 (patch)
tree9d63debff0b602a15df56008cc739c087fbe8b26
parentf76fe63da5f2c2f85215e86c8ecc63eda7c93902 (diff)
feat(send): support PR and PR update events
send as a PR if the commit would make patches that are too big for nostr events. send as a PR update if the proposal is PR. send as a PR, revising a patch root, if patches would be too big. in tests `get_pretend_proposal_root_event` has to be a actual proposal with a tip, rather than just a cover letter, so we have replaced it.
-rw-r--r--src/bin/git_remote_nostr/push.rs1
-rw-r--r--src/bin/ngit/sub_commands/send.rs137
-rw-r--r--src/lib/git_events.rs10
-rw-r--r--src/lib/push.rs3
-rw-r--r--src/lib/utils.rs36
-rw-r--r--test_utils/src/lib.rs2
-rw-r--r--tests/ngit_send.rs84
7 files changed, 202 insertions, 71 deletions
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs
index e588a5a..3967699 100644
--- a/src/bin/git_remote_nostr/push.rs
+++ b/src/bin/git_remote_nostr/push.rs
@@ -461,6 +461,7 @@ async fn generate_patches_or_pr_event_or_pr_updates(
461 ahead.first().context("no commits to push")?, 461 ahead.first().context("no commits to push")?,
462 user_ref, 462 user_ref,
463 root_proposal, 463 root_proposal,
464 &None,
464 signer, 465 signer,
465 term, 466 term,
466 ) 467 )
diff --git a/src/bin/ngit/sub_commands/send.rs b/src/bin/ngit/sub_commands/send.rs
index 9f1857f..0aefb03 100644
--- a/src/bin/ngit/sub_commands/send.rs
+++ b/src/bin/ngit/sub_commands/send.rs
@@ -4,9 +4,11 @@ use anyhow::{Context, Result, bail};
4use console::Style; 4use console::Style;
5use ngit::{ 5use ngit::{
6 client::{Params, send_events}, 6 client::{Params, send_events},
7 git_events::{EventRefType, generate_cover_letter_and_patch_events}, 7 git_events::{EventRefType, KIND_PULL_REQUEST, generate_cover_letter_and_patch_events},
8 push::push_refs_and_generate_pr_or_pr_update_event,
9 utils::proposal_tip_is_pr_or_pr_update,
8}; 10};
9use nostr::{ToBech32, nips::nip19::Nip19Event}; 11use nostr::{ToBech32, event::Event, nips::nip19::Nip19Event};
10use nostr_sdk::hashes::sha1::Hash as Sha1Hash; 12use nostr_sdk::hashes::sha1::Hash as Sha1Hash;
11 13
12use crate::{ 14use crate::{
@@ -60,12 +62,14 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re
60 fetching_with_report(git_repo_path, &client, &repo_coordinates).await?; 62 fetching_with_report(git_repo_path, &client, &repo_coordinates).await?;
61 } 63 }
62 64
63 let (root_proposal_id, mention_tags) = 65 let repo_ref = get_repo_ref_from_cache(Some(git_repo_path), &repo_coordinates).await?;
64 get_root_proposal_id_and_mentions_from_in_reply_to(git_repo.get_path()?, &args.in_reply_to) 66
67 let (root_proposal, mention_tags) =
68 get_root_proposal_and_mentions_from_in_reply_to(git_repo.get_path()?, &args.in_reply_to)
65 .await?; 69 .await?;
66 70
67 if let Some(root_ref) = args.in_reply_to.first() { 71 if let Some(root_ref) = args.in_reply_to.first() {
68 if root_proposal_id.is_some() { 72 if root_proposal.is_some() {
69 println!("creating proposal revision for: {root_ref}"); 73 println!("creating proposal revision for: {root_ref}");
70 } 74 }
71 } 75 }
@@ -112,7 +116,30 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re
112 &main_tip, 116 &main_tip,
113 )?; 117 )?;
114 118
115 let title = if args.no_cover_letter { 119 let as_pr = {
120 if let Some(root_proposal) = &root_proposal {
121 proposal_tip_is_pr_or_pr_update(git_repo_path, &repo_ref, &root_proposal.id).await?
122 } else {
123 false
124 }
125 } || git_repo.are_commits_too_big_for_patches(&commits);
126
127 let title = if as_pr {
128 match &args.title {
129 Some(t) => Some(t.clone()),
130 None => {
131 if root_proposal.is_none() {
132 Some(
133 Interactor::default()
134 .input(PromptInputParms::default().with_prompt("title"))?
135 .clone(),
136 )
137 } else {
138 None
139 }
140 }
141 }
142 } else if args.no_cover_letter {
116 None 143 None
117 } else { 144 } else {
118 match &args.title { 145 match &args.title {
@@ -142,7 +169,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re
142 t.clone() 169 t.clone()
143 } else { 170 } else {
144 Interactor::default() 171 Interactor::default()
145 .input(PromptInputParms::default().with_prompt("cover letter description"))? 172 .input(PromptInputParms::default().with_prompt("description"))?
146 .clone() 173 .clone()
147 }, 174 },
148 )) 175 ))
@@ -161,42 +188,58 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re
161 188
162 client.set_signer(signer.clone()).await; 189 client.set_signer(signer.clone()).await;
163 190
164 let repo_ref = get_repo_ref_from_cache(Some(git_repo_path), &repo_coordinates).await?;
165
166 // oldest first 191 // oldest first
167 commits.reverse(); 192 commits.reverse();
168 193
169 let events = generate_cover_letter_and_patch_events( 194 let events = if as_pr {
170 cover_letter_title_description.clone(), 195 push_refs_and_generate_pr_or_pr_update_event(
171 &git_repo, 196 &git_repo,
172 &commits, 197 &repo_ref,
173 &signer, 198 commits.last().context("no commits")?,
174 &repo_ref, 199 &user_ref,
175 &root_proposal_id, 200 root_proposal.as_ref(),
176 &mention_tags, 201 &cover_letter_title_description,
177 ) 202 &signer,
178 .await?; 203 &console::Term::stdout(),
204 )
205 .await?
179 206
180 println!( 207 // TODO
181 "posting {} patch{} {} a covering letter...", 208 // - allow specifying clone url and ref
182 if cover_letter_title_description.is_none() { 209 } else {
183 events.len() 210 let events = generate_cover_letter_and_patch_events(
184 } else { 211 cover_letter_title_description.clone(),
185 events.len() - 1 212 &git_repo,
186 }, 213 &commits,
187 if cover_letter_title_description.is_none() && events.len().eq(&1) 214 &signer,
188 || cover_letter_title_description.is_some() && events.len().eq(&2) 215 &repo_ref,
189 { 216 &root_proposal.as_ref().map(|e| e.id.to_string()),
190 "" 217 &mention_tags,
191 } else { 218 )
192 "es" 219 .await?;
193 }, 220
194 if cover_letter_title_description.is_none() { 221 println!(
195 "without" 222 "posting {} patch{} {} a covering letter...",
196 } else { 223 if cover_letter_title_description.is_none() {
197 "with" 224 events.len()
198 } 225 } else {
199 ); 226 events.len() - 1
227 },
228 if cover_letter_title_description.is_none() && events.len().eq(&1)
229 || cover_letter_title_description.is_some() && events.len().eq(&2)
230 {
231 ""
232 } else {
233 "es"
234 },
235 if cover_letter_title_description.is_none() {
236 "without"
237 } else {
238 "with"
239 }
240 );
241 events
242 };
200 243
201 send_events( 244 send_events(
202 &client, 245 &client,
@@ -209,7 +252,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re
209 ) 252 )
210 .await?; 253 .await?;
211 254
212 if root_proposal_id.is_none() { 255 if root_proposal.is_none() {
213 if let Some(event) = events.first() { 256 if let Some(event) = events.first() {
214 let event_bech32 = if let Some(relay) = repo_ref.relays.first() { 257 let event_bech32 = if let Some(relay) = repo_ref.relays.first() {
215 Nip19Event { 258 Nip19Event {
@@ -376,11 +419,11 @@ fn summarise_commit_for_selection(git_repo: &Repo, commit: &Sha1Hash) -> Result<
376 )) 419 ))
377} 420}
378 421
379async fn get_root_proposal_id_and_mentions_from_in_reply_to( 422async fn get_root_proposal_and_mentions_from_in_reply_to(
380 git_repo_path: &Path, 423 git_repo_path: &Path,
381 in_reply_to: &[String], 424 in_reply_to: &[String],
382) -> Result<(Option<String>, Vec<nostr::Tag>)> { 425) -> Result<(Option<Event>, Vec<nostr::Tag>)> {
383 let root_proposal_id = if let Some(first) = in_reply_to.first() { 426 let root_proposal = if let Some(first) = in_reply_to.first() {
384 match event_tag_from_nip19_or_hex(first, "in-reply-to", EventRefType::Root, true, false)? 427 match event_tag_from_nip19_or_hex(first, "in-reply-to", EventRefType::Root, true, false)?
385 .as_standardized() 428 .as_standardized()
386 { 429 {
@@ -398,8 +441,8 @@ async fn get_root_proposal_id_and_mentions_from_in_reply_to(
398 .await?; 441 .await?;
399 442
400 if let Some(first) = events.iter().find(|e| e.id.eq(event_id)) { 443 if let Some(first) = events.iter().find(|e| e.id.eq(event_id)) {
401 if event_is_patch_set_root(first) { 444 if event_is_patch_set_root(first) || first.kind.eq(&KIND_PULL_REQUEST) {
402 Some(event_id.to_string()) 445 Some(first.clone())
403 } else { 446 } else {
404 None 447 None
405 } 448 }
@@ -415,7 +458,7 @@ async fn get_root_proposal_id_and_mentions_from_in_reply_to(
415 458
416 let mut mention_tags = vec![]; 459 let mut mention_tags = vec![];
417 for (i, reply_to) in in_reply_to.iter().enumerate() { 460 for (i, reply_to) in in_reply_to.iter().enumerate() {
418 if i.ne(&0) || root_proposal_id.is_none() { 461 if i.ne(&0) || root_proposal.is_none() {
419 mention_tags.push( 462 mention_tags.push(
420 event_tag_from_nip19_or_hex( 463 event_tag_from_nip19_or_hex(
421 reply_to, 464 reply_to,
@@ -431,7 +474,7 @@ async fn get_root_proposal_id_and_mentions_from_in_reply_to(
431 } 474 }
432 } 475 }
433 476
434 Ok((root_proposal_id, mention_tags)) 477 Ok((root_proposal, mention_tags))
435} 478}
436 479
437// TODO 480// TODO
diff --git a/src/lib/git_events.rs b/src/lib/git_events.rs
index 79f5772..bbfcbea 100644
--- a/src/lib/git_events.rs
+++ b/src/lib/git_events.rs
@@ -376,11 +376,13 @@ pub fn event_tag_from_nip19_or_hex(
376 } 376 }
377} 377}
378 378
379#[allow(clippy::too_many_arguments)]
379pub fn generate_unsigned_pr_or_update_event( 380pub fn generate_unsigned_pr_or_update_event(
380 git_repo: &Repo, 381 git_repo: &Repo,
381 repo_ref: &RepoRef, 382 repo_ref: &RepoRef,
382 signing_public_key: &PublicKey, 383 signing_public_key: &PublicKey,
383 root_proposal: Option<&Event>, 384 root_proposal: Option<&Event>,
385 title_description_overide: &Option<(String, String)>,
384 commit: &Sha1Hash, 386 commit: &Sha1Hash,
385 clone_url_hint: &[&str], 387 clone_url_hint: &[&str],
386 mentions: &[nostr::Tag], 388 mentions: &[nostr::Tag],
@@ -395,13 +397,17 @@ pub fn generate_unsigned_pr_or_update_event(
395 None 397 None
396 }; 398 };
397 399
398 let title = if let Some(cl) = &root_patch_cover_letter { 400 let title = if let Some((title, _)) = &title_description_overide {
401 title.clone()
402 } else if let Some(cl) = &root_patch_cover_letter {
399 cl.title.clone() 403 cl.title.clone()
400 } else { 404 } else {
401 git_repo.get_commit_message_summary(commit)? 405 git_repo.get_commit_message_summary(commit)?
402 }; 406 };
403 407
404 let description = if let Some(cl) = &root_patch_cover_letter { 408 let description = if let Some((_, description)) = &title_description_overide {
409 description.clone()
410 } else if let Some(cl) = &root_patch_cover_letter {
405 cl.description.clone() 411 cl.description.clone()
406 } else { 412 } else {
407 let mut description = git_repo.get_commit_message(commit)?.trim().to_string(); 413 let mut description = git_repo.get_commit_message(commit)?.trim().to_string();
diff --git a/src/lib/push.rs b/src/lib/push.rs
index 1c09555..bcd368b 100644
--- a/src/lib/push.rs
+++ b/src/lib/push.rs
@@ -321,12 +321,14 @@ impl<'a> PushReporter<'a> {
321 } 321 }
322} 322}
323 323
324#[allow(clippy::too_many_arguments)]
324pub async fn push_refs_and_generate_pr_or_pr_update_event( 325pub async fn push_refs_and_generate_pr_or_pr_update_event(
325 git_repo: &Repo, 326 git_repo: &Repo,
326 repo_ref: &RepoRef, 327 repo_ref: &RepoRef,
327 tip: &Sha1Hash, 328 tip: &Sha1Hash,
328 user_ref: &UserRef, 329 user_ref: &UserRef,
329 root_proposal: Option<&Event>, 330 root_proposal: Option<&Event>,
331 title_description_overide: &Option<(String, String)>,
330 signer: &Arc<dyn NostrSigner>, 332 signer: &Arc<dyn NostrSigner>,
331 term: &Term, 333 term: &Term,
332) -> Result<Vec<Event>> { 334) -> Result<Vec<Event>> {
@@ -348,6 +350,7 @@ pub async fn push_refs_and_generate_pr_or_pr_update_event(
348 repo_ref, 350 repo_ref,
349 &user_ref.public_key, 351 &user_ref.public_key,
350 root_proposal, 352 root_proposal,
353 title_description_overide,
351 tip, 354 tip,
352 &[clone_url], 355 &[clone_url],
353 &[], 356 &[],
diff --git a/src/lib/utils.rs b/src/lib/utils.rs
index 431757f..431a14f 100644
--- a/src/lib/utils.rs
+++ b/src/lib/utils.rs
@@ -3,11 +3,13 @@ use std::{
3 collections::HashMap, 3 collections::HashMap,
4 fmt, 4 fmt,
5 io::{self, Stdin}, 5 io::{self, Stdin},
6 path::Path,
6 str::FromStr, 7 str::FromStr,
7}; 8};
8 9
9use anyhow::{Context, Result, bail}; 10use anyhow::{Context, Result, bail};
10use git2::Repository; 11use git2::Repository;
12use nostr::nips::nip19::ToBech32;
11use nostr_sdk::{Event, EventId, Kind, PublicKey, Url}; 13use nostr_sdk::{Event, EventId, Kind, PublicKey, Url};
12 14
13use crate::{ 15use crate::{
@@ -20,7 +22,8 @@ use crate::{
20 nostr_url::{CloneUrl, NostrUrlDecoded, ServerProtocol}, 22 nostr_url::{CloneUrl, NostrUrlDecoded, ServerProtocol},
21 }, 23 },
22 git_events::{ 24 git_events::{
23 event_is_revision_root, get_pr_tip_event_or_most_recent_patch_with_ancestors, get_status, 25 KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, event_is_revision_root,
26 get_pr_tip_event_or_most_recent_patch_with_ancestors, get_status,
24 is_event_proposal_root_for_branch, status_kinds, 27 is_event_proposal_root_for_branch, status_kinds,
25 }, 28 },
26 repo_ref::RepoRef, 29 repo_ref::RepoRef,
@@ -187,6 +190,37 @@ pub async fn get_all_proposals(
187 Ok(all_proposals) 190 Ok(all_proposals)
188} 191}
189 192
193pub async fn proposal_tip_is_pr_or_pr_update(
194 git_repo_path: &Path,
195 repo_ref: &RepoRef,
196 proposal_id: &EventId,
197) -> Result<bool> {
198 let commits_events =
199 get_all_proposal_patch_pr_pr_update_events_from_cache(git_repo_path, repo_ref, proposal_id)
200 .await
201 .context(format!(
202 "cannot get existing proposal events for {}",
203 proposal_id.to_bech32()?
204 ))?;
205 let most_recent_proposal_patch_chain = get_pr_tip_event_or_most_recent_patch_with_ancestors(
206 commits_events.clone(),
207 )
208 .context(format!(
209 "cannot find tip from proposal events for {}",
210 proposal_id.to_bech32()?,
211 ))?;
212
213 Ok([KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE].contains(
214 &most_recent_proposal_patch_chain
215 .first()
216 .context(format!(
217 "cannot find any proposal events for {}",
218 proposal_id.to_bech32()?
219 ))?
220 .kind,
221 ))
222}
223
190pub fn find_proposal_and_patches_by_branch_name<'a>( 224pub fn find_proposal_and_patches_by_branch_name<'a>(
191 refstr: &'a str, 225 refstr: &'a str,
192 proposals: &'a HashMap<EventId, (Event, Vec<Event>)>, 226 proposals: &'a HashMap<EventId, (Event, Vec<Event>)>,
diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs
index 3ae004f..12cac76 100644
--- a/test_utils/src/lib.rs
+++ b/test_utils/src/lib.rs
@@ -210,7 +210,7 @@ pub fn generate_repo_ref_event_with_git_server_with_keys(
210} 210}
211/// enough to fool event_is_patch_set_root 211/// enough to fool event_is_patch_set_root
212pub fn get_pretend_proposal_root_event() -> nostr::Event { 212pub fn get_pretend_proposal_root_event() -> nostr::Event {
213 serde_json::from_str(r#"{"id":"431e58eb8e1b4e20292d1d5bbe81d5cfb042e1bc165de32eddfdd52245a4cce4","pubkey":"f53e4bcd7a9cdef049cf6467d638a1321958acd3b71eb09823fd6fadb023d768","created_at":1721404213,"kind":1617,"tags":[["a","30617:ba882566eff14f3baa976103998c452d27fe95b65a796a6a9f92628bced76fe5:9ee507fc4357d7ee16a5d8901bedcd103f23c17d-consider-it-random"],["a","30617:f53e4bcd7a9cdef049cf6467d638a1321958acd3b71eb09823fd6fadb023d768:9ee507fc4357d7ee16a5d8901bedcd103f23c17d-consider-it-random"],["r","9ee507fc4357d7ee16a5d8901bedcd103f23c17d"],["t","cover-letter"],["alt","git patch cover letter: exampletitle"],["t","root"],["e","8cb75aa4cda10a3a0f3242dc49d36159d30b3185bf63414cf6ce17f5c14a73b1","","mention"],["branch-name","feature"],["p","ba882566eff14f3baa976103998c452d27fe95b65a796a6a9f92628bced76fe5"],["p","f53e4bcd7a9cdef049cf6467d638a1321958acd3b71eb09823fd6fadb023d768"]],"content":"From fe973a840fba2a8ab37dd505c154854a69a6505c Mon Sep 17 00:00:00 2001\nSubject: [PATCH 0/2] exampletitle\n\nexampledescription","sig":"37d5b2338bf9fd9d598e6494ae88af9a8dbd52330cfe9d025ee55e35e2f3f55e931ba039d9f7fed8e6fc40206e47619a24f730f8eddc2a07ccfb3988a5005170"}"#).unwrap() 213 serde_json::from_str(r#"{"id":"000c104861e34a453481ab23e7de21a6baf475b394479705363b035936732528","pubkey":"f53e4bcd7a9cdef049cf6467d638a1321958acd3b71eb09823fd6fadb023d768","created_at":1754322009,"kind":1617,"tags":[["a","30617:f53e4bcd7a9cdef049cf6467d638a1321958acd3b71eb09823fd6fadb023d768:9ee507fc4357d7ee16a5d8901bedcd103f23c17d-consider-it-random","ws://localhost:8055"],["a","30617:ba882566eff14f3baa976103998c452d27fe95b65a796a6a9f92628bced76fe5:9ee507fc4357d7ee16a5d8901bedcd103f23c17d-consider-it-random","ws://localhost:8055"],["r","9ee507fc4357d7ee16a5d8901bedcd103f23c17d"],["r","232efb37ebc67692c9e9ff58b83c0d3d63971a0a"],["alt","git patch: add t3.md"],["t","root"],["branch-name","feature"],["p","ba882566eff14f3baa976103998c452d27fe95b65a796a6a9f92628bced76fe5"],["commit","232efb37ebc67692c9e9ff58b83c0d3d63971a0a"],["parent-commit","431b84edc0d2fa118d63faa3c2db9c73d630a5ae"],["commit-pgp-sig",""],["description","add t3.md"],["author","Joe Bloggs","joe.bloggs@pm.me","0","0"],["committer","Joe Bloggs","joe.bloggs@pm.me","0","0"]],"content":"From 232efb37ebc67692c9e9ff58b83c0d3d63971a0a Mon Sep 17 00:00:00 2001\nFrom: Joe Bloggs <joe.bloggs@pm.me>\nDate: Thu, 1 Jan 1970 00:00:00 +0000\nSubject: [PATCH 1/2] add t3.md\n\n---\n t3.md | 1 +\n 1 file changed, 1 insertion(+)\n create mode 100644 t3.md\n\ndiff --git a/t3.md b/t3.md\nnew file mode 100644\nindex 0000000..f0eec86\n--- /dev/null\n+++ b/t3.md\n@@ -0,0 +1 @@\n+some content\n\\ No newline at end of file\n--\nlibgit2 1.9.1\n\n","sig":"65577fea803ea464bb073273a3fbfbdb5bfdaa64fb3b1d029ee8f3729fde051ad90610d08e441335f365b6c1d6f2270909bc37d12433ca82f0b2928b7a503e31"}"#).unwrap()
214} 214}
215 215
216/// wrapper for a cli testing tool - currently wraps rexpect and dialoguer 216/// wrapper for a cli testing tool - currently wraps rexpect and dialoguer
diff --git a/tests/ngit_send.rs b/tests/ngit_send.rs
index ec72667..e128bd9 100644
--- a/tests/ngit_send.rs
+++ b/tests/ngit_send.rs
@@ -37,6 +37,25 @@ mod when_commits_behind_ask_to_proceed {
37 Ok(test_repo) 37 Ok(test_repo)
38 } 38 }
39 39
40 fn create_relay_51() -> Result<Relay<'static>> {
41 Ok(Relay::new(
42 8051,
43 None,
44 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
45 relay.respond_events(
46 client_id,
47 &subscription_id,
48 &vec![
49 generate_test_key_1_metadata_event("fred"),
50 generate_test_key_1_relay_list_event(),
51 generate_repo_ref_event(),
52 ],
53 )?;
54 Ok(())
55 }),
56 ))
57 }
58
40 fn expect_confirm_prompt(p: &mut CliTester) -> Result<CliTesterConfirmPrompt> { 59 fn expect_confirm_prompt(p: &mut CliTester) -> Result<CliTesterConfirmPrompt> {
41 p.expect("fetching updates...\r\n")?; 60 p.expect("fetching updates...\r\n")?;
42 p.expect_eventually("\r\n")?; // may be 'no updates' or some updates 61 p.expect_eventually("\r\n")?; // may be 'no updates' or some updates
@@ -49,37 +68,62 @@ mod when_commits_behind_ask_to_proceed {
49 ) 68 )
50 } 69 }
51 70
52 #[test] 71 #[tokio::test]
53 fn asked_with_default_no() -> Result<()> { 72 #[serial]
73 async fn asked_with_default_no() -> Result<()> {
54 let test_repo = prep_test_repo()?; 74 let test_repo = prep_test_repo()?;
75 let mut r51 = create_relay_51()?;
76 // // check relay had the right number of events
77 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
78 let mut p = CliTester::new_from_dir(&test_repo.dir, ["send", "HEAD~2"]);
79 expect_confirm_prompt(&mut p)?;
80 p.exit()?;
81 relay::shutdown_relay(8051)?;
82 Ok(())
83 });
55 84
56 let mut p = CliTester::new_from_dir(&test_repo.dir, ["send", "HEAD~2"]); 85 // launch relay
57 expect_confirm_prompt(&mut p)?; 86 r51.listen_until_close().await?;
58 p.exit()?; 87 cli_tester_handle.join().unwrap()?;
59 Ok(()) 88 Ok(())
60 } 89 }
61 90
62 #[test] 91 #[tokio::test]
63 fn when_response_is_false_aborts() -> Result<()> { 92 #[serial]
93 async fn when_response_is_false_aborts() -> Result<()> {
64 let test_repo = prep_test_repo()?; 94 let test_repo = prep_test_repo()?;
95 let mut r51 = create_relay_51()?;
96 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
97 let mut p = CliTester::new_from_dir(&test_repo.dir, ["send", "HEAD~2"]);
98 expect_confirm_prompt(&mut p)?.succeeds_with(Some(false))?;
99 p.expect_end_with("Error: aborting so commits can be rebased\r\n")?;
100 relay::shutdown_relay(8051)?;
101 Ok(())
102 });
65 103
66 let mut p = CliTester::new_from_dir(&test_repo.dir, ["send", "HEAD~2"]); 104 // launch relay
67 105 r51.listen_until_close().await?;
68 expect_confirm_prompt(&mut p)?.succeeds_with(Some(false))?; 106 cli_tester_handle.join().unwrap()?;
69
70 p.expect_end_with("Error: aborting so commits can be rebased\r\n")?;
71
72 Ok(()) 107 Ok(())
73 } 108 }
74 #[test] 109
110 #[tokio::test]
75 #[serial] 111 #[serial]
76 fn when_response_is_true_proceeds() -> Result<()> { 112 async fn when_response_is_true_proceeds() -> Result<()> {
77 let test_repo = prep_test_repo()?; 113 let test_repo = prep_test_repo()?;
114 let mut r51 = create_relay_51()?;
115 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
116 let mut p = CliTester::new_from_dir(&test_repo.dir, ["send", "HEAD~2"]);
117 expect_confirm_prompt(&mut p)?.succeeds_with(Some(true))?;
118 p.expect("? include cover letter")?;
119 p.exit()?;
120 relay::shutdown_relay(8051)?;
121 Ok(())
122 });
78 123
79 let mut p = CliTester::new_from_dir(&test_repo.dir, ["send", "HEAD~2"]); 124 // launch relay
80 expect_confirm_prompt(&mut p)?.succeeds_with(Some(true))?; 125 r51.listen_until_close().await?;
81 p.expect("? include cover letter")?; 126 cli_tester_handle.join().unwrap()?;
82 p.exit()?;
83 Ok(()) 127 Ok(())
84 } 128 }
85} 129}
@@ -1620,7 +1664,7 @@ mod root_proposal_specified_using_in_reply_to_with_range_of_head_2_and_cover_let
1620 .unwrap() 1664 .unwrap()
1621 .as_slice()[1], 1665 .as_slice()[1],
1622 // id of state nevent 1666 // id of state nevent
1623 "431e58eb8e1b4e20292d1d5bbe81d5cfb042e1bc165de32eddfdd52245a4cce4", 1667 "000c104861e34a453481ab23e7de21a6baf475b394479705363b035936732528",
1624 ); 1668 );
1625 } 1669 }
1626 Ok(()) 1670 Ok(())