diff options
| -rw-r--r-- | src/bin/git_remote_nostr/push.rs | 1 | ||||
| -rw-r--r-- | src/bin/ngit/sub_commands/send.rs | 137 | ||||
| -rw-r--r-- | src/lib/git_events.rs | 10 | ||||
| -rw-r--r-- | src/lib/push.rs | 3 | ||||
| -rw-r--r-- | src/lib/utils.rs | 36 | ||||
| -rw-r--r-- | test_utils/src/lib.rs | 2 | ||||
| -rw-r--r-- | tests/ngit_send.rs | 84 |
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}; | |||
| 4 | use console::Style; | 4 | use console::Style; |
| 5 | use ngit::{ | 5 | use 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 | }; |
| 9 | use nostr::{ToBech32, nips::nip19::Nip19Event}; | 11 | use nostr::{ToBech32, event::Event, nips::nip19::Nip19Event}; |
| 10 | use nostr_sdk::hashes::sha1::Hash as Sha1Hash; | 12 | use nostr_sdk::hashes::sha1::Hash as Sha1Hash; |
| 11 | 13 | ||
| 12 | use crate::{ | 14 | use 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 | ||
| 379 | async fn get_root_proposal_id_and_mentions_from_in_reply_to( | 422 | async 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)] | ||
| 379 | pub fn generate_unsigned_pr_or_update_event( | 380 | pub 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)] | ||
| 324 | pub async fn push_refs_and_generate_pr_or_pr_update_event( | 325 | pub 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 | ||
| 9 | use anyhow::{Context, Result, bail}; | 10 | use anyhow::{Context, Result, bail}; |
| 10 | use git2::Repository; | 11 | use git2::Repository; |
| 12 | use nostr::nips::nip19::ToBech32; | ||
| 11 | use nostr_sdk::{Event, EventId, Kind, PublicKey, Url}; | 13 | use nostr_sdk::{Event, EventId, Kind, PublicKey, Url}; |
| 12 | 14 | ||
| 13 | use crate::{ | 15 | use 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 | ||
| 193 | pub 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 | |||
| 190 | pub fn find_proposal_and_patches_by_branch_name<'a>( | 224 | pub 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 |
| 212 | pub fn get_pretend_proposal_root_event() -> nostr::Event { | 212 | pub 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(()) |