From b67376ff54abeab31422921ba5f4883d5d3dccdb Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Tue, 23 Jul 2024 16:36:06 +0100 Subject: feat(list): unique proposal branch names to prevent accidental name conflicts. also moved to prs/* namespace `pull` and `push` integration tests are intermitantly failing to end at least for `push` they work when run individually but not when run together --- Cargo.lock | 3 + src/sub_commands/list.rs | 39 ++-- src/sub_commands/pull.rs | 3 +- src/sub_commands/push.rs | 8 +- src/sub_commands/send.rs | 15 ++ test_utils/Cargo.toml | 3 + test_utils/src/lib.rs | 60 +++++- tests/list.rs | 539 ++++++++++++++++++++++++++++++++++------------- tests/pull.rs | 406 +++++++++++++++++++++++++++++++---- tests/push.rs | 204 ++++++++++++++++-- 10 files changed, 1062 insertions(+), 218 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d5604b..aa3f3e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3054,9 +3054,12 @@ dependencies = [ "assert_cmd", "dialoguer", "directories", + "futures", "git2", "nostr", + "nostr-database", "nostr-sdk", + "nostr-sqlite", "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 2ae4cfb..cc7ac6f 100644 --- a/src/sub_commands/list.rs +++ b/src/sub_commands/list.rs @@ -263,11 +263,11 @@ pub async fn launch() -> Result<()> { .get_local_branch_names() .context("gitlib2 will not show a list of local branch names")? .iter() - .any(|n| n.eq(&cover_letter.branch_name)); + .any(|n| n.eq(&cover_letter.get_branch_name().unwrap())); let checked_out_proposal_branch = git_repo .get_checked_out_branch_name()? - .eq(&cover_letter.branch_name); + .eq(&cover_letter.get_branch_name()?); let proposal_base_commit = str_to_sha1(&tag_value( most_recent_proposal_patch_chain.last().context( @@ -329,14 +329,14 @@ pub async fn launch() -> Result<()> { check_clean(&git_repo)?; let _ = git_repo .apply_patch_chain( - &cover_letter.branch_name, + &cover_letter.get_branch_name()?, most_recent_proposal_patch_chain, ) .context("cannot apply patch chain")?; println!( "checked out proposal as '{}' branch", - cover_letter.branch_name + cover_letter.get_branch_name()? ); Ok(()) } @@ -349,7 +349,7 @@ pub async fn launch() -> Result<()> { }; } - let local_branch_tip = git_repo.get_tip_of_branch(&cover_letter.branch_name)?; + let local_branch_tip = git_repo.get_tip_of_branch(&cover_letter.get_branch_name()?)?; // up-to-date if proposal_tip.eq(&local_branch_tip) { @@ -384,10 +384,10 @@ pub async fn launch() -> Result<()> { )? { 0 => { check_clean(&git_repo)?; - git_repo.checkout(&cover_letter.branch_name)?; + git_repo.checkout(&cover_letter.get_branch_name()?)?; println!( "checked out proposal as '{}' branch", - cover_letter.branch_name + cover_letter.get_branch_name()? ); Ok(()) } @@ -421,10 +421,10 @@ pub async fn launch() -> Result<()> { )? { 0 => { check_clean(&git_repo)?; - git_repo.checkout(&cover_letter.branch_name)?; + git_repo.checkout(&cover_letter.get_branch_name()?)?; let _ = git_repo .apply_patch_chain( - &cover_letter.branch_name, + &cover_letter.get_branch_name()?, most_recent_proposal_patch_chain, ) .context("cannot apply patch chain")?; @@ -474,14 +474,14 @@ pub async fn launch() -> Result<()> { 0 => { check_clean(&git_repo)?; git_repo.create_branch_at_commit( - &cover_letter.branch_name, + &cover_letter.get_branch_name()?, &proposal_base_commit.to_string(), )?; - git_repo.checkout(&cover_letter.branch_name)?; + git_repo.checkout(&cover_letter.get_branch_name()?)?; let chain_length = most_recent_proposal_patch_chain.len(); let _ = git_repo .apply_patch_chain( - &cover_letter.branch_name, + &cover_letter.get_branch_name()?, most_recent_proposal_patch_chain, ) .context("cannot apply patch chain")?; @@ -496,7 +496,7 @@ pub async fn launch() -> Result<()> { } 1 => { check_clean(&git_repo)?; - git_repo.checkout(&cover_letter.branch_name)?; + git_repo.checkout(&cover_letter.get_branch_name()?)?; println!( "checked out old proposal in existing branch ({} ahead {} behind '{main_branch_name}')", local_ahead_of_main.len(), @@ -537,7 +537,7 @@ pub async fn launch() -> Result<()> { ]), )? { 0 => { - git_repo.checkout(&cover_letter.branch_name)?; + git_repo.checkout(&cover_letter.get_branch_name()?)?; println!( "checked out proposal branch with {} unpublished commits ({} ahead {} behind '{main_branch_name}')", local_ahead_of_proposal.len(), @@ -604,7 +604,7 @@ pub async fn launch() -> Result<()> { )? { 0 => { check_clean(&git_repo)?; - git_repo.checkout(&cover_letter.branch_name)?; + git_repo.checkout(&cover_letter.get_branch_name()?)?; println!( "checked out old proposal in existing branch ({} ahead {} behind '{main_branch_name}')", local_ahead_of_main.len(), @@ -615,15 +615,18 @@ pub async fn launch() -> Result<()> { 1 => { check_clean(&git_repo)?; git_repo.create_branch_at_commit( - &cover_letter.branch_name, + &cover_letter.get_branch_name()?, &proposal_base_commit.to_string(), )?; let chain_length = most_recent_proposal_patch_chain.len(); let _ = git_repo - .apply_patch_chain(&cover_letter.branch_name, most_recent_proposal_patch_chain) + .apply_patch_chain( + &cover_letter.get_branch_name()?, + most_recent_proposal_patch_chain, + ) .context("cannot apply patch chain")?; - git_repo.checkout(&cover_letter.branch_name)?; + git_repo.checkout(&cover_letter.get_branch_name()?)?; println!( "checked out latest version of proposal ({} ahead {} behind '{main_branch_name}'), replacing unpublished version ({} ahead {} behind '{main_branch_name}')", chain_length, diff --git a/src/sub_commands/pull.rs b/src/sub_commands/pull.rs index dfa6f89..e33a744 100644 --- a/src/sub_commands/pull.rs +++ b/src/sub_commands/pull.rs @@ -50,7 +50,8 @@ pub async fn launch() -> Result<()> { .await? .iter() .find(|e| { - event_to_cover_letter(e).is_ok_and(|cl| cl.branch_name.eq(&branch_name)) + event_to_cover_letter(e) + .is_ok_and(|cl| cl.get_branch_name().is_ok_and(|s| s.eq(&branch_name))) && !event_is_revision_root(e) }) .context("cannot find proposal that matches the current branch name")? diff --git a/src/sub_commands/push.rs b/src/sub_commands/push.rs index d05158f..9af8b40 100644 --- a/src/sub_commands/push.rs +++ b/src/sub_commands/push.rs @@ -16,7 +16,7 @@ use crate::{ get_most_recent_patch_with_ancestors, get_proposals_and_revisions_from_cache, 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, }; @@ -66,7 +66,11 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { get_proposals_and_revisions_from_cache(git_repo_path, repo_ref.coordinates()) .await? .iter() - .find(|e| event_to_cover_letter(e).is_ok_and(|cl| cl.branch_name.eq(&branch_name))) + .find(|e| { + event_to_cover_letter(e) + .is_ok_and(|cl| cl.get_branch_name().is_ok_and(|s| s.eq(&branch_name))) + && !event_is_revision_root(e) + }) .context("cannot find proposal that matches the current branch name")? .clone(); diff --git a/src/sub_commands/send.rs b/src/sub_commands/send.rs index 33ce104..6b9dd58 100644 --- a/src/sub_commands/send.rs +++ b/src/sub_commands/send.rs @@ -769,8 +769,22 @@ pub struct CoverLetter { pub title: String, pub description: String, pub branch_name: String, + pub event_id: Option, } +impl CoverLetter { + pub fn get_branch_name(&self) -> Result { + Ok(format!( + "prs/{}({})", + self.branch_name, + &self + .event_id + .context("proposal root event_id must be know to get it's branch name")? + .to_hex() + .as_str()[..8], + )) + } +} pub fn event_is_cover_letter(event: &nostr::Event) -> bool { // TODO: look for Subject:[ PATCH 0/n ] but watch out for: // [PATCH v1 0/n ] or @@ -841,6 +855,7 @@ pub fn event_to_cover_letter(event: &nostr::Event) -> Result { .collect(); s }, + event_id: Some(event.id()), }) } diff --git a/test_utils/Cargo.toml b/test_utils/Cargo.toml index b7010c9..2e4c012 100644 --- a/test_utils/Cargo.toml +++ b/test_utils/Cargo.toml @@ -8,9 +8,12 @@ anyhow = "1.0.75" assert_cmd = "2.0.12" dialoguer = "0.10.4" directories = "5.0.1" +futures = "0.3.28" git2 = "0.18.1" nostr = "0.33.0" +nostr-database = "0.33.0" nostr-sdk = "0.33.0" +nostr-sqlite = "0.33.0" once_cell = "1.18.0" rand = "0.8" rexpect = { git = "https://github.com/rust-cli/rexpect.git", rev = "9eb61dd" } diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index a2ed5f2..de7aee5 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -1,9 +1,17 @@ -use std::{ffi::OsStr, path::PathBuf, str::FromStr}; +use std::{ + ffi::OsStr, + path::{Path, PathBuf}, + str::FromStr, +}; use anyhow::{bail, ensure, Context, Result}; use dialoguer::theme::{ColorfulTheme, Theme}; +use futures::executor::block_on; +use git::GitTestRepo; use nostr::{self, nips::nip65::RelayMetadata, Kind, Tag}; +use nostr_database::{NostrDatabase, Order}; use nostr_sdk::{serde_json, NostrSigner, TagStandard}; +use nostr_sqlite::SQLiteDatabase; use once_cell::sync::Lazy; use rexpect::session::{Options, PtySession}; use strip_ansi_escapes::strip_str; @@ -935,3 +943,53 @@ where }, ) } + +/** copied from client.rs */ +async fn get_local_cache_database(git_repo_path: &Path) -> Result { + SQLiteDatabase::open(git_repo_path.join(".git/nostr-cache.sqlite")) + .await + .context("cannot open or create nostr cache database at .git/nostr-cache.sqlite") +} + +/** copied from client.rs */ +pub async fn get_events_from_cache( + git_repo_path: &Path, + filters: Vec, +) -> Result> { + get_local_cache_database(git_repo_path) + .await? + .query(filters.clone(), Order::Asc) + .await + .context( + "cannot execute query on opened git repo nostr cache database .git/nostr-cache.sqlite", + ) +} + +pub fn get_proposal_branch_name( + test_repo: &GitTestRepo, + branch_name_in_event: &str, +) -> Result { + let events = block_on(get_events_from_cache( + &test_repo.dir, + vec![ + nostr::Filter::default() + .kind(nostr_sdk::Kind::GitPatch) + .hashtag("root"), + ], + ))?; + for event in events { + if event.iter_tags().any(|t| { + !t.as_vec()[1].eq("revision-root") + && event.iter_tags().any(|t| { + t.as_vec()[0].eq("branch-name") && t.as_vec()[1].eq(branch_name_in_event) + }) + }) { + return Ok(format!( + "prs/{}({})", + branch_name_in_event, + &event.id.to_hex().as_str()[..8], + )); + } + } + bail!("cannot find proposal root with branch-name tag matching title") +} diff --git a/tests/list.rs b/tests/list.rs index 22d82fd..60c9423 100644 --- a/tests/list.rs +++ b/tests/list.rs @@ -277,10 +277,8 @@ mod when_main_branch_is_uptodate { ], )?; c.succeeds_with(0, false, Some(0))?; - p.expect(format!( - "checked out proposal as '{FEATURE_BRANCH_NAME_1}' branch\r\n" - ))?; - p.expect_end()?; + p.expect_end_eventually_and_print()?; + for p in [51, 52, 53, 55, 56] { relay::shutdown_relay(8000 + p)?; } @@ -351,10 +349,11 @@ mod when_main_branch_is_uptodate { ], )?; c.succeeds_with(0, false, None)?; - p.expect(format!( - "checked out proposal as '{FEATURE_BRANCH_NAME_1}' branch\r\n" + p.expect(&format!( + "checked out proposal as 'prs/{}(", + FEATURE_BRANCH_NAME_1, ))?; - p.expect_end()?; + p.expect_end_eventually_with(")' branch\r\n")?; for p in [51, 52, 53, 55, 56] { relay::shutdown_relay(8000 + p)?; @@ -381,7 +380,10 @@ mod when_main_branch_is_uptodate { async fn proposal_branch_created_with_correct_name() -> Result<()> { let (_, test_repo) = prep_and_run().await?; assert_eq!( - vec![FEATURE_BRANCH_NAME_1, "main"], + vec![ + "main", + &get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?, + ], test_repo.get_local_branch_names()? ); Ok(()) @@ -392,7 +394,7 @@ mod when_main_branch_is_uptodate { async fn proposal_branch_checked_out() -> Result<()> { let (_, test_repo) = prep_and_run().await?; assert_eq!( - FEATURE_BRANCH_NAME_1, + get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?, test_repo.get_checked_out_branch_name()?, ); Ok(()) @@ -404,7 +406,10 @@ mod when_main_branch_is_uptodate { 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)?, + test_repo.get_tip_of_local_branch(&get_proposal_branch_name( + &test_repo, + FEATURE_BRANCH_NAME_1 + )?)?, ); Ok(()) } @@ -461,11 +466,8 @@ mod when_main_branch_is_uptodate { ], )?; c.succeeds_with(0, false, Some(0))?; - p.expect(format!( - "checked out proposal as '{FEATURE_BRANCH_NAME_3}' branch\r\n" - ))?; - p.expect_end()?; - println!("blablagothere"); + p.expect_end_eventually_and_print()?; + for p in [51, 52, 53, 55, 56] { relay::shutdown_relay(8000 + p)?; } @@ -537,10 +539,11 @@ mod when_main_branch_is_uptodate { ], )?; c.succeeds_with(0, false, Some(0))?; - p.expect(format!( - "checked out proposal as '{FEATURE_BRANCH_NAME_3}' branch\r\n" + p.expect(&format!( + "checked out proposal as 'prs/{}(", + FEATURE_BRANCH_NAME_3, ))?; - p.expect_end()?; + p.expect_end_eventually_with(")' branch\r\n")?; for p in [51, 52, 53, 55, 56] { relay::shutdown_relay(8000 + p)?; @@ -567,7 +570,10 @@ mod when_main_branch_is_uptodate { async fn proposal_branch_created_with_correct_name() -> Result<()> { let (_, test_repo) = prep_and_run().await?; assert_eq!( - vec![FEATURE_BRANCH_NAME_3, "main"], + vec![ + "main", + &get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_3)?, + ], test_repo.get_local_branch_names()? ); Ok(()) @@ -578,7 +584,7 @@ mod when_main_branch_is_uptodate { async fn proposal_branch_checked_out() -> Result<()> { let (_, test_repo) = prep_and_run().await?; assert_eq!( - FEATURE_BRANCH_NAME_3, + get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_3)?, test_repo.get_checked_out_branch_name()?, ); Ok(()) @@ -590,7 +596,10 @@ mod when_main_branch_is_uptodate { let (originating_repo, test_repo) = prep_and_run().await?; assert_eq!( originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_3)?, - test_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_3)?, + test_repo.get_tip_of_local_branch(&get_proposal_branch_name( + &test_repo, + FEATURE_BRANCH_NAME_3 + )?)?, ); Ok(()) } @@ -653,10 +662,7 @@ mod when_main_branch_is_uptodate { ], )?; c.succeeds_with(0, false, Some(0))?; - p.expect(format!( - "checked out proposal as '{FEATURE_BRANCH_NAME_4}' branch\r\n" - ))?; - p.expect_end()?; + p.expect_end_eventually_and_print()?; for p in [51, 52, 53, 55, 56] { relay::shutdown_relay(8000 + p)?; @@ -737,10 +743,11 @@ mod when_main_branch_is_uptodate { ], )?; c.succeeds_with(0, false, Some(0))?; - p.expect(format!( - "checked out proposal as '{FEATURE_BRANCH_NAME_4}' branch\r\n" + p.expect(&format!( + "checked out proposal as 'prs/{}(", + FEATURE_BRANCH_NAME_4, ))?; - p.expect_end()?; + p.expect_end_eventually_with(")' branch\r\n")?; for p in [51, 52, 53, 55, 56] { relay::shutdown_relay(8000 + p)?; @@ -767,7 +774,10 @@ mod when_main_branch_is_uptodate { async fn proposal_branch_created_with_correct_name() -> Result<()> { let (_, test_repo) = prep_and_run().await?; assert_eq!( - vec![FEATURE_BRANCH_NAME_4, "main"], + vec![ + "main", + &get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_4)?, + ], test_repo.get_local_branch_names()? ); Ok(()) @@ -778,7 +788,7 @@ mod when_main_branch_is_uptodate { async fn proposal_branch_checked_out() -> Result<()> { let (_, test_repo) = prep_and_run().await?; assert_eq!( - FEATURE_BRANCH_NAME_4, + get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_4)?, test_repo.get_checked_out_branch_name()?, ); Ok(()) @@ -790,7 +800,10 @@ mod when_main_branch_is_uptodate { let (originating_repo, test_repo) = prep_and_run().await?; assert_eq!( originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_4)?, - test_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_4)?, + test_repo.get_tip_of_local_branch(&get_proposal_branch_name( + &test_repo, + FEATURE_BRANCH_NAME_4 + )?)?, ); Ok(()) } @@ -824,21 +837,40 @@ mod when_main_branch_is_uptodate { 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)> { + let cli_tester_handle = std::thread::spawn( + move || -> Result<(GitTestRepo, GitTestRepo)> { let originating_repo = cli_tester_create_proposals()?; let test_repo = GitTestRepo::default(); test_repo.populate()?; + // create proposal branch let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); - - create_and_populate_branch( - &test_repo, - FEATURE_BRANCH_NAME_1, - "a", - false, + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!("create and checkout proposal branch (2 ahead 0 behind 'main')"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + test_repo.checkout("main")?; + // run test + p = CliTester::new_from_dir(&test_repo.dir, ["list"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here let mut c = p.expect_choice( @@ -860,16 +892,14 @@ mod when_main_branch_is_uptodate { ], )?; c.succeeds_with(0, false, Some(0))?; - p.expect(format!( - "checked out proposal as '{FEATURE_BRANCH_NAME_1}' branch\r\n" - ))?; - p.expect_end()?; + p.expect_end_eventually_and_print()?; for p in [51, 52, 53, 55, 56] { relay::shutdown_relay(8000 + p)?; } Ok((originating_repo, test_repo)) - }); + }, + ); // launch relay let _ = join!( @@ -911,16 +941,34 @@ mod when_main_branch_is_uptodate { let test_repo = GitTestRepo::default(); test_repo.populate()?; + // create proposal branch let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); - - create_and_populate_branch( - &test_repo, - FEATURE_BRANCH_NAME_1, - "a", - false, + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], )?; - test_repo.checkout("main")?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!("create and checkout proposal branch (2 ahead 0 behind 'main')"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + test_repo.checkout("main")?; + // run test + p = CliTester::new_from_dir(&test_repo.dir, ["list"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here let mut c = p.expect_choice( @@ -942,10 +990,11 @@ mod when_main_branch_is_uptodate { ], )?; c.succeeds_with(0, false, Some(0))?; - p.expect(format!( - "checked out proposal as '{FEATURE_BRANCH_NAME_1}' branch\r\n" + p.expect(&format!( + "checked out proposal as 'prs/{}(", + FEATURE_BRANCH_NAME_1, ))?; - p.expect_end()?; + p.expect_end_eventually_with(")' branch\r\n")?; for p in [51, 52, 53, 55, 56] { relay::shutdown_relay(8000 + p)?; @@ -972,7 +1021,7 @@ mod when_main_branch_is_uptodate { async fn proposal_branch_checked_out() -> Result<()> { let (_, test_repo) = prep_and_run().await?; assert_eq!( - FEATURE_BRANCH_NAME_1, + get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?, test_repo.get_checked_out_branch_name()?, ); Ok(()) @@ -1003,19 +1052,46 @@ mod when_main_branch_is_uptodate { let cli_tester_handle = std::thread::spawn( move || -> Result<(GitTestRepo, GitTestRepo)> { let originating_repo = cli_tester_create_proposals()?; - let test_repo = GitTestRepo::default(); test_repo.populate()?; + // create proposal branch let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!("create and checkout proposal branch (2 ahead 0 behind 'main')"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; - create_and_populate_branch( - &test_repo, - FEATURE_BRANCH_NAME_1, - "a", + // remove latest commit so it is behind + let branch_name = test_repo.get_checked_out_branch_name()?; + test_repo.checkout("main")?; + test_repo.git_repo.branch( + &branch_name, + &test_repo + .git_repo + .find_commit(test_repo.get_tip_of_local_branch(&branch_name)?)? + .parent(0)?, true, )?; - test_repo.checkout("main")?; - + // run test + p = CliTester::new_from_dir(&test_repo.dir, ["list"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here let mut c = p.expect_choice( @@ -1087,16 +1163,44 @@ mod when_main_branch_is_uptodate { let test_repo = GitTestRepo::default(); test_repo.populate()?; + // create proposal branch let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!("create and checkout proposal branch (2 ahead 0 behind 'main')"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; - create_and_populate_branch( - &test_repo, - FEATURE_BRANCH_NAME_1, - "a", + // remove latest commit so it is behind + let branch_name = test_repo.get_checked_out_branch_name()?; + test_repo.checkout("main")?; + test_repo.git_repo.branch( + &branch_name, + &test_repo + .git_repo + .find_commit(test_repo.get_tip_of_local_branch(&branch_name)?)? + .parent(0)?, true, )?; - test_repo.checkout("main")?; - + // run test + p = CliTester::new_from_dir(&test_repo.dir, ["list"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here let mut c = p.expect_choice( @@ -1146,7 +1250,7 @@ mod when_main_branch_is_uptodate { async fn proposal_branch_checked_out() -> Result<()> { let (_, test_repo) = prep_and_run().await?; assert_eq!( - FEATURE_BRANCH_NAME_1, + get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?, test_repo.get_checked_out_branch_name()?, ); Ok(()) @@ -1158,7 +1262,10 @@ mod when_main_branch_is_uptodate { 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)?, + test_repo.get_tip_of_local_branch(&get_proposal_branch_name( + &test_repo, + FEATURE_BRANCH_NAME_1 + )?)?, ); Ok(()) } @@ -1185,30 +1292,59 @@ mod when_main_branch_is_uptodate { 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)> { + let cli_tester_handle = std::thread::spawn( + move || -> Result<(GitTestRepo, GitTestRepo)> { let originating_repo = cli_tester_create_proposals()?; let test_repo = GitTestRepo::default(); test_repo.populate()?; + // create proposal branch let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); - - // simulating checking out the proposal (the commits_ids will match) - create_and_populate_branch( - &test_repo, - "different-branch-name", - "a", - false, + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], )?; - // simulating amending the proposal - create_and_populate_branch( - &test_repo, - FEATURE_BRANCH_NAME_1, - "a-changed", - false, + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!("create and checkout proposal branch (2 ahead 0 behind 'main')"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + // remove latest commit so it is behind + let branch_name = test_repo.get_checked_out_branch_name()?; + test_repo.checkout("main")?; + test_repo.git_repo.branch( + &branch_name, + &test_repo + .git_repo + .find_commit(test_repo.get_tip_of_local_branch(&branch_name)?)? + .parent(0)?, + true, + )?; + // add another commit (so we have an ammened local branch) + test_repo.checkout(&branch_name)?; + std::fs::write( + test_repo.dir.join("ammended-commit.md"), + "some content", + )?; + test_repo.stage_and_commit("add ammended-commit.md")?; test_repo.checkout("main")?; + + // run test + p = CliTester::new_from_dir(&test_repo.dir, ["list"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here let mut c = p.expect_choice( @@ -1242,7 +1378,8 @@ mod when_main_branch_is_uptodate { relay::shutdown_relay(8000 + p)?; } Ok((originating_repo, test_repo)) - }); + }, + ); // launch relay let _ = join!( r51.listen_until_close(), @@ -1284,24 +1421,53 @@ mod when_main_branch_is_uptodate { let test_repo = GitTestRepo::default(); test_repo.populate()?; + // create proposal branch let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); - - // simulating checking out the proposal (the commits_ids will match) - create_and_populate_branch( - &test_repo, - "different-branch-name", - "a", - false, + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], )?; - // simulating amending the proposal - create_and_populate_branch( - &test_repo, - FEATURE_BRANCH_NAME_1, - "a-changed", - false, + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!("create and checkout proposal branch (2 ahead 0 behind 'main')"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + // remove latest commit so it is behind + let branch_name = test_repo.get_checked_out_branch_name()?; test_repo.checkout("main")?; + test_repo.git_repo.branch( + &branch_name, + &test_repo + .git_repo + .find_commit(test_repo.get_tip_of_local_branch(&branch_name)?)? + .parent(0)?, + true, + )?; + // add another commit (so we have an ammened local branch) + test_repo.checkout(&branch_name)?; + std::fs::write( + test_repo.dir.join("ammended-commit.md"), + "some content", + )?; + test_repo.stage_and_commit("add ammended-commit.md")?; + test_repo.checkout("main")?; + + // run test + p = CliTester::new_from_dir(&test_repo.dir, ["list"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here let mut c = p.expect_choice( @@ -1362,7 +1528,10 @@ mod when_main_branch_is_uptodate { let (originating_repo, test_repo) = prep_and_run().await?; println!("test_dir: {:?}", test_repo.dir); assert_eq!( - test_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, + test_repo.get_tip_of_local_branch(&get_proposal_branch_name( + &test_repo, + FEATURE_BRANCH_NAME_1 + )?)?, originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, ); Ok(()) @@ -1395,19 +1564,41 @@ mod when_main_branch_is_uptodate { let test_repo = GitTestRepo::default(); test_repo.populate()?; + // create proposal branch let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); - - create_and_populate_branch( - &test_repo, - FEATURE_BRANCH_NAME_1, - "a", - false, + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!("create and checkout proposal branch (2 ahead 0 behind 'main')"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], )?; - // add appended commit to local branch - std::fs::write(test_repo.dir.join("appended.md"), "some content")?; - test_repo.stage_and_commit("appended commit")?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + // add another commit (so we have a local branch 1 ahead) + std::fs::write( + test_repo.dir.join("ammended-commit.md"), + "some content", + )?; + test_repo.stage_and_commit("add ammended-commit.md")?; test_repo.checkout("main")?; + + // run test + p = CliTester::new_from_dir(&test_repo.dir, ["list"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here let mut c = p.expect_choice( @@ -1481,19 +1672,41 @@ mod when_main_branch_is_uptodate { let test_repo = GitTestRepo::default(); test_repo.populate()?; + // create proposal branch let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); - - create_and_populate_branch( - &test_repo, - FEATURE_BRANCH_NAME_1, - "a", - false, + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], )?; - // add appended commit to local branch - std::fs::write(test_repo.dir.join("appended.md"), "some content")?; - test_repo.stage_and_commit("appended commit")?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!("create and checkout proposal branch (2 ahead 0 behind 'main')"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + // add another commit (so we have a local branch 1 ahead) + std::fs::write( + test_repo.dir.join("ammended-commit.md"), + "some content", + )?; + test_repo.stage_and_commit("add ammended-commit.md")?; test_repo.checkout("main")?; + + // run test + p = CliTester::new_from_dir(&test_repo.dir, ["list"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here let mut c = p.expect_choice( @@ -1545,7 +1758,7 @@ mod when_main_branch_is_uptodate { async fn proposal_branch_checked_out() -> Result<()> { let (_, test_repo) = prep_and_run().await?; assert_eq!( - FEATURE_BRANCH_NAME_1, + get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?, test_repo.get_checked_out_branch_name()?, ); Ok(()) @@ -1556,7 +1769,10 @@ mod when_main_branch_is_uptodate { async fn didnt_overwrite_local_appendments() -> Result<()> { let (originating_repo, test_repo) = prep_and_run().await?; assert_ne!( - test_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, + test_repo.get_tip_of_local_branch(&get_proposal_branch_name( + &test_repo, + FEATURE_BRANCH_NAME_1 + )?)?, originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, ); Ok(()) @@ -1592,7 +1808,34 @@ mod when_main_branch_is_uptodate { tokio::task::spawn_blocking(move || { // create 3 proposals let _ = cli_tester_create_proposals()?; - // get proposal id of first + // download the origianl version of the first proposal + let test_repo = GitTestRepo::default(); + test_repo.populate()?; + + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!("create and checkout proposal branch (2 ahead 0 behind 'main')"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + // get proposal id of first let client = Client::default(); Handle::current().block_on(client.add_relay("ws://localhost:8055"))?; @@ -1638,15 +1881,6 @@ mod when_main_branch_is_uptodate { 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")?; @@ -1724,6 +1958,34 @@ mod when_main_branch_is_uptodate { move || { // create 3 proposals let _ = cli_tester_create_proposals()?; + // download the origianl version of the first proposal + let test_repo = GitTestRepo::default(); + test_repo.populate()?; + + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!("create and checkout proposal branch (2 ahead 0 behind 'main')"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + // get proposal id of first let client = Client::default(); Handle::current() @@ -1771,16 +2033,6 @@ mod when_main_branch_is_uptodate { 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")?; @@ -1839,7 +2091,7 @@ mod when_main_branch_is_uptodate { async fn proposal_branch_checked_out() -> Result<()> { let (_, test_repo) = prep_and_run().await?; assert_eq!( - FEATURE_BRANCH_NAME_1, + get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?, test_repo.get_checked_out_branch_name()?, ); Ok(()) @@ -1851,7 +2103,10 @@ mod when_main_branch_is_uptodate { 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)?, + test_repo.get_tip_of_local_branch(&get_proposal_branch_name( + &test_repo, + FEATURE_BRANCH_NAME_1 + )?)?, ); Ok(()) } diff --git a/tests/pull.rs b/tests/pull.rs index ecd64ff..0febd5c 100644 --- a/tests/pull.rs +++ b/tests/pull.rs @@ -156,7 +156,30 @@ mod when_main_is_checked_out { let test_repo = GitTestRepo::default(); test_repo.populate()?; - create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", false)?; + // create proposal branch + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!("create and checkout proposal branch (2 ahead 0 behind 'main')"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; test_repo.checkout("main")?; let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]); @@ -277,7 +300,32 @@ mod when_branch_is_checked_out { let test_repo = GitTestRepo::default(); test_repo.populate()?; - create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", false)?; + // create proposal branch + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]); p.expect("fetching updates...\r\n")?; @@ -333,7 +381,44 @@ mod when_branch_is_checked_out { let test_repo = GitTestRepo::default(); test_repo.populate()?; - create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", true)?; + // create proposal branch + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + + // remove latest commit so it is behind + let branch_name = test_repo.get_checked_out_branch_name()?; + test_repo.checkout("main")?; + test_repo.git_repo.branch( + &branch_name, + &test_repo + .git_repo + .find_commit(test_repo.get_tip_of_local_branch(&branch_name)?)? + .parent(0)?, + true, + )?; let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]); p.expect_end_eventually()?; @@ -387,13 +472,49 @@ mod when_branch_is_checked_out { let test_repo = GitTestRepo::default(); test_repo.populate()?; - create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", true)?; + // create proposal branch + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + + // remove latest commit so it is behind + let branch_name = test_repo.get_checked_out_branch_name()?; + test_repo.checkout("main")?; + test_repo.git_repo.branch( + &branch_name, + &test_repo + .git_repo + .find_commit(test_repo.get_tip_of_local_branch(&branch_name)?)? + .parent(0)?, + true, + )?; let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here - p.expect("applied 1 new commits\r\n")?; - p.expect_end()?; + p.expect_end_with("applied 1 new commits\r\n")?; for p in [51, 52, 53, 55, 56] { relay::shutdown_relay(8000 + p)?; @@ -421,7 +542,10 @@ mod when_branch_is_checked_out { 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)?, + test_repo.get_tip_of_local_branch(&get_proposal_branch_name( + &test_repo, + FEATURE_BRANCH_NAME_1 + )?)?, ); Ok(()) } @@ -453,20 +577,72 @@ mod when_branch_is_checked_out { r55.events.push(generate_test_key_1_relay_list_event()); let cli_tester_handle = std::thread::spawn(move || -> Result<()> { - cli_tester_create_proposals()?; + let originating_repo = cli_tester_create_proposals()?; let test_repo = GitTestRepo::default(); test_repo.populate()?; - // simulating amending an older version of the proposal commits on the current - // branch - create_and_populate_branch( - &test_repo, - FEATURE_BRANCH_NAME_1, - "a-changed", - false, + // create proposal branch + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + + // remove latest commit so it is behind + let branch_name = test_repo.get_checked_out_branch_name()?; + test_repo.checkout("main")?; + test_repo.git_repo.branch( + &branch_name, + &test_repo + .git_repo + .find_commit(test_repo.get_tip_of_local_branch(&branch_name)?)? + .parent(0)?, + true, + )?; + // add another commit (so we have an ammened local branch) + test_repo.checkout(&branch_name)?; + std::fs::write(test_repo.dir.join("ammended-commit.md"), "some content")?; + test_repo.stage_and_commit("add ammended-commit.md")?; + test_repo.checkout("main")?; + // create and send a revision from another repository + originating_repo.checkout("main")?; + test_repo.git_repo.branch( + &branch_name, + &test_repo + .git_repo + .find_commit(test_repo.get_tip_of_local_branch(&branch_name)?)? + .parent(0)?, + true, + )?; + originating_repo.checkout(&branch_name)?; + test_repo.checkout(&branch_name)?; + std::fs::write(test_repo.dir.join("ammended-commit.md"), "some content")?; + test_repo.stage_and_commit("add ammended-commit.md")?; + let mut p = CliTester::new_from_dir(&test_repo.dir, ["push", "--force"]); + p.expect_end_eventually()?; + // test when branch is ammended an older version of the proposal let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here @@ -534,17 +710,51 @@ mod when_branch_is_checked_out { let test_repo = GitTestRepo::default(); test_repo.populate()?; - // simulating checking out the proposal (the commits_ids will match) - create_and_populate_branch(&test_repo, "different-branch-name", "a", false)?; + // create proposal branch + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + + // remove latest commit so it is behind + let branch_name = test_repo.get_checked_out_branch_name()?; test_repo.checkout("main")?; - // simulating amending the proposal - create_and_populate_branch( - &test_repo, - FEATURE_BRANCH_NAME_1, - "a-changed", - false, + test_repo.git_repo.branch( + &branch_name, + &test_repo + .git_repo + .find_commit(test_repo.get_tip_of_local_branch(&branch_name)?)? + .parent(0)?, + true, )?; + // add another commit (so we have an ammened local branch) + test_repo.checkout(&branch_name)?; + std::fs::write(test_repo.dir.join("ammended-commit.md"), "some content")?; + test_repo.stage_and_commit("add ammended-commit.md")?; + test_repo.checkout("main")?; + // run test let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here @@ -606,11 +816,39 @@ mod when_branch_is_checked_out { let test_repo = GitTestRepo::default(); test_repo.populate()?; - create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", false)?; - // add appended commit to local branch - std::fs::write(test_repo.dir.join("appended.md"), "some content")?; - test_repo.stage_and_commit("appended commit")?; + // create proposal branch + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + // add another commit (so we have a local branch 1 ahead) + std::fs::write(test_repo.dir.join("ammended-commit.md"), "some content")?; + test_repo.stage_and_commit("add ammended-commit.md")?; + test_repo.checkout("main")?; + + // run test let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here @@ -665,11 +903,39 @@ mod when_branch_is_checked_out { let test_repo = GitTestRepo::default(); test_repo.populate()?; - create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", false)?; - // add appended commit to local branch - std::fs::write(test_repo.dir.join("appended.md"), "some content")?; - test_repo.stage_and_commit("appended commit")?; + // create proposal branch + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + + // add another commit (so we have a local branch 1 ahead) + std::fs::write(test_repo.dir.join("ammended-commit.md"), "some content")?; + test_repo.stage_and_commit("add ammended-commit.md")?; + test_repo.checkout("main")?; + // run test let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here @@ -701,7 +967,10 @@ mod when_branch_is_checked_out { async fn didnt_overwrite_local_appendments() -> Result<()> { let (originating_repo, test_repo) = prep_and_run().await?; assert_ne!( - test_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, + test_repo.get_tip_of_local_branch(&get_proposal_branch_name( + &test_repo, + FEATURE_BRANCH_NAME_1 + )?)?, originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, ); Ok(()) @@ -737,6 +1006,36 @@ mod when_branch_is_checked_out { tokio::task::spawn_blocking(move || { // create 3 proposals let _ = cli_tester_create_proposals()?; + // download the origianl version of the first proposal + let test_repo = GitTestRepo::default(); + test_repo.populate()?; + + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + // get proposal id of first let client = Client::default(); Handle::current().block_on(client.add_relay("ws://localhost:8055"))?; @@ -780,10 +1079,6 @@ mod when_branch_is_checked_out { 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")?; @@ -838,6 +1133,36 @@ mod when_branch_is_checked_out { move || { // create 3 proposals let _ = cli_tester_create_proposals()?; + // download the origianl version of the first proposal + let test_repo = GitTestRepo::default(); + test_repo.populate()?; + + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + // get proposal id of first let client = Client::default(); Handle::current().block_on(client.add_relay("ws://localhost:8055"))?; @@ -882,10 +1207,6 @@ mod when_branch_is_checked_out { 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")?; @@ -923,7 +1244,10 @@ mod when_branch_is_checked_out { 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)?, + test_repo.get_tip_of_local_branch(&get_proposal_branch_name( + &test_repo, + FEATURE_BRANCH_NAME_1 + )?)?, ); Ok(()) } diff --git a/tests/push.rs b/tests/push.rs index 1f09d28..b771d4b 100644 --- a/tests/push.rs +++ b/tests/push.rs @@ -201,7 +201,32 @@ mod when_branch_is_checked_out { let test_repo = GitTestRepo::default(); test_repo.populate()?; - create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", false)?; + // create proposal branch + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; let mut p = CliTester::new_from_dir(&test_repo.dir, ["push"]); p.expect("fetching updates...\r\n")?; @@ -259,8 +284,46 @@ mod when_branch_is_checked_out { let test_repo = GitTestRepo::default(); test_repo.populate()?; - create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", true)?; - + // create proposal branch + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + + // remove latest commit so it is behind + let branch_name = test_repo.get_checked_out_branch_name()?; + test_repo.checkout("main")?; + test_repo.git_repo.branch( + &branch_name, + &test_repo + .git_repo + .find_commit(test_repo.get_tip_of_local_branch(&branch_name)?)? + .parent(0)?, + true, + )?; + test_repo.checkout(&branch_name)?; + // run test let mut p = CliTester::new_from_dir(&test_repo.dir, ["push"]); p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here @@ -322,11 +385,37 @@ mod when_branch_is_checked_out { let test_repo = GitTestRepo::default(); test_repo.populate()?; - create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", false)?; - - std::fs::write(test_repo.dir.join("a5.md"), "some content")?; - test_repo.stage_and_commit("add a5.md".to_string().as_str())?; + // create proposal branch + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + // add another commit (so we have an ammened local branch) + std::fs::write(test_repo.dir.join("ammended-commit.md"), "some content")?; + test_repo.stage_and_commit("add ammended-commit.md")?; + // run test let mut p = CliTester::new_from_dir( &test_repo.dir, [ @@ -404,7 +493,35 @@ mod when_branch_is_checked_out { let test_repo = GitTestRepo::default(); test_repo.populate()?; - create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", false)?; + // create proposal branch + let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!("create and checkout proposal branch (2 ahead 0 behind 'main')"), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + // add another commit (so we have an ammened local branch) + std::fs::write(test_repo.dir.join("ammended-commit.md"), "some content")?; + test_repo.stage_and_commit("add ammended-commit.md")?; + + // run test let mut p = CliTester::new_from_dir( &test_repo.dir, @@ -443,7 +560,7 @@ mod when_branch_is_checked_out { let (test_repo, r55_events) = prep_and_run().await?; let commit_id = test_repo - .get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)? + .get_tip_of_local_branch(&test_repo.get_checked_out_branch_name()?)? .to_string(); assert!(r55_events.iter().any(|e| { e.tags @@ -482,20 +599,52 @@ mod when_branch_is_checked_out { let cli_tester_handle = std::thread::spawn(move || -> Result<()> { cli_tester_create_proposals()?; + // discover proposal branch name + let tmp_repo = GitTestRepo::default(); + tmp_repo.populate()?; + let mut p = CliTester::new_from_dir(&tmp_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + let branch_name = tmp_repo.get_checked_out_branch_name()?; + let test_repo = GitTestRepo::default(); test_repo.populate()?; // simulate rebase std::fs::write(test_repo.dir.join("amazing.md"), "some content")?; test_repo.stage_and_commit("commit for rebasing on top of")?; - create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", true)?; + create_and_populate_branch(&test_repo, &branch_name, "a", true)?; let mut p = CliTester::new_from_dir(&test_repo.dir, ["push"]); // p.expect_end_eventually_and_print()?; p.expect("fetching updates...\r\n")?; p.expect_eventually("\r\n")?; // some updates listed here - p.expect("Error: local unpublished proposal has been rebased. consider force pushing\r\n")?; + p.expect( + "Error: local unpublished proposal has been rebased. consider force pushing\r\n", + )?; p.expect_end()?; for p in [51, 52, 53, 55, 56] { @@ -543,6 +692,35 @@ mod when_branch_is_checked_out { let cli_tester_handle = std::thread::spawn(move || -> Result<()> { cli_tester_create_proposals()?; + // discover proposal branch name + let tmp_repo = GitTestRepo::default(); + tmp_repo.populate()?; + let mut p = CliTester::new_from_dir(&tmp_repo.dir, ["list"]); + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // some updates listed here + let mut c = p.expect_choice( + "all proposals", + vec![ + format!("\"{PROPOSAL_TITLE_3}\""), + format!("\"{PROPOSAL_TITLE_2}\""), + format!("\"{PROPOSAL_TITLE_1}\""), + ], + )?; + c.succeeds_with(2, true, None)?; + let mut c = p.expect_choice( + "", + vec![ + format!( + "create and checkout proposal branch (2 ahead 0 behind 'main')" + ), + format!("apply to current branch with `git am`"), + format!("download to ./patches"), + format!("back"), + ], + )?; + c.succeeds_with(0, false, Some(0))?; + p.expect_end_eventually()?; + let branch_name = tmp_repo.get_checked_out_branch_name()?; let test_repo = GitTestRepo::default(); test_repo.populate()?; @@ -550,7 +728,7 @@ mod when_branch_is_checked_out { // simulate rebase std::fs::write(test_repo.dir.join("amazing.md"), "some content")?; test_repo.stage_and_commit("commit for rebasing on top of")?; - create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", false)?; + create_and_populate_branch(&test_repo, &branch_name, "a", false)?; let mut p = CliTester::new_from_dir( &test_repo.dir, [ @@ -574,7 +752,7 @@ mod when_branch_is_checked_out { let mut selector = p.expect_multi_select( "select commits for proposal", vec![ - "(Joe Bloggs) add a4.md [feature-example-t] 355bdf1".to_string(), + format!("(Joe Bloggs) add a4.md [{branch_name}] 355bdf1"), "(Joe Bloggs) add a3.md dbd1115".to_string(), "(Joe Bloggs) commit for rebasing on top of [main] 1aa2cfe" .to_string(), -- cgit v1.2.3