From ab1214060a7a2d55068a7ccc9c7f6a04fd7d5aa2 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 19 Jul 2024 20:38:00 +0100 Subject: feat: intergrate `fetch` into `send` reworking the tests and test suite as appropriate --- src/client.rs | 31 +++++++++++++++++++++++++- src/repo_ref.rs | 6 +++--- src/sub_commands/fetch.rs | 13 ++--------- src/sub_commands/send.rs | 55 +++++++++++++++++------------------------------ test_utils/src/git.rs | 26 +++++++++++++++++++++- test_utils/src/lib.rs | 2 +- tests/init.rs | 2 +- tests/send.rs | 24 +++++++++++---------- 8 files changed, 95 insertions(+), 64 deletions(-) diff --git a/src/client.rs b/src/client.rs index ccd5cfb..3b18ad8 100644 --- a/src/client.rs +++ b/src/client.rs @@ -517,7 +517,13 @@ impl Connect for Client { fresh_profiles = HashSet::new(); let relay = self.client.relay(&relay_url).await?; - let events: Vec = get_events_of(&relay, filters, &None).await?; + let events: Vec = get_events_of(&relay, filters.clone(), &None) + .await? + .iter() + // don't process events that don't match filters + .filter(|e| filters.iter().any(|f| f.match_event(e))) + .cloned() + .collect(); // TODO: try reconcile process_fetched_events( @@ -1363,3 +1369,26 @@ pub struct FetchRequest { profiles_to_fetch_from_user_relays: HashMap, user_relays_for_profiles: HashSet, } + +pub async fn fetching_with_report( + git_repo_path: &Path, + #[cfg(test)] client: &crate::client::MockConnect, + #[cfg(not(test))] client: &Client, + repo_coordinates: &HashSet, +) -> Result { + let term = console::Term::stderr(); + term.write_line("fetching updates...")?; + let (relay_reports, progress_reporter) = client + .fetch_all(git_repo_path, repo_coordinates, &HashSet::new()) + .await?; + if !relay_reports.iter().any(std::result::Result::is_err) { + let _ = progress_reporter.clear(); + } + let report = consolidate_fetch_reports(relay_reports); + if report.to_string().is_empty() { + println!("no updates"); + } else { + println!("updates: {report}"); + } + Ok(report) +} diff --git a/src/repo_ref.rs b/src/repo_ref.rs index 9bc3201..3e1fd22 100644 --- a/src/repo_ref.rs +++ b/src/repo_ref.rs @@ -313,14 +313,14 @@ pub async fn get_repo_coordinates( } fn ask_for_naddr() -> Result { - let mut prompt = "repository naddr"; Ok(loop { if let Ok(c) = Coordinate::parse( - Interactor::default().input(PromptInputParms::default().with_prompt(prompt))?, + Interactor::default() + .input(PromptInputParms::default().with_prompt("repository naddr"))?, ) { break c; } - prompt = "repository valid naddr"; + println!("not a valid naddr"); }) } diff --git a/src/sub_commands/fetch.rs b/src/sub_commands/fetch.rs index 28eb960..ab6e0fc 100644 --- a/src/sub_commands/fetch.rs +++ b/src/sub_commands/fetch.rs @@ -9,7 +9,7 @@ use crate::client::Client; #[cfg(test)] use crate::client::MockConnect; use crate::{ - client::{consolidate_fetch_reports, Connect}, + client::{fetching_with_report, Connect}, git::{Repo, RepoActions}, repo_ref::get_repo_coordinates, Cli, @@ -38,16 +38,7 @@ pub async fn launch(args: &Cli, command_args: &SubCommandArgs) -> Result<()> { } repo_coordinates }; - println!("fetching updates..."); - let (relay_reports, _) = client - .fetch_all(git_repo.get_path()?, &repo_coordinates, &HashSet::new()) - .await?; - let report = consolidate_fetch_reports(relay_reports); - if report.to_string().is_empty() { - println!("no updates"); - } else { - println!("updates: {report}"); - } + fetching_with_report(git_repo.get_path()?, &client, &repo_coordinates).await?; client.disconnect().await?; Ok(()) } diff --git a/src/sub_commands/send.rs b/src/sub_commands/send.rs index 410e119..a289def 100644 --- a/src/sub_commands/send.rs +++ b/src/sub_commands/send.rs @@ -1,4 +1,4 @@ -use std::{str::FromStr, time::Duration}; +use std::{path::Path, str::FromStr, time::Duration}; use anyhow::{bail, Context, Result}; use console::Style; @@ -19,10 +19,12 @@ use crate::{ cli_interactor::{ Interactor, InteractorPrompt, PromptConfirmParms, PromptInputParms, PromptMultiChoiceParms, }, - client::{sign_event, Connect}, + client::{ + fetching_with_report, get_events_from_cache, get_repo_ref_from_cache, sign_event, Connect, + }, git::{Repo, RepoActions}, login, - repo_ref::{self, RepoRef, REPO_REF_KIND}, + repo_ref::{get_repo_coordinates, RepoRef, REPO_REF_KIND}, Cli, }; @@ -49,6 +51,7 @@ pub struct SubCommandArgs { #[allow(clippy::too_many_lines)] pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { let git_repo = Repo::discover().context("cannot find a git repository")?; + let git_repo_path = git_repo.get_path()?; let (main_branch_name, main_tip) = git_repo .get_main_or_master_branch() @@ -59,13 +62,13 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { #[cfg(test)] let mut client = ::default(); - let (root_proposal_id, mention_tags) = get_root_proposal_id_and_mentions_from_in_reply_to( - &client, - // TODO: user repo relays when when event cache is in place - client.get_fallback_relays(), - &args.in_reply_to, - ) - .await?; + let repo_coordinates = get_repo_coordinates(&git_repo, &client).await?; + + fetching_with_report(git_repo_path, &client, &repo_coordinates).await?; + + let (root_proposal_id, mention_tags) = + get_root_proposal_id_and_mentions_from_in_reply_to(git_repo.get_path()?, &args.in_reply_to) + .await?; if let Some(root_ref) = args.in_reply_to.first() { if root_proposal_id.is_some() { @@ -191,17 +194,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { client.set_signer(signer.clone()).await; - let repo_ref = repo_ref::fetch( - &git_repo, - git_repo - .get_root_commit() - .context("failed to get root commit of the repository")? - .to_string(), - &client, - user_ref.relays.write(), - true, - ) - .await?; + let repo_ref = get_repo_ref_from_cache(git_repo_path, &repo_coordinates).await?; // oldest first commits.reverse(); @@ -520,9 +513,7 @@ fn summarise_commit_for_selection(git_repo: &Repo, commit: &Sha1Hash) -> Result< } async fn get_root_proposal_id_and_mentions_from_in_reply_to( - #[cfg(test)] client: &crate::client::MockConnect, - #[cfg(not(test))] client: &Client, - repo_relays: &[String], + git_repo_path: &Path, in_reply_to: &[String], ) -> Result<(Option, Vec)> { let root_proposal_id = if let Some(first) = in_reply_to.first() { @@ -535,13 +526,10 @@ async fn get_root_proposal_id_and_mentions_from_in_reply_to( marker: _, public_key: _, }) => { - let events = client - .get_events( - repo_relays.to_vec(), - vec![nostr::Filter::new().id(*event_id)], - ) - .await - .context("whilst getting events specified in --in-reply-to")?; + let events = + get_events_from_cache(git_repo_path, vec![nostr::Filter::new().id(*event_id)]) + .await?; + if let Some(first) = events.iter().find(|e| e.id.eq(event_id)) { if event_is_patch_set_root(first) { Some(event_id.to_string()) @@ -549,10 +537,7 @@ async fn get_root_proposal_id_and_mentions_from_in_reply_to( None } } else { - bail!( - "cannot find first event specified in --in-reply-to \"{}\"", - first, - ) + None } } _ => None, diff --git a/test_utils/src/git.rs b/test_utils/src/git.rs index db50165..7aa7e84 100644 --- a/test_utils/src/git.rs +++ b/test_utils/src/git.rs @@ -5,6 +5,10 @@ use std::{env::current_dir, fs, path::PathBuf}; use anyhow::{Context, Result}; use git2::{Oid, RepositoryInitOptions, Signature, Time}; +use nostr::nips::nip01::Coordinate; +use nostr_sdk::{Kind, ToBech32}; + +use crate::{generate_repo_ref_event, REPOSITORY_KIND}; pub struct GitTestRepo { pub dir: PathBuf, @@ -13,7 +17,24 @@ pub struct GitTestRepo { impl Default for GitTestRepo { fn default() -> Self { - Self::new("main").unwrap() + let repo_event = generate_repo_ref_event(); + let coordinate = Coordinate { + kind: Kind::Custom(REPOSITORY_KIND), + public_key: repo_event.author(), + identifier: repo_event.identifier().unwrap().to_string(), + relays: vec![ + "ws://localhost:8055".to_string(), + "ws://localhost:8056".to_string(), + ], + }; + + let repo = Self::new("main").unwrap(); + let _ = repo + .git_repo + .config() + .unwrap() + .set_str("nostr.repo", &coordinate.to_bech32().unwrap()); + repo } } impl GitTestRepo { @@ -33,6 +54,9 @@ impl GitTestRepo { git_repo, }) } + pub fn without_repo_in_git_config() -> Self { + Self::new("main").unwrap() + } pub fn initial_commit(&self) -> Result { let oid = self.git_repo.index()?.write_tree()?; diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index 2825eaa..a3ffd70 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -191,7 +191,7 @@ pub fn generate_repo_ref_event() -> nostr::Event { /// enough to fool event_is_patch_set_root pub fn get_pretend_proposal_root_event() -> nostr::Event { - serde_json::from_str(r#"{"id":"8cb75aa4cda10a3a0f3242dc49d36159d30b3185bf63414cf6ce17f5c14a73b1","pubkey":"f53e4bcd7a9cdef049cf6467d638a1321958acd3b71eb09823fd6fadb023d768","created_at":1714984571,"kind":1617,"tags":[["t","root"]],"content":"","sig":"6c197314b8c4c61da696dff888198333004d1ecc5d7bae2c554857f2f2b0d3ecc09369a5d8ba089c1bf89e3c6f5be40ade873fd698438ef8b303ffc6df35eb3f"}"#).unwrap() + 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() } /// wrapper for a cli testing tool - currently wraps rexpect and dialoguer diff --git a/tests/init.rs b/tests/init.rs index 5209898..7e2e080 100644 --- a/tests/init.rs +++ b/tests/init.rs @@ -57,7 +57,7 @@ mod when_repo_not_previously_claimed { use super::*; fn prep_git_repo() -> Result { - let test_repo = GitTestRepo::default(); + let test_repo = GitTestRepo::without_repo_in_git_config(); test_repo.populate()?; test_repo.add_remote("origin", "https://localhost:1000")?; Ok(test_repo) diff --git a/tests/send.rs b/tests/send.rs index 87bd54f..0f18bd1 100644 --- a/tests/send.rs +++ b/tests/send.rs @@ -37,6 +37,8 @@ mod when_commits_behind_ask_to_proceed { } fn expect_confirm_prompt(p: &mut CliTester) -> Result { + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // may be 'no updates' or some updates p.expect("creating proposal from 2 commits:\r\n")?; p.expect("fe973a8 add t4.md\r\n")?; p.expect("232efb3 add t3.md\r\n")?; @@ -130,11 +132,13 @@ fn cli_tester_create_proposal(git_repo: &GitTestRepo, include_cover_letter: bool } fn expect_msgs_first(p: &mut CliTester, include_cover_letter: bool) -> Result<()> { + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // may be 'no updates' or some updates p.expect("creating proposal from 2 commits:\r\n")?; p.expect("fe973a8 add t4.md\r\n")?; p.expect("232efb3 add t3.md\r\n")?; - p.expect("searching for profile...\r\n")?; - p.expect("logged in as fred\r\n")?; + // sometimes there will be a 'searching for profile...' msg + p.expect_eventually("logged in as fred\r\n")?; p.expect(format!( "posting 2 patches {} a covering letter...\r\n", if include_cover_letter { @@ -921,7 +925,6 @@ mod when_cover_letter_details_specified_with_range_of_head_2_sends_cover_letter_ let cli_tester_handle = std::thread::spawn(move || -> Result<()> { let mut p = cli_tester_create_proposal(&git_repo, true); expect_msgs_first(&mut p, true)?; - // p.expect_end_with("bla")?; relay::expect_send_with_progress( &mut p, vec![ @@ -1159,6 +1162,8 @@ mod when_range_ommited_prompts_for_selection_defaulting_ahead_of_main { CliTester::new_from_dir(&git_repo.dir, args) } fn expect_msgs_first(p: &mut CliTester) -> Result<()> { + p.expect("fetching updates...\r\n")?; + p.expect_eventually("\r\n")?; // may be 'no updates' or some updates let mut selector = p.expect_multi_select( "select commits for proposal", vec![ @@ -1349,7 +1354,6 @@ mod root_proposal_specified_using_in_reply_to_with_range_of_head_2_and_cover_let "HEAD~2", "--in-reply-to", &proposal_root_bech32, - // "nevent1qqsged665nx6zz36puey9hzf6ds4n5ctxxzm7c6pfnmvu9l4c9988vgzyr6nuj7d02wdauzfeajx043c5yepjk9v6wm3avycy07kltdsy0tksh0zxyx", "--title", "exampletitle", "--description", @@ -1358,16 +1362,16 @@ mod root_proposal_specified_using_in_reply_to_with_range_of_head_2_and_cover_let CliTester::new_from_dir(&git_repo.dir, args) } fn expect_msgs_first(p: &mut CliTester, include_cover_letter: bool) -> Result<()> { + p.expect("fetching updates...\r\n")?; + p.expect("updates: 1 new maintainer, 1 announcement update, 1 proposal\r\n")?; let proposal_root_bech32 = get_pretend_proposal_root_event().id.to_bech32().unwrap(); p.expect(format!( "creating proposal revision for: {}\r\n", proposal_root_bech32, ))?; - // p.expect("creating proposal revision for: nevent1qqsged665nx6zz36puey9hzf6ds4n5ctxxzm7c6pfnmvu9l4c9988vgzyr6nuj7d02wdauzfeajx043c5yepjk9v6wm3avycy07kltdsy0tksh0zxyx\r\n")?; p.expect("creating proposal from 2 commits:\r\n")?; p.expect("fe973a8 add t4.md\r\n")?; p.expect("232efb3 add t3.md\r\n")?; - p.expect("searching for profile...\r\n")?; p.expect("logged in as fred\r\n")?; p.expect(format!( "posting 2 patches {} a covering letter...\r\n", @@ -1574,7 +1578,7 @@ mod root_proposal_specified_using_in_reply_to_with_range_of_head_2_and_cover_let .unwrap() .as_vec()[1], // id of state nevent - "8cb75aa4cda10a3a0f3242dc49d36159d30b3185bf63414cf6ce17f5c14a73b1", + "431e58eb8e1b4e20292d1d5bbe81d5cfb042e1bc165de32eddfdd52245a4cce4", ); } Ok(()) @@ -1620,7 +1624,7 @@ mod in_reply_to_mentions_issue { } fn cli_tester_create_proposal(git_repo: &GitTestRepo) -> CliTester { - let proposal_root_bech32 = get_pretend_issue_event().id.to_bech32().unwrap(); + let issue_bech32 = get_pretend_issue_event().id.to_bech32().unwrap(); let args = vec![ "--nsec", TEST_KEY_1_NSEC, @@ -1630,7 +1634,7 @@ mod in_reply_to_mentions_issue { "send", "HEAD~2", "--in-reply-to", - &proposal_root_bech32, + &issue_bech32, // "note1a9z8vhtzttnny0ggpksd7p5uwf4qu4ys59a52tu9fkz7rrmczkyqc46ngg", "--title", "exampletitle", @@ -1738,7 +1742,6 @@ mod in_reply_to_mentions_issue { } } mod in_reply_to_mentions_npub_and_nprofile_which_get_mentioned_in_proposal_root { - use nostr::JsonUtil; use super::*; @@ -1835,7 +1838,6 @@ mod in_reply_to_mentions_npub_and_nprofile_which_get_mentioned_in_proposal_root for relay in [&r53, &r55, &r56] { let cover_letter_event: &nostr::Event = relay.events.iter().find(|e| is_cover_letter(e)).unwrap(); - println!("{:?}", &cover_letter_event.as_json()); assert!(cover_letter_event.iter_tags().any(|t| { t.as_vec()[0].eq("p") && t.as_vec()[1].eq(&nostr::Keys::parse( -- cgit v1.2.3