From 06be0bc44011411b78217459f505ed12281b32c4 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 1 Dec 2023 00:00:00 +0000 Subject: feat(prs-list) list and pull selected as branch - fetch prs and present as a selectable list - create and / or checkout branch for selected pr - apply latest patches as commits --- test_utils/src/git.rs | 60 ++++++++++++++++- test_utils/src/lib.rs | 167 +++++++++++++++++++++++++++++++++++++++++++++++- test_utils/src/relay.rs | 33 +++++++++- 3 files changed, 254 insertions(+), 6 deletions(-) (limited to 'test_utils/src') diff --git a/test_utils/src/git.rs b/test_utils/src/git.rs index 166693d..af87a3a 100644 --- a/test_utils/src/git.rs +++ b/test_utils/src/git.rs @@ -3,7 +3,7 @@ // implement drop? use std::{env::current_dir, fs, path::PathBuf}; -use anyhow::Result; +use anyhow::{Context, Result}; use git2::{Oid, RepositoryInitOptions, Signature, Time}; pub struct GitTestRepo { @@ -53,7 +53,27 @@ impl GitTestRepo { self.stage_and_commit("add t2.md") } + pub fn populate_with_test_branch(&self) -> Result { + self.populate()?; + self.create_branch("add-example-feature")?; + fs::write(self.dir.join("f1.md"), "some content")?; + self.stage_and_commit("add f1.md")?; + fs::write(self.dir.join("f2.md"), "some content")?; + self.stage_and_commit("add f2.md")?; + fs::write(self.dir.join("f3.md"), "some content1")?; + self.stage_and_commit("add f3.md") + } + pub fn stage_and_commit(&self, message: &str) -> Result { + self.stage_and_commit_custom_signature(message, None, None) + } + + pub fn stage_and_commit_custom_signature( + &self, + message: &str, + author: Option<&git2::Signature>, + commiter: Option<&git2::Signature>, + ) -> Result { let prev_oid = self.git_repo.head().unwrap().peel_to_commit()?; let mut index = self.git_repo.index()?; @@ -62,8 +82,8 @@ impl GitTestRepo { let oid = self.git_repo.commit( Some("HEAD"), - &joe_signature(), - &joe_signature(), + author.unwrap_or(&joe_signature()), + commiter.unwrap_or(&joe_signature()), message, &self.git_repo.find_tree(index.write_tree()?)?, &[&prev_oid], @@ -92,6 +112,40 @@ impl GitTestRepo { let oid = self.git_repo.head()?.peel_to_commit()?.id(); Ok(oid) } + + pub fn get_local_branch_names(&self) -> Result> { + let local_branches = self + .git_repo + .branches(Some(git2::BranchType::Local)) + .context("getting GitRepo branches should not error even for a blank repository")?; + + let mut branch_names = vec![]; + + for iter in local_branches { + let branch = iter?.0; + if let Some(name) = branch.name()? { + branch_names.push(name.to_string()); + } + } + Ok(branch_names) + } + + pub fn get_checked_out_branch_name(&self) -> Result { + Ok(self + .git_repo + .head()? + .shorthand() + .context("an object without a shorthand is checked out")? + .to_string()) + } + + pub fn get_tip_of_local_branch(&self, branch_name: &str) -> Result { + let branch = self + .git_repo + .find_branch(branch_name, git2::BranchType::Local) + .context(format!("cannot find branch {branch_name}"))?; + Ok(branch.into_reference().peel_to_commit()?.id()) + } } impl Drop for GitTestRepo { diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index e867a7b..fa80f1f 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -11,6 +11,10 @@ use strip_ansi_escapes::strip_str; pub mod git; pub mod relay; +pub static PR_KIND: u64 = 318; +pub static PATCH_KIND: u64 = 317; +pub static REPOSITORY_KIND: u64 = 300317; + pub static TEST_KEY_1_NSEC: &str = "nsec1ppsg5sm2aexq06juxmu9evtutr6jkwkhp98exxxvwamhru9lyx9s3rwseq"; pub static TEST_KEY_1_SK_HEX: &str = @@ -120,8 +124,6 @@ pub fn make_event_old_or_change_user( unsigned.sign(keys).unwrap() } -pub static REPOSITORY_KIND: u64 = 300317; - pub fn generate_repo_ref_event() -> nostr::Event { // taken from test git_repo let root_commit = "9ee507fc4357d7ee16a5d8901bedcd103f23c17d"; @@ -208,6 +210,20 @@ impl CliTester { i.prompt(true, default).context("initial confirm prompt")?; Ok(i) } + + pub fn expect_choice( + &mut self, + prompt: &str, + choices: Vec, + ) -> Result { + let mut i = CliTesterChoicePrompt { + tester: self, + prompt: prompt.to_string(), + choices, + }; + i.prompt(false).context("initial confirm prompt")?; + Ok(i) + } } pub struct CliTesterInputPrompt<'a> { @@ -397,6 +413,137 @@ impl CliTesterConfirmPrompt<'_> { } } +pub struct CliTesterChoicePrompt<'a> { + tester: &'a mut CliTester, + prompt: String, + choices: Vec, +} + +impl CliTesterChoicePrompt<'_> { + fn prompt(&mut self, eventually: bool) -> Result<&mut Self> { + let mut s = String::new(); + self.tester + .formatter + .format_select_prompt(&mut s, self.prompt.as_str()) + .expect("diagluer theme formatter should succeed"); + ensure!( + s.contains(self.prompt.as_str()), + "dialoguer must be broken as formatted prompt success doesnt contain prompt" + ); + + if eventually { + self.tester + .expect_eventually(sanatize(s).as_str()) + .context("expect input prompt eventually")?; + } else { + self.tester + .expect(sanatize(s).as_str()) + .context("expect confirm prompt")?; + } + + Ok(self) + } + + pub fn succeeds_with(&mut self, chosen_index: u64, report: bool) -> Result<&mut Self> { + fn show_options( + tester: &mut CliTester, + choices: &Vec, + selected_index: Option, + ) -> Result<()> { + if selected_index.is_some() { + for _ in 0..choices.len() { + tester.expect("\r").context("expect new line per choice")?; + } + } else { + tester + .expect("\r\n") + .context("expect new line before choices")?; + } + + for (index, item) in choices.iter().enumerate() { + let mut s = String::new(); + tester + .formatter + .format_select_prompt_item( + &mut s, + item.as_str(), + if let Some(i) = selected_index { + index == i + } else { + false + }, + ) + .expect("diagluer theme formatter should succeed"); + ensure!( + s.contains(item.as_str()), + "dialoguer must be broken as formatted prompt success doesnt contain prompt" + ); + tester.expect(sanatize(s)).context("expect choice item")?; + + tester + .expect(if choices.len() == index { + "\r\r" + } else { + "\r\n" + }) + .context("expect new line after choice item")?; + } + Ok(()) + } + fn show_selected( + tester: &mut CliTester, + prompt: &str, + choices: &[String], + selected_index: u64, + ) -> Result<()> { + let mut s = String::new(); + + let selected = choices[usize::try_from(selected_index)?].clone(); + tester + .formatter + .format_select_prompt_selection(&mut s, prompt, selected.as_str()) + .expect("diagluer theme formatter should succeed"); + ensure!( + s.contains(selected.as_str()), + "dialoguer must be broken as formatted prompt success doesnt contain prompt" + ); + tester.expect(sanatize(s)).context("expect choice item")?; + Ok(()) + } + + show_options(self.tester, &self.choices, None)?; + + for _ in 0..(chosen_index + 1) { + self.tester.send("j")?; + } + + self.tester.send(" ")?; + + for index in 0..(chosen_index + 1) { + show_options(self.tester, &self.choices, Some(usize::try_from(index)?))?; + } + + for _ in 0..self.choices.len() { + self.tester + .expect("\r") + .context("expect new line per option")?; + } + + self.tester + .expect("\r") + .context("expect new line after options")?; + + if report { + show_selected(self.tester, &self.prompt, &self.choices, chosen_index)?; + self.tester + .expect("\r\n") + .context("expect new line at end")?; + } + + Ok(self) + } +} + impl CliTester { pub fn new(args: I) -> Self where @@ -525,6 +672,16 @@ impl CliTester { Ok(()) } + pub fn expect_end_eventually_and_print(&mut self) -> Result<()> { + let before = self + .rexpect_session + .exp_eof() + .context("expected immediate end but got timed out")?; + println!("ended eventually with:"); + println!("{}", &before); + Ok(()) + } + pub fn expect_end_with_whitespace(&mut self) -> Result<()> { let before = self .rexpect_session @@ -551,6 +708,12 @@ impl CliTester { .context("send_line failed")?; Ok(()) } + + fn send(&mut self, s: &str) -> Result<()> { + self.rexpect_session.send(s).context("send failed")?; + self.rexpect_session.flush()?; + Ok(()) + } } /// sanatize unicode string for rexpect diff --git a/test_utils/src/relay.rs b/test_utils/src/relay.rs index 4ef34e6..50f6337 100644 --- a/test_utils/src/relay.rs +++ b/test_utils/src/relay.rs @@ -91,6 +91,27 @@ impl<'a> Relay<'a> { self.respond_eose(client_id, subscription_id.clone()) } + /// send collected events, filtered by filters, and eose + pub fn respond_standard_req( + &self, + client_id: u64, + subscription_id: &nostr::SubscriptionId, + filters: &[nostr::Filter], + ) -> Result { + // let t: Vec = self.events.iter().map(|e| e.kind).collect(); + // .filter(|e| filters.iter().any(|filter| filter.match_event(e))) + // println!("letsgo{:?}", t); + self.respond_events( + client_id, + subscription_id, + &self + .events + .iter() + .filter(|e| filters.iter().any(|filter| filter.match_event(e))) + .cloned() + .collect(), + ) + } /// listen, collect events and responds with event_listener to events or /// Ok(eventid) if event_listner is None pub async fn listen_until_close(&mut self) -> Result<()> { @@ -108,6 +129,8 @@ impl<'a> Relay<'a> { // break; } simple_websockets::Event::Message(client_id, message) => { + // println!("bla{:?}", &message); + println!( "{} Received a message from client #{}: {:?}", self.port, client_id, message @@ -118,8 +141,15 @@ impl<'a> Relay<'a> { break; } } + // println!("{:?}", &message); if let Ok(event) = get_nevent(&message) { + // println!("{:?}", &event); + // let t: Vec = self.events.iter().map(|e| e.kind).collect(); + // println!("before{:?}", t); self.events.push(event.clone()); + // let t: Vec = self.events.iter().map(|e| e.kind).collect(); + // println!("after{:?}", t); + if let Some(listner) = self.event_listener { listner(self, client_id, event)?; } else { @@ -132,7 +162,8 @@ impl<'a> Relay<'a> { if let Some(listner) = self.req_listener { listner(self, client_id, subscription_id, filters)?; } else { - self.respond_eose(client_id, subscription_id)?; + self.respond_standard_req(client_id, &subscription_id, &filters)?; + // self.respond_eose(client_id, subscription_id)?; } // respond with events // respond with EOSE -- cgit v1.2.3