From fdc15cb017b022a3b932ac5a337c649cb63df93c Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Thu, 22 Feb 2024 10:11:39 +0000 Subject: fix(list): support `--in-reply-to` latest revision update list to support rebases via proposal revisions as created by `ngit send --in-reply-to` or upcoming change `ngit push --force` --- Cargo.lock | 1 + src/sub_commands/list.rs | 171 +++++++++++++++----------- src/sub_commands/push.rs | 35 ++++-- src/sub_commands/send.rs | 5 + test_utils/Cargo.toml | 1 + tests/list.rs | 311 ++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 445 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41d8882..a4df86f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2817,6 +2817,7 @@ dependencies = [ "directories", "git2", "nostr", + "nostr-sdk", "once_cell", "rand", "rexpect 0.5.0 (git+https://github.com/rust-cli/rexpect.git?rev=9eb61dd)", diff --git a/src/sub_commands/list.rs b/src/sub_commands/list.rs index b556d5a..88ad52f 100644 --- a/src/sub_commands/list.rs +++ b/src/sub_commands/list.rs @@ -13,8 +13,8 @@ use crate::{ git::{str_to_sha1, Repo, RepoActions}, repo_ref::{self, RepoRef, REPO_REF_KIND}, sub_commands::send::{ - commit_msg_from_patch_oneliner, event_is_cover_letter, event_to_cover_letter, - patch_supports_commit_ids, PATCH_KIND, + commit_msg_from_patch_oneliner, event_is_cover_letter, event_is_revision_root, + event_to_cover_letter, patch_supports_commit_ids, PATCH_KIND, }, Cli, }; @@ -53,13 +53,17 @@ pub async fn launch(_cli_args: &Cli, _args: &SubCommandArgs) -> Result<()> { println!("finding proposals..."); - let proposal_events: Vec = + let proposal_events_and_revisions: Vec = find_proposal_events(&client, &repo_ref, &root_commit.to_string()).await?; - if proposal_events.is_empty() { + if proposal_events_and_revisions.is_empty() { println!("no proposals found... create one? try `ngit send`"); return Ok(()); } + let proposal_events: Vec<&nostr::Event> = proposal_events_and_revisions + .iter() + .filter(|e| !event_is_revision_root(e)) + .collect::>(); loop { let selected_index = Interactor::default().choice( @@ -81,17 +85,32 @@ pub async fn launch(_cli_args: &Cli, _args: &SubCommandArgs) -> Result<()> { ), )?; - let cover_letter = event_to_cover_letter(&proposal_events[selected_index]) + let cover_letter = event_to_cover_letter(proposal_events[selected_index]) .context("cannot extract proposal details from proposal root event")?; println!("finding commits..."); - let commits_events: Vec = find_commits_for_proposal_root_event( + let commits_events: Vec = find_commits_for_proposal_root_events( &client, - &proposal_events[selected_index], + &[ + vec![proposal_events[selected_index]], + proposal_events_and_revisions + .iter() + .filter(|e| { + e.tags.iter().any(|t| { + t.as_vec().len().gt(&1) + && t.as_vec()[1].eq(&proposal_events[selected_index].id.to_string()) + }) + }) + .collect::>(), + ] + .concat(), &repo_ref, ) .await?; + // for commit in &commits_events { + // println!("cevent: {:?}", commit.as_json()); + // } let Ok(most_recent_proposal_patch_chain) = get_most_recent_patch_with_ancestors(commits_events.clone()) @@ -107,6 +126,9 @@ pub async fn launch(_cli_args: &Cli, _args: &SubCommandArgs) -> Result<()> { } return Ok(()); }; + // for commit in &most_recent_proposal_patch_chain { + // println!("recent_event: {:?}", commit.as_json()); + // } let binding_patch_text_ref = format!("{} commits", most_recent_proposal_patch_chain.len()); let patch_text_ref = if most_recent_proposal_patch_chain.len().gt(&1) { @@ -354,59 +376,63 @@ pub async fn launch(_cli_args: &Cli, _args: &SubCommandArgs) -> Result<()> { .unwrap_or_default() .eq(&local_branch_tip.to_string()) }) { + println!( + "updated proposal available ({} ahead {} behind '{main_branch_name}'). existing version is {} ahead {} behind '{main_branch_name}'", + most_recent_proposal_patch_chain.len(), + proposal_behind_main.len(), + local_ahead_of_main.len(), + local_beind_main.len(), + ); return match Interactor::default().choice( - PromptChoiceParms::default().with_default(0) - .with_choices( - vec![ - format!( - "checkout new version of proposal branch ({} ahead {} behind '{main_branch_name}'), replacing old version ({} ahead {} behind '{main_branch_name}')", - most_recent_proposal_patch_chain.len(), - proposal_behind_main.len(), - local_ahead_of_main.len(), - local_beind_main.len(), - ), - format!( - "checkout existing outdated proposal branch ({} ahead {} behind '{main_branch_name}')", - local_ahead_of_main.len(), - local_beind_main.len(), - ), - format!("apply to current branch with `git am`"), - format!("download to ./patches"), - "back".to_string(), - ], - ) + PromptChoiceParms::default() + .with_default(0) + .with_choices(vec![ + format!("checkout and overwrite existing proposal branch"), + format!("checkout existing outdated proposal branch"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + "back".to_string(), + ]), )? { - 0 => { - check_clean(&git_repo)?; - git_repo.create_branch_at_commit(&cover_letter.branch_name, &proposal_base_commit.to_string())?; - git_repo.checkout(&cover_letter.branch_name)?; - let chain_length = most_recent_proposal_patch_chain.len(); - let _ = git_repo - .apply_patch_chain(&cover_letter.branch_name, most_recent_proposal_patch_chain) - .context("cannot apply patch chain")?; - format!( - "checked out new version of proposal ({} ahead {} behind '{main_branch_name}'), replacing old version ({} ahead {} behind '{main_branch_name}')", - chain_length, - proposal_behind_main.len(), - local_ahead_of_main.len(), - local_beind_main.len(), - ); - Ok(()) - }, - 1 => { - check_clean(&git_repo)?; - git_repo.checkout(&cover_letter.branch_name)?; - format!( - "checked out old proposal in existing branch ({} ahead {} behind '{main_branch_name}')", - local_ahead_of_main.len(), - local_beind_main.len(), - ); - Ok(()) - }, - 2 => {launch_git_am_with_patches(most_recent_proposal_patch_chain)}, - 3 => {save_patches_to_dir(most_recent_proposal_patch_chain, &git_repo)}, - 4 => { continue }, - _ => { bail!("unexpected choice")} + 0 => { + check_clean(&git_repo)?; + git_repo.create_branch_at_commit( + &cover_letter.branch_name, + &proposal_base_commit.to_string(), + )?; + git_repo.checkout(&cover_letter.branch_name)?; + let chain_length = most_recent_proposal_patch_chain.len(); + let _ = git_repo + .apply_patch_chain( + &cover_letter.branch_name, + most_recent_proposal_patch_chain, + ) + .context("cannot apply patch chain")?; + println!( + "checked out new version of proposal ({} ahead {} behind '{main_branch_name}'), replacing old version ({} ahead {} behind '{main_branch_name}')", + chain_length, + proposal_behind_main.len(), + local_ahead_of_main.len(), + local_beind_main.len(), + ); + Ok(()) + } + 1 => { + check_clean(&git_repo)?; + git_repo.checkout(&cover_letter.branch_name)?; + println!( + "checked out old proposal in existing branch ({} ahead {} behind '{main_branch_name}')", + local_ahead_of_main.len(), + local_beind_main.len(), + ); + Ok(()) + } + 2 => launch_git_am_with_patches(most_recent_proposal_patch_chain), + 3 => save_patches_to_dir(most_recent_proposal_patch_chain, &git_repo), + 4 => continue, + _ => { + bail!("unexpected choice") + } }; } @@ -699,11 +725,11 @@ pub fn get_most_recent_patch_with_ancestors( ) -> Result> { patches.sort_by_key(|e| e.created_at); - let first_patch = patches.first().context("no patches found")?; + let youngest_patch = patches.last().context("no patches found")?; let patches_with_youngest_created_at: Vec<&nostr::Event> = patches .iter() - .filter(|p| p.created_at.eq(&first_patch.created_at)) + .filter(|p| p.created_at.eq(&youngest_patch.created_at)) .collect(); let mut res = vec![]; @@ -787,10 +813,10 @@ pub async fn find_proposal_events( .collect::>()) } -pub async fn find_commits_for_proposal_root_event( +pub async fn find_commits_for_proposal_root_events( #[cfg(test)] client: &crate::client::MockConnect, #[cfg(not(test))] client: &Client, - proposal_root_event: &nostr::Event, + proposal_root_events: &Vec<&nostr::Event>, repo_ref: &RepoRef, ) -> Result> { let mut patch_events: Vec = client @@ -799,10 +825,12 @@ pub async fn find_commits_for_proposal_root_event( vec![ nostr::Filter::default() .kind(nostr::Kind::Custom(PATCH_KIND)) - // this requires every patch to reference the root event - // this will not pick up v2,v3 patch sets - // TODO: fetch commits for v2.. patch sets - .event(proposal_root_event.id), + .events( + proposal_root_events + .iter() + .map(|e| e.id) + .collect::>(), + ), ], ) .await @@ -811,14 +839,19 @@ pub async fn find_commits_for_proposal_root_event( .filter(|e| { e.kind.as_u64() == PATCH_KIND && e.tags.iter().any(|t| { - t.as_vec().len() > 2 && t.as_vec()[1].eq(&proposal_root_event.id.to_string()) + t.as_vec().len() > 2 + && proposal_root_events + .iter() + .any(|e| t.as_vec()[1].eq(&e.id.to_string())) }) }) .map(std::borrow::ToOwned::to_owned) .collect(); - if !event_is_cover_letter(proposal_root_event) { - patch_events.push(proposal_root_event.clone()); + for e in proposal_root_events { + if !event_is_cover_letter(e) && !patch_events.iter().any(|e2| e2.id.eq(&e.id)) { + patch_events.push(e.to_owned().clone()); + } } Ok(patch_events) } diff --git a/src/sub_commands/push.rs b/src/sub_commands/push.rs index 73bdb38..dd32b2c 100644 --- a/src/sub_commands/push.rs +++ b/src/sub_commands/push.rs @@ -12,10 +12,10 @@ use crate::{ repo_ref::{self, RepoRef}, sub_commands::{ list::{ - find_commits_for_proposal_root_event, find_proposal_events, get_commit_id_from_patch, + find_commits_for_proposal_root_events, find_proposal_events, get_commit_id_from_patch, get_most_recent_patch_with_ancestors, tag_value, }, - send::{event_to_cover_letter, generate_patch_event, send_events}, + send::{event_is_revision_root, event_to_cover_letter, generate_patch_event, send_events}, }, Cli, }; @@ -149,12 +149,17 @@ pub async fn fetch_proposal_root_and_most_recent_patch_chain( ) -> Result<(nostr::Event, Vec)> { println!("finding proposal root event..."); - let proposal_events: Vec = + let proposal_events_and_revisions: Vec = find_proposal_events(client, repo_ref, &root_commit.to_string()) .await .context("cannot get proposal events for repo")?; - let proposal_root_event: nostr::Event = proposal_events + let proposal_events: Vec<&nostr::Event> = proposal_events_and_revisions + .iter() + .filter(|e| !event_is_revision_root(e)) + .collect::>(); + + let proposal_root_event: &nostr::Event = proposal_events .iter() .find(|e| { event_to_cover_letter(e).is_ok_and(|cl| cl.branch_name.eq(branch_name)) @@ -166,8 +171,24 @@ pub async fn fetch_proposal_root_and_most_recent_patch_chain( println!("found proposal root event. finding commits..."); - let commits_events: Vec = - find_commits_for_proposal_root_event(client, &proposal_root_event, repo_ref).await?; + let commits_events: Vec = find_commits_for_proposal_root_events( + client, + &[ + vec![proposal_root_event], + proposal_events_and_revisions + .iter() + .filter(|e| { + e.tags.iter().any(|t| { + t.as_vec().len().gt(&1) + && t.as_vec()[1].eq(&proposal_root_event.id.to_string()) + }) + }) + .collect::>(), + ] + .concat(), + repo_ref, + ) + .await?; - Ok((proposal_root_event, commits_events)) + Ok((proposal_root_event.clone(), commits_events)) } diff --git a/src/sub_commands/send.rs b/src/sub_commands/send.rs index c9c81ee..1ccb1f4 100644 --- a/src/sub_commands/send.rs +++ b/src/sub_commands/send.rs @@ -613,6 +613,11 @@ pub fn event_is_patch_set_root(event: &nostr::Event) -> bool { event.kind.as_u64().eq(&PATCH_KIND) && event.iter_tags().any(|t| t.as_vec()[1].eq("root")) } +pub fn event_is_revision_root(event: &nostr::Event) -> bool { + event.kind.as_u64().eq(&PATCH_KIND) + && event.iter_tags().any(|t| t.as_vec()[1].eq("revision-root")) +} + pub fn patch_supports_commit_ids(event: &nostr::Event) -> bool { event.kind.as_u64().eq(&PATCH_KIND) && event diff --git a/test_utils/Cargo.toml b/test_utils/Cargo.toml index adb8909..8928f67 100644 --- a/test_utils/Cargo.toml +++ b/test_utils/Cargo.toml @@ -10,6 +10,7 @@ dialoguer = "0.10.4" directories = "5.0.1" git2 = "0.18.1" nostr = "0.27.0" +nostr-sdk = { version = "0.27.0", features = ["blocking"] } once_cell = "1.18.0" rand = "0.8" rexpect = { git = "https://github.com/rust-cli/rexpect.git", rev = "9eb61dd" } diff --git a/tests/list.rs b/tests/list.rs index 7762b1c..eb6dc3d 100644 --- a/tests/list.rs +++ b/tests/list.rs @@ -1,5 +1,6 @@ use anyhow::Result; use futures::join; +use nostr_sdk::client::blocking::Client; use serial_test::serial; use test_utils::{git::GitTestRepo, relay::Relay, *}; @@ -20,18 +21,21 @@ fn cli_tester_create_proposals() -> Result { FEATURE_BRANCH_NAME_1, "a", Some((PROPOSAL_TITLE_1, "proposal a description")), + None, )?; cli_tester_create_proposal( &git_repo, FEATURE_BRANCH_NAME_2, "b", Some((PROPOSAL_TITLE_2, "proposal b description")), + None, )?; cli_tester_create_proposal( &git_repo, FEATURE_BRANCH_NAME_3, "c", Some((PROPOSAL_TITLE_3, "proposal c description")), + None, )?; Ok(git_repo) } @@ -65,10 +69,27 @@ fn cli_tester_create_proposal( branch_name: &str, prefix: &str, cover_letter_title_and_description: Option<(&str, &str)>, + in_reply_to: Option, ) -> Result<()> { create_and_populate_branch(test_repo, branch_name, prefix, false)?; - if let Some((title, description)) = cover_letter_title_and_description { + if let Some(in_reply_to) = in_reply_to { + let mut p = CliTester::new_from_dir( + &test_repo.dir, + [ + "--nsec", + TEST_KEY_1_NSEC, + "--password", + TEST_PASSWORD, + "--disable-cli-spinners", + "send", + "--no-cover-letter", + "--in-reply-to", + in_reply_to.as_str(), + ], + ); + p.expect_end_eventually()?; + } else if let Some((title, description)) = cover_letter_title_and_description { let mut p = CliTester::new_from_dir( &test_repo.dir, [ @@ -636,6 +657,7 @@ mod when_main_branch_is_uptodate { FEATURE_BRANCH_NAME_4, "d", None, + None, )?; let test_repo = GitTestRepo::default(); test_repo.populate()?; @@ -715,6 +737,7 @@ mod when_main_branch_is_uptodate { FEATURE_BRANCH_NAME_4, "d", None, + None, )?; let test_repo = GitTestRepo::default(); test_repo.populate()?; @@ -1193,8 +1216,290 @@ mod when_main_branch_is_uptodate { } mod when_latest_event_rebases_branch { - // use super::*; - // TODO + use std::time::Duration; + + use super::*; + + async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> { + // fallback (51,52) user write (53, 55) repo (55, 56) + let (mut r51, mut r52, mut r53, mut r55, mut r56) = ( + Relay::new(8051, None, None), + Relay::new(8052, None, None), + Relay::new(8053, None, None), + Relay::new(8055, None, None), + Relay::new(8056, None, None), + ); + + r51.events.push(generate_test_key_1_relay_list_event()); + r51.events.push(generate_test_key_1_metadata_event("fred")); + r51.events.push(generate_repo_ref_event()); + + r55.events.push(generate_repo_ref_event()); + r55.events.push(generate_test_key_1_metadata_event("fred")); + r55.events.push(generate_test_key_1_relay_list_event()); + + let cli_tester_handle = std::thread::spawn( + move || -> Result<(GitTestRepo, GitTestRepo)> { + // create 3 proposals + let _ = cli_tester_create_proposals()?; + // get proposal id of first + let client = Client::new(&nostr::Keys::generate()); + client.add_relay("ws://localhost:8055")?; + client.connect_relay("ws://localhost:8055")?; + let proposals = client.get_events_of( + vec![ + nostr::Filter::default() + .kind(nostr::Kind::Custom(PATCH_KIND)) + .custom_tag(nostr::Alphabet::T, vec!["root"]), + ], + Some(Duration::from_millis(500)), + )?; + client.disconnect()?; + + let proposal_1_id = proposals + .iter() + .find(|e| { + e.tags + .iter() + .any(|t| t.as_vec()[1].eq(&FEATURE_BRANCH_NAME_1)) + }) + .unwrap() + .id; + // recreate proposal 1 on top of a another commit (like a rebase on top + // of one extra commit) + let second_originating_repo = GitTestRepo::default(); + second_originating_repo.populate()?; + std::fs::write( + second_originating_repo.dir.join("amazing.md"), + "some content", + )?; + second_originating_repo + .stage_and_commit("commit for rebasing on top of")?; + cli_tester_create_proposal( + &second_originating_repo, + FEATURE_BRANCH_NAME_1, + "a", + Some((PROPOSAL_TITLE_1, "proposal a description")), + Some(proposal_1_id.to_string()), + )?; + + // pretend we have downloaded the origianl version of the first proposal + let test_repo = GitTestRepo::default(); + test_repo.populate()?; + create_and_populate_branch( + &test_repo, + FEATURE_BRANCH_NAME_1, + "a", + false, + )?; + // pretend we have pulled the updated main branch + test_repo.checkout("main")?; + std::fs::write(test_repo.dir.join("amazing.md"), "some content")?; + test_repo.stage_and_commit("commit for rebasing on top of")?; + + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("finding proposals...\r\n")?; + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_1}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_3}\""), + ], + )?; + c.succeeds_with(0, true, None)?; + p.expect("finding commits...\r\n")?; + p.expect("updated proposal available (2 ahead 0 behind 'main'). existing version is 2 ahead 1 behind 'main'\r\n")?; + // its got here but tmpgit-32.. indicates that creatubg the eature + // branch in line 1291 didnt work so the choices will be different + let mut c = p.expect_choice( + "", + vec![ + format!("checkout and overwrite existing proposal branch"), + format!("checkout existing outdated proposal branch"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect("checked out new version of proposal (2 ahead 0 behind 'main'), replacing old version (2 ahead 1 behind 'main')\r\n")?; + p.expect_end()?; + + for p in [51, 52, 53, 55, 56] { + relay::shutdown_relay(8000 + p)?; + } + Ok((second_originating_repo, test_repo)) + }, + ); + + // launch relay + let _ = join!( + r51.listen_until_close(), + r52.listen_until_close(), + r53.listen_until_close(), + r55.listen_until_close(), + r56.listen_until_close(), + ); + let res = cli_tester_handle.join().unwrap()?; + + Ok(res) + } + + mod cli_prompts { + use super::*; + async fn run_async_prompts_to_choose_from_proposal_titles() -> Result<()> { + let (mut r51, mut r52, mut r53, mut r55, mut r56) = ( + Relay::new(8051, None, None), + Relay::new(8052, None, None), + Relay::new(8053, None, None), + Relay::new(8055, None, None), + Relay::new(8056, None, None), + ); + + r51.events.push(generate_test_key_1_relay_list_event()); + r51.events.push(generate_test_key_1_metadata_event("fred")); + r51.events.push(generate_repo_ref_event()); + + r55.events.push(generate_repo_ref_event()); + r55.events.push(generate_test_key_1_metadata_event("fred")); + r55.events.push(generate_test_key_1_relay_list_event()); + + let cli_tester_handle = std::thread::spawn(move || -> Result<()> { + // create 3 proposals + let _ = cli_tester_create_proposals()?; + // get proposal id of first + let client = Client::new(&nostr::Keys::generate()); + client.add_relay("ws://localhost:8055")?; + client.connect_relay("ws://localhost:8055")?; + let proposals = client.get_events_of( + vec![ + nostr::Filter::default() + .kind(nostr::Kind::Custom(PATCH_KIND)) + .custom_tag(nostr::Alphabet::T, vec!["root"]), + ], + Some(Duration::from_millis(500)), + )?; + client.disconnect()?; + + let proposal_1_id = proposals + .iter() + .find(|e| { + e.tags + .iter() + .any(|t| t.as_vec()[1].eq(&FEATURE_BRANCH_NAME_1)) + }) + .unwrap() + .id; + // recreate proposal 1 on top of a another commit (like a rebase on top + // of one extra commit) + let second_originating_repo = GitTestRepo::default(); + second_originating_repo.populate()?; + std::fs::write( + second_originating_repo.dir.join("amazing.md"), + "some content", + )?; + second_originating_repo + .stage_and_commit("commit for rebasing on top of")?; + cli_tester_create_proposal( + &second_originating_repo, + FEATURE_BRANCH_NAME_1, + "a", + Some((PROPOSAL_TITLE_1, "proposal a description")), + Some(proposal_1_id.to_string()), + )?; + + // pretend we have downloaded the origianl version of the first proposal + let test_repo = GitTestRepo::default(); + test_repo.populate()?; + create_and_populate_branch( + &test_repo, + FEATURE_BRANCH_NAME_1, + "a", + false, + )?; + // pretend we have pulled the updated main branch + test_repo.checkout("main")?; + std::fs::write(test_repo.dir.join("amazing.md"), "some content")?; + test_repo.stage_and_commit("commit for rebasing on top of")?; + + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("finding proposals...\r\n")?; + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_1}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_3}\""), + ], + )?; + c.succeeds_with(0, true, None)?; + p.expect("finding commits...\r\n")?; + p.expect("updated proposal available (2 ahead 0 behind 'main'). existing version is 2 ahead 1 behind 'main'\r\n")?; + // its got here but tmpgit-32.. indicates that creatubg the eature + // branch in line 1291 didnt work so the choices will be different + let mut c = p.expect_choice( + "", + vec![ + format!("checkout and overwrite existing proposal branch"), + format!("checkout existing outdated proposal branch"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect("checked out new version of proposal (2 ahead 0 behind 'main'), replacing old version (2 ahead 1 behind 'main')\r\n")?; + p.expect_end()?; + + for p in [51, 52, 53, 55, 56] { + relay::shutdown_relay(8000 + p)?; + } + Ok(()) + }); + + // launch relay + let _ = join!( + r51.listen_until_close(), + r52.listen_until_close(), + r53.listen_until_close(), + r55.listen_until_close(), + r56.listen_until_close(), + ); + cli_tester_handle.join().unwrap()?; + println!("{:?}", r55.events); + Ok(()) + } + + #[tokio::test] + #[serial] + async fn prompts_to_choose_from_proposal_titles() -> Result<()> { + let _ = run_async_prompts_to_choose_from_proposal_titles().await; + Ok(()) + } + } + + #[tokio::test] + #[serial] + async fn proposal_branch_checked_out() -> Result<()> { + let (_, test_repo) = prep_and_run().await?; + assert_eq!( + FEATURE_BRANCH_NAME_1, + test_repo.get_checked_out_branch_name()?, + ); + Ok(()) + } + + #[tokio::test] + #[serial] + async fn proposal_branch_tip_is_most_recent_proposal_revision_tip() -> Result<()> { + let (originating_repo, test_repo) = prep_and_run().await?; + assert_eq!( + originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, + test_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, + ); + Ok(()) + } } } } -- cgit v1.2.3