upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2023-10-01 00:00:00 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2023-10-01 00:00:00 +0100
commit000901c0cbca8464b5a89bcc93c5474f6564bafd (patch)
tree0ae11836c173ec6246e8b1eab7dc1e265e125426
parentb9a88672b8734448615354e3f46748d2fdc2f647 (diff)
feat(prs-create) send to multiple relays
add tests but these currently don't work when run together
-rw-r--r--Cargo.lock130
-rw-r--r--Cargo.toml2
-rw-r--r--src/client.rs23
-rw-r--r--src/git.rs17
-rw-r--r--src/login.rs3
-rw-r--r--src/main.rs3
-rw-r--r--src/sub_commands/prs/create.rs279
-rw-r--r--test_utils/Cargo.toml1
-rw-r--r--test_utils/src/lib.rs39
-rw-r--r--test_utils/src/relay.rs196
-rw-r--r--tests/prs_create.rs455
11 files changed, 1089 insertions, 59 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 15bc112..b8c0f04 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -863,6 +863,19 @@ dependencies = [
863] 863]
864 864
865[[package]] 865[[package]]
866name = "flume"
867version = "0.10.14"
868source = "registry+https://github.com/rust-lang/crates.io-index"
869checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
870dependencies = [
871 "futures-core",
872 "futures-sink",
873 "nanorand",
874 "pin-project",
875 "spin 0.9.8",
876]
877
878[[package]]
866name = "fnv" 879name = "fnv"
867version = "1.0.7" 880version = "1.0.7"
868source = "registry+https://github.com/rust-lang/crates.io-index" 881source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1219,6 +1232,19 @@ dependencies = [
1219] 1232]
1220 1233
1221[[package]] 1234[[package]]
1235name = "indicatif"
1236version = "0.17.7"
1237source = "registry+https://github.com/rust-lang/crates.io-index"
1238checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
1239dependencies = [
1240 "console",
1241 "instant",
1242 "number_prefix",
1243 "portable-atomic",
1244 "unicode-width",
1245]
1246
1247[[package]]
1222name = "inout" 1248name = "inout"
1223version = "0.1.3" 1249version = "0.1.3"
1224source = "registry+https://github.com/rust-lang/crates.io-index" 1250source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1481,6 +1507,15 @@ dependencies = [
1481] 1507]
1482 1508
1483[[package]] 1509[[package]]
1510name = "nanorand"
1511version = "0.7.0"
1512source = "registry+https://github.com/rust-lang/crates.io-index"
1513checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
1514dependencies = [
1515 "getrandom",
1516]
1517
1518[[package]]
1484name = "ngit" 1519name = "ngit"
1485version = "0.0.1" 1520version = "0.0.1"
1486dependencies = [ 1521dependencies = [
@@ -1489,11 +1524,13 @@ dependencies = [
1489 "async-trait", 1524 "async-trait",
1490 "chacha20poly1305", 1525 "chacha20poly1305",
1491 "clap", 1526 "clap",
1527 "console",
1492 "dialoguer", 1528 "dialoguer",
1493 "directories", 1529 "directories",
1494 "duplicate", 1530 "duplicate",
1495 "futures", 1531 "futures",
1496 "git2", 1532 "git2",
1533 "indicatif",
1497 "keyring", 1534 "keyring",
1498 "mockall", 1535 "mockall",
1499 "nostr", 1536 "nostr",
@@ -1591,7 +1628,7 @@ dependencies = [
1591 "tokio", 1628 "tokio",
1592 "tokio-rustls", 1629 "tokio-rustls",
1593 "tokio-socks", 1630 "tokio-socks",
1594 "tokio-tungstenite", 1631 "tokio-tungstenite 0.20.1",
1595 "url-fork", 1632 "url-fork",
1596 "webpki-roots", 1633 "webpki-roots",
1597 "ws_stream_wasm", 1634 "ws_stream_wasm",
@@ -1684,6 +1721,12 @@ dependencies = [
1684] 1721]
1685 1722
1686[[package]] 1723[[package]]
1724name = "number_prefix"
1725version = "0.4.0"
1726source = "registry+https://github.com/rust-lang/crates.io-index"
1727checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
1728
1729[[package]]
1687name = "object" 1730name = "object"
1688version = "0.32.1" 1731version = "0.32.1"
1689source = "registry+https://github.com/rust-lang/crates.io-index" 1732source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1814,6 +1857,26 @@ dependencies = [
1814] 1857]
1815 1858
1816[[package]] 1859[[package]]
1860name = "pin-project"
1861version = "1.1.3"
1862source = "registry+https://github.com/rust-lang/crates.io-index"
1863checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
1864dependencies = [
1865 "pin-project-internal",
1866]
1867
1868[[package]]
1869name = "pin-project-internal"
1870version = "1.1.3"
1871source = "registry+https://github.com/rust-lang/crates.io-index"
1872checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
1873dependencies = [
1874 "proc-macro2",
1875 "quote",
1876 "syn 2.0.38",
1877]
1878
1879[[package]]
1817name = "pin-project-lite" 1880name = "pin-project-lite"
1818version = "0.2.13" 1881version = "0.2.13"
1819source = "registry+https://github.com/rust-lang/crates.io-index" 1882source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1870,6 +1933,12 @@ dependencies = [
1870] 1933]
1871 1934
1872[[package]] 1935[[package]]
1936name = "portable-atomic"
1937version = "1.4.3"
1938source = "registry+https://github.com/rust-lang/crates.io-index"
1939checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b"
1940
1941[[package]]
1873name = "ppv-lite86" 1942name = "ppv-lite86"
1874version = "0.2.17" 1943version = "0.2.17"
1875source = "registry+https://github.com/rust-lang/crates.io-index" 1944source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2169,7 +2238,7 @@ dependencies = [
2169 "cc", 2238 "cc",
2170 "libc", 2239 "libc",
2171 "once_cell", 2240 "once_cell",
2172 "spin", 2241 "spin 0.5.2",
2173 "untrusted", 2242 "untrusted",
2174 "web-sys", 2243 "web-sys",
2175 "winapi", 2244 "winapi",
@@ -2483,6 +2552,18 @@ dependencies = [
2483] 2552]
2484 2553
2485[[package]] 2554[[package]]
2555name = "simple-websockets"
2556version = "0.1.6"
2557source = "registry+https://github.com/rust-lang/crates.io-index"
2558checksum = "7f38cc14717bb624d10e9bb4fff30344e8f540c0d2c0f876f8fb0111d808ee7c"
2559dependencies = [
2560 "flume",
2561 "futures-util",
2562 "tokio",
2563 "tokio-tungstenite 0.19.0",
2564]
2565
2566[[package]]
2486name = "slab" 2567name = "slab"
2487version = "0.4.9" 2568version = "0.4.9"
2488source = "registry+https://github.com/rust-lang/crates.io-index" 2569source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2524,6 +2605,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2524checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 2605checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
2525 2606
2526[[package]] 2607[[package]]
2608name = "spin"
2609version = "0.9.8"
2610source = "registry+https://github.com/rust-lang/crates.io-index"
2611checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
2612dependencies = [
2613 "lock_api",
2614]
2615
2616[[package]]
2527name = "static_assertions" 2617name = "static_assertions"
2528version = "1.1.0" 2618version = "1.1.0"
2529source = "registry+https://github.com/rust-lang/crates.io-index" 2619source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2625,6 +2715,7 @@ dependencies = [
2625 "once_cell", 2715 "once_cell",
2626 "rand", 2716 "rand",
2627 "rexpect 0.5.0 (git+https://github.com/phaer/rexpect.git?branch=skip-ansi-escape-codes)", 2717 "rexpect 0.5.0 (git+https://github.com/phaer/rexpect.git?branch=skip-ansi-escape-codes)",
2718 "simple-websockets",
2628 "strip-ansi-escapes", 2719 "strip-ansi-escapes",
2629] 2720]
2630 2721
@@ -2674,7 +2765,9 @@ dependencies = [
2674 "libc", 2765 "libc",
2675 "mio", 2766 "mio",
2676 "num_cpus", 2767 "num_cpus",
2768 "parking_lot",
2677 "pin-project-lite", 2769 "pin-project-lite",
2770 "signal-hook-registry",
2678 "socket2 0.5.4", 2771 "socket2 0.5.4",
2679 "tokio-macros", 2772 "tokio-macros",
2680 "windows-sys 0.48.0", 2773 "windows-sys 0.48.0",
@@ -2715,6 +2808,18 @@ dependencies = [
2715 2808
2716[[package]] 2809[[package]]
2717name = "tokio-tungstenite" 2810name = "tokio-tungstenite"
2811version = "0.19.0"
2812source = "registry+https://github.com/rust-lang/crates.io-index"
2813checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c"
2814dependencies = [
2815 "futures-util",
2816 "log",
2817 "tokio",
2818 "tungstenite 0.19.0",
2819]
2820
2821[[package]]
2822name = "tokio-tungstenite"
2718version = "0.20.1" 2823version = "0.20.1"
2719source = "registry+https://github.com/rust-lang/crates.io-index" 2824source = "registry+https://github.com/rust-lang/crates.io-index"
2720checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" 2825checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
@@ -2724,7 +2829,7 @@ dependencies = [
2724 "rustls", 2829 "rustls",
2725 "tokio", 2830 "tokio",
2726 "tokio-rustls", 2831 "tokio-rustls",
2727 "tungstenite", 2832 "tungstenite 0.20.1",
2728 "webpki-roots", 2833 "webpki-roots",
2729] 2834]
2730 2835
@@ -2805,6 +2910,25 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
2805 2910
2806[[package]] 2911[[package]]
2807name = "tungstenite" 2912name = "tungstenite"
2913version = "0.19.0"
2914source = "registry+https://github.com/rust-lang/crates.io-index"
2915checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67"
2916dependencies = [
2917 "byteorder",
2918 "bytes",
2919 "data-encoding",
2920 "http",
2921 "httparse",
2922 "log",
2923 "rand",
2924 "sha1",
2925 "thiserror",
2926 "url",
2927 "utf-8",
2928]
2929
2930[[package]]
2931name = "tungstenite"
2808version = "0.20.1" 2932version = "0.20.1"
2809source = "registry+https://github.com/rust-lang/crates.io-index" 2933source = "registry+https://github.com/rust-lang/crates.io-index"
2810checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" 2934checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
diff --git a/Cargo.toml b/Cargo.toml
index f7577a4..1571c02 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,10 +16,12 @@ anyhow = "1.0.75"
16async-trait = "0.1.73" 16async-trait = "0.1.73"
17chacha20poly1305 = "0.10.1" 17chacha20poly1305 = "0.10.1"
18clap = { version = "4.3.19", features = ["derive"] } 18clap = { version = "4.3.19", features = ["derive"] }
19console = "0.15.7"
19dialoguer = "0.10.4" 20dialoguer = "0.10.4"
20directories = "5.0.1" 21directories = "5.0.1"
21futures = "0.3.28" 22futures = "0.3.28"
22git2 = "0.18.1" 23git2 = "0.18.1"
24indicatif = "0.17.7"
23keyring = "2.0.5" 25keyring = "2.0.5"
24nostr = "0.24.0" 26nostr = "0.24.0"
25nostr-sdk = "0.24.0" 27nostr-sdk = "0.24.0"
diff --git a/src/client.rs b/src/client.rs
index a6f7dda..e0e0494 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -18,6 +18,7 @@ use nostr::Event;
18 18
19pub struct Client { 19pub struct Client {
20 client: nostr_sdk::Client, 20 client: nostr_sdk::Client,
21 pub fallback_relays: Vec<String>,
21} 22}
22 23
23#[async_trait] 24#[async_trait]
@@ -26,6 +27,7 @@ pub trait Connect {
26 fn default() -> Self; 27 fn default() -> Self;
27 fn new(opts: Params) -> Self; 28 fn new(opts: Params) -> Self;
28 async fn connect(&self) -> Result<()>; 29 async fn connect(&self) -> Result<()>;
30 async fn disconnect(&self) -> Result<()>;
29 async fn send_event_to(&self, url: &str, event: nostr::event::Event) -> Result<nostr::EventId>; 31 async fn send_event_to(&self, url: &str, event: nostr::event::Event) -> Result<nostr::EventId>;
30} 32}
31 33
@@ -34,19 +36,31 @@ impl Connect for Client {
34 fn default() -> Self { 36 fn default() -> Self {
35 Client { 37 Client {
36 client: nostr_sdk::Client::new(&nostr::Keys::generate()), 38 client: nostr_sdk::Client::new(&nostr::Keys::generate()),
39 fallback_relays: vec![
40 "ws://localhost:8080".to_string(),
41 "ws://localhost:8052".to_string(),
42 ],
37 } 43 }
38 } 44 }
39 fn new(opts: Params) -> Self { 45 fn new(opts: Params) -> Self {
40 Client { 46 Client {
41 client: nostr_sdk::Client::new(&opts.keys.unwrap_or(nostr::Keys::generate())), 47 client: nostr_sdk::Client::new(&opts.keys.unwrap_or(nostr::Keys::generate())),
48 fallback_relays: opts.fallback_relays,
42 } 49 }
43 } 50 }
44 async fn connect(&self) -> Result<()> { 51 async fn connect(&self) -> Result<()> {
45 self.client.add_relay("ws://localhost:8080", None).await?; 52 for relay in &self.fallback_relays {
53 self.client.add_relay(relay.as_str(), None).await?;
54 }
46 self.client.connect().await; 55 self.client.connect().await;
47 // self.client.s
48 Ok(()) 56 Ok(())
49 } 57 }
58
59 async fn disconnect(&self) -> Result<()> {
60 self.client.disconnect().await?;
61 Ok(())
62 }
63
50 async fn send_event_to(&self, url: &str, event: Event) -> Result<nostr::EventId> { 64 async fn send_event_to(&self, url: &str, event: Event) -> Result<nostr::EventId> {
51 Ok(self.client.send_event_to(url, event).await?) 65 Ok(self.client.send_event_to(url, event).await?)
52 } 66 }
@@ -55,6 +69,7 @@ impl Connect for Client {
55#[derive(Default)] 69#[derive(Default)]
56pub struct Params { 70pub struct Params {
57 pub keys: Option<nostr::Keys>, 71 pub keys: Option<nostr::Keys>,
72 pub fallback_relays: Vec<String>,
58} 73}
59 74
60impl Params { 75impl Params {
@@ -62,4 +77,8 @@ impl Params {
62 self.keys = Some(keys); 77 self.keys = Some(keys);
63 self 78 self
64 } 79 }
80 pub fn with_fallback_relays(mut self, fallback_relays: Vec<String>) -> Self {
81 self.fallback_relays = fallback_relays;
82 self
83 }
65} 84}
diff --git a/src/git.rs b/src/git.rs
index ddbc646..337444a 100644
--- a/src/git.rs
+++ b/src/git.rs
@@ -245,6 +245,23 @@ mod tests {
245 245
246 use super::*; 246 use super::*;
247 247
248 #[test]
249 fn get_commit_parent() -> Result<()> {
250 let test_repo = GitTestRepo::default();
251 let parent_oid = test_repo.populate()?;
252 std::fs::write(test_repo.dir.join("t100.md"), "some content")?;
253 let child_oid = test_repo.stage_and_commit("add t100.md")?;
254
255 let git_repo = Repo::from_path(&test_repo.dir)?;
256
257 assert_eq!(
258 // Sha1Hash::from_byte_array("bla".to_string().as_bytes()),
259 oid_to_sha1(&parent_oid),
260 git_repo.get_commit_parent(&oid_to_sha1(&child_oid))?,
261 );
262 Ok(())
263 }
264
248 mod make_patch_from_commit { 265 mod make_patch_from_commit {
249 use super::*; 266 use super::*;
250 #[test] 267 #[test]
diff --git a/src/login.rs b/src/login.rs
index a6ce76d..12fe76e 100644
--- a/src/login.rs
+++ b/src/login.rs
@@ -81,5 +81,8 @@ pub fn launch(nsec: &Option<String>, password: &Option<String>) -> Result<nostr:
81 .to_bech32() 81 .to_bech32()
82 .context("public key should always produce bech32")? 82 .context("public key should always produce bech32")?
83 ); 83 );
84
85 // fetching metdata
86
84 Ok(key) 87 Ok(key)
85} 88}
diff --git a/src/main.rs b/src/main.rs
index 9c37aa7..5c8518b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -24,6 +24,9 @@ pub struct Cli {
24 /// password to decrypt nsec 24 /// password to decrypt nsec
25 #[arg(short, long, global = true)] 25 #[arg(short, long, global = true)]
26 password: Option<String>, 26 password: Option<String>,
27 /// disable spinner animations
28 #[arg(long, action)]
29 disable_cli_spinners: bool,
27} 30}
28 31
29#[derive(Subcommand)] 32#[derive(Subcommand)]
diff --git a/src/sub_commands/prs/create.rs b/src/sub_commands/prs/create.rs
index 89ea652..3047e92 100644
--- a/src/sub_commands/prs/create.rs
+++ b/src/sub_commands/prs/create.rs
@@ -1,4 +1,9 @@
1use std::time::Duration;
2
1use anyhow::{bail, Context, Result}; 3use anyhow::{bail, Context, Result};
4use console::Term;
5use futures::future::join_all;
6use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
2use nostr::{prelude::sha1::Hash as Sha1Hash, EventBuilder, Marker, Tag, TagKind}; 7use nostr::{prelude::sha1::Hash as Sha1Hash, EventBuilder, Marker, Tag, TagKind};
3 8
4use crate::{ 9use crate::{
@@ -79,14 +84,245 @@ pub async fn launch(
79 .input(PromptInputParms::default().with_prompt("description (Optional)"))?, 84 .input(PromptInputParms::default().with_prompt("description (Optional)"))?,
80 }; 85 };
81 86
82 let root_commit = git_repo
83 .get_root_commit(to_branch.as_str())
84 .context("failed to get root commit of the repository")?;
85
86 // create PR event 87 // create PR event
87 88
88 let keys = login::launch(&cli_args.nsec, &cli_args.password)?; 89 let keys = login::launch(&cli_args.nsec, &cli_args.password)?;
89 90
91 let events =
92 generate_pr_and_patch_events(&title, &description, &to_branch, &git_repo, &ahead, keys)?;
93
94 let my_write_relays: Vec<String> = vec![
95 "ws://localhost:8051".to_string(),
96 "ws://localhost:8052".to_string(),
97 ];
98
99 let repo_read_relays: Vec<String> = vec![
100 "ws://localhost:8051".to_string(),
101 "ws://localhost:8053".to_string(),
102 ];
103
104 send_events(
105 events,
106 keys,
107 my_write_relays,
108 repo_read_relays,
109 !cli_args.disable_cli_spinners,
110 )
111 .await?;
112 // TODO check if there is already a similarly named PR
113
114 // println!("failures:");
115 // println!("ws://relay.anon.io Error: Payment Required");
116
117 // should we have a relays in Repository event?
118 // yes
119 //
120
121 // TODO connect to relays and post
122
123 Ok(())
124}
125
126async fn send_events(
127 events: Vec<nostr::Event>,
128 keys: nostr::Keys,
129 my_write_relays: Vec<String>,
130 repo_read_relays: Vec<String>,
131 animate: bool,
132) -> Result<()> {
133 let (_, _, _, all) = unique_and_duplicate_all(&my_write_relays, &repo_read_relays);
134
135 let client = Client::new(
136 ClientParams::default()
137 .with_keys(keys)
138 // .with_fallback_relays(vec!["ws://localhost:8080".to_string()]),
139 .with_fallback_relays(all.iter().map(std::string::ToString::to_string).collect()),
140 );
141
142 let term = Term::stdout();
143 term.write_line("connecting to relays...")?;
144 client.connect().await?;
145 term.clear_last_lines(1)?;
146
147 println!(
148 "posting 1 pull request with {} commits...",
149 events.len() - 1
150 );
151
152 let m = MultiProgress::new();
153 let pb_style = ProgressStyle::with_template(if animate {
154 " {spinner} {prefix} {bar} {pos}/{len} {msg}"
155 } else {
156 " - {prefix} {bar} {pos}/{len} {msg}"
157 })?
158 .progress_chars("##-");
159
160 let pb_after_style =
161 |symbol| ProgressStyle::with_template(format!(" {symbol} {}", "{prefix} {msg}",).as_str());
162 let pb_after_style_succeeded = pb_after_style(if animate {
163 console::style("✔".to_string())
164 .for_stderr()
165 .green()
166 .to_string()
167 } else {
168 "y".to_string()
169 })?;
170
171 let pb_after_style_failed = pb_after_style(if animate {
172 console::style("✘".to_string())
173 .for_stderr()
174 .red()
175 .to_string()
176 } else {
177 "x".to_string()
178 })?;
179
180 join_all(all.iter().map(|&relay| async {
181 let details = format!(
182 "{}{} {}",
183 if my_write_relays.iter().any(|r| relay.eq(r)) {
184 " [my-relay]"
185 } else {
186 ""
187 },
188 if repo_read_relays.iter().any(|r| relay.eq(r)) {
189 " [repo-relay]"
190 } else {
191 ""
192 },
193 *relay,
194 );
195 let pb = m.add(
196 ProgressBar::new(events.len() as u64)
197 .with_prefix(details.to_string())
198 .with_style(pb_style.clone()),
199 );
200 if animate {
201 pb.enable_steady_tick(Duration::from_millis(300));
202 }
203 pb.inc(0); // need to make pb display intially
204 let mut failed = false;
205 for event in &events {
206 match client.send_event_to(relay.as_str(), event.clone()).await {
207 Ok(_) => pb.inc(1),
208 Err(e) => {
209 pb.set_style(pb_after_style_failed.clone());
210 pb.finish_with_message(
211 console::style(
212 e.to_string()
213 .replace("relay pool error:", "error:")
214 .replace("event not published: ", ""),
215 )
216 .for_stderr()
217 .red()
218 .to_string(),
219 );
220 failed = true;
221 break;
222 }
223 };
224 }
225 if !failed {
226 pb.set_style(pb_after_style_succeeded.clone());
227 pb.finish_with_message("");
228 }
229 }))
230 .await;
231 client.disconnect().await?;
232 Ok(())
233}
234
235/// returns `(unique_vec1, unique_vec2, duplicates, all)`
236fn unique_and_duplicate_all<'a, S>(
237 vec1: &'a Vec<S>,
238 vec2: &'a Vec<S>,
239) -> (Vec<&'a S>, Vec<&'a S>, Vec<&'a S>, Vec<&'a S>)
240where
241 S: PartialEq,
242{
243 let mut vec1_u = vec![];
244 let mut vec2_u = vec![];
245 let mut dup = vec![];
246 let mut all = vec![];
247 for s1 in vec1 {
248 if vec2.iter().any(|s2| s1.eq(s2)) {
249 dup.push(s1);
250 } else {
251 vec1_u.push(s1);
252 }
253 }
254 for s2 in vec2 {
255 if !vec1.iter().any(|s1| s2.eq(s1)) {
256 vec2_u.push(s2);
257 }
258 }
259 for a in [&dup, &vec1_u, &vec2_u] {
260 for e in a {
261 all.push(&**e);
262 }
263 }
264 (vec1_u, vec2_u, dup, all)
265}
266
267mod tests_unique_and_duplicate {
268
269 #[test]
270 fn correct_number_of_unique_and_duplicate_items() {
271 let v1 = vec![
272 "t1".to_string(),
273 "t2".to_string(),
274 "t3".to_string(),
275 "t4".to_string(),
276 "t5".to_string(),
277 ];
278 let v2 = vec![
279 "t3".to_string(),
280 "t4".to_string(),
281 "t5".to_string(),
282 "t6".to_string(),
283 ];
284
285 let (v1_u, v2_u, d, a) = super::unique_and_duplicate_all(&v1, &v2);
286
287 assert_eq!(v1_u.len(), 2);
288 assert_eq!(v2_u.len(), 1);
289 assert_eq!(d.len(), 3);
290 assert_eq!(a.len(), 6);
291 }
292 #[test]
293 fn all_begins_with_duplicates() {
294 let v1 = vec![
295 "t1".to_string(),
296 "t2".to_string(),
297 "t3".to_string(),
298 "t4".to_string(),
299 "t5".to_string(),
300 ];
301 let v2 = vec![
302 "t3".to_string(),
303 "t4".to_string(),
304 "t5".to_string(),
305 "t6".to_string(),
306 ];
307
308 let (_, _, d, a) = super::unique_and_duplicate_all(&v1, &v2);
309
310 assert_eq!(a[0], d[0]);
311 }
312}
313
314fn generate_pr_and_patch_events(
315 title: &String,
316 description: &String,
317 to_branch: &str,
318 git_repo: &Repo,
319 commits: &Vec<Sha1Hash>,
320 keys: nostr::Keys,
321) -> Result<Vec<nostr::Event>> {
322 let root_commit = git_repo
323 .get_root_commit(to_branch)
324 .context("failed to get root commit of the repository")?;
325
90 let pr_event = EventBuilder::new( 326 let pr_event = EventBuilder::new(
91 nostr::event::Kind::Custom(318), 327 nostr::event::Kind::Custom(318),
92 format!("{title}\r\n\r\n{description}"), 328 format!("{title}\r\n\r\n{description}"),
@@ -103,12 +339,14 @@ pub async fn launch(
103 .to_event(&keys) 339 .to_event(&keys)
104 .context("failed to create pr event")?; 340 .context("failed to create pr event")?;
105 341
106 let mut patch_events = vec![]; 342 let pr_event_id = pr_event.id;
107 for commit in &ahead { 343
344 let mut events = vec![pr_event];
345 for commit in commits {
108 let commit_parent = git_repo 346 let commit_parent = git_repo
109 .get_commit_parent(commit) 347 .get_commit_parent(commit)
110 .context("failed to create patch event")?; 348 .context("failed to create patch event")?;
111 patch_events.push( 349 events.push(
112 EventBuilder::new( 350 EventBuilder::new(
113 nostr::event::Kind::Custom(317), 351 nostr::event::Kind::Custom(317),
114 git_repo 352 git_repo
@@ -119,7 +357,7 @@ pub async fn launch(
119 Tag::Hashtag(commit.to_string()), 357 Tag::Hashtag(commit.to_string()),
120 Tag::Hashtag(commit_parent.to_string()), 358 Tag::Hashtag(commit_parent.to_string()),
121 Tag::Event( 359 Tag::Event(
122 pr_event.id, 360 pr_event_id,
123 None, // TODO: add relay 361 None, // TODO: add relay
124 Some(Marker::Root), 362 Some(Marker::Root),
125 ), 363 ),
@@ -136,32 +374,11 @@ pub async fn launch(
136 // TODO: add relay tags 374 // TODO: add relay tags
137 ], 375 ],
138 ) 376 )
139 .to_event(&keys), 377 .to_event(&keys)?,
140 ); 378 );
141 } 379 }
142 380 Ok(events)
143 let client = Client::new(ClientParams::default().with_keys(keys));
144
145 println!("connecting...");
146 client.connect().await?;
147 println!("connected...");
148
149 // TODO check if there is already a similarly named PR
150 let _ = client
151 .send_event_to("ws://localhost:8080", pr_event)
152 .await?;
153 // TODO post each PR
154 // TODO report
155 println!("posted successfully to 4/5 of your relays and 0/4 of maintainers relays");
156 // should we have a relays in Repository event?
157 // yes
158 //
159
160 // TODO connect to relays and post
161
162 Ok(())
163} 381}
164
165// TODO 382// TODO
166// - find profile 383// - find profile
167// - file relays 384// - file relays
diff --git a/test_utils/Cargo.toml b/test_utils/Cargo.toml
index 1773d93..2d3555b 100644
--- a/test_utils/Cargo.toml
+++ b/test_utils/Cargo.toml
@@ -13,4 +13,5 @@ nostr = "0.24.0"
13once_cell = "1.18.0" 13once_cell = "1.18.0"
14rand = "0.8" 14rand = "0.8"
15rexpect = { git = "https://github.com/phaer/rexpect.git", branch= "skip-ansi-escape-codes" } 15rexpect = { git = "https://github.com/phaer/rexpect.git", branch= "skip-ansi-escape-codes" }
16simple-websockets = "0.1.6"
16strip-ansi-escapes = "0.2.0" 17strip-ansi-escapes = "0.2.0"
diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs
index 0f870f6..a9d818c 100644
--- a/test_utils/src/lib.rs
+++ b/test_utils/src/lib.rs
@@ -9,6 +9,7 @@ use rexpect::session::{Options, PtySession};
9use strip_ansi_escapes::strip_str; 9use strip_ansi_escapes::strip_str;
10 10
11pub mod git; 11pub mod git;
12pub mod relay;
12 13
13pub static TEST_KEY_1_NSEC: &str = 14pub static TEST_KEY_1_NSEC: &str =
14 "nsec1ppsg5sm2aexq06juxmu9evtutr6jkwkhp98exxxvwamhru9lyx9s3rwseq"; 15 "nsec1ppsg5sm2aexq06juxmu9evtutr6jkwkhp98exxxvwamhru9lyx9s3rwseq";
@@ -353,7 +354,12 @@ impl CliTester {
353 } 354 }
354 355
355 /// returns what came before expected message 356 /// returns what came before expected message
356 pub fn expect_eventually(&mut self, message: &str) -> Result<String> { 357 pub fn expect_eventually<S>(&mut self, message: S) -> Result<String>
358 where
359 S: Into<String>,
360 {
361 let message_string = message.into();
362 let message = message_string.as_str();
357 let before = self 363 let before = self
358 .rexpect_session 364 .rexpect_session
359 .exp_string(message) 365 .exp_string(message)
@@ -361,8 +367,27 @@ impl CliTester {
361 Ok(before) 367 Ok(before)
362 } 368 }
363 369
364 pub fn expect(&mut self, message: &str) -> Result<&mut Self> { 370 pub fn expect_after_whitespace<S>(&mut self, message: S) -> Result<&mut Self>
371 where
372 S: Into<String>,
373 {
374 assert_eq!("", self.expect_eventually(message)?.trim());
375 Ok(self)
376 }
377
378 pub fn expect<S>(&mut self, message: S) -> Result<&mut Self>
379 where
380 S: Into<String>,
381 {
382 let message_string = message.into();
383 let message = message_string.as_str();
365 let before = self.expect_eventually(message)?; 384 let before = self.expect_eventually(message)?;
385 if !before.is_empty() {
386 std::fs::write("aaaaaaaaaaaa.txt", before.clone())?;
387
388 // let mut output = std::fs::File::create("aaaaaaaaaaa.txt")?;
389 // write!(output, "{}", *before);
390 }
366 ensure!( 391 ensure!(
367 before.is_empty(), 392 before.is_empty(),
368 format!( 393 format!(
@@ -397,6 +422,16 @@ impl CliTester {
397 assert_eq!(before, message); 422 assert_eq!(before, message);
398 Ok(()) 423 Ok(())
399 } 424 }
425
426 pub fn expect_end_with_whitespace(&mut self) -> Result<()> {
427 let before = self
428 .rexpect_session
429 .exp_eof()
430 .context("expected immediate end but got timed out")?;
431 assert_eq!(before.trim(), "");
432 Ok(())
433 }
434
400 pub fn expect_end_eventually(&mut self) -> Result<String> { 435 pub fn expect_end_eventually(&mut self) -> Result<String> {
401 self.rexpect_session 436 self.rexpect_session
402 .exp_eof() 437 .exp_eof()
diff --git a/test_utils/src/relay.rs b/test_utils/src/relay.rs
new file mode 100644
index 0000000..6de3618
--- /dev/null
+++ b/test_utils/src/relay.rs
@@ -0,0 +1,196 @@
1use std::collections::HashMap;
2
3use anyhow::{bail, Result};
4use nostr::{ClientMessage, RelayMessage};
5
6use crate::CliTester;
7
8type ListenerFunc<'a> = &'a dyn Fn(&mut Relay, u64, nostr::Event) -> Result<()>;
9
10pub struct Relay<'a> {
11 port: u16,
12 event_hub: simple_websockets::EventHub,
13 clients: HashMap<u64, simple_websockets::Responder>,
14 pub events: Vec<nostr::Event>,
15 event_listener: Option<ListenerFunc<'a>>,
16}
17
18impl<'a> Relay<'a> {
19 pub fn new(port: u16, event_listener: Option<ListenerFunc<'a>>) -> Self {
20 let event_hub = simple_websockets::launch(port)
21 .unwrap_or_else(|_| panic!("failed to listen on port {port}"));
22 Self {
23 port,
24 events: vec![],
25 event_hub,
26 clients: HashMap::new(),
27 event_listener,
28 }
29 }
30 pub fn respond_ok(
31 &self,
32 client_id: u64,
33 event: nostr::Event,
34 error: Option<&str>,
35 ) -> Result<bool> {
36 let responder = self.clients.get(&client_id).unwrap();
37
38 let ok_json = RelayMessage::Ok {
39 event_id: event.id,
40 status: error.is_none(),
41 message: error.unwrap_or("").to_string(),
42 }
43 .as_json();
44 // bail!(format!("{}", &ok_json));
45 Ok(responder.send(simple_websockets::Message::Text(ok_json)))
46 }
47 /// listen, collect events and responds with event_listener to events or
48 /// Ok(eventid) if event_listner is None
49 pub async fn listen_until_close(&mut self) -> Result<()> {
50 loop {
51 println!("polling");
52 match self.event_hub.poll_async().await {
53 simple_websockets::Event::Connect(client_id, responder) => {
54 // add their Responder to our `clients` map:
55 self.clients.insert(client_id, responder);
56 }
57 simple_websockets::Event::Disconnect(client_id) => {
58 // remove the disconnected client from the clients map:
59 println!("{} disconnected", self.port);
60 self.clients.remove(&client_id);
61 break;
62 }
63 simple_websockets::Event::Message(client_id, message) => {
64 println!(
65 "Received a message from client #{}: {:?}",
66 client_id, message
67 );
68
69 if let Ok(event) = get_nevent(message) {
70 self.events.push(event.clone());
71 if let Some(listner) = self.event_listener {
72 listner(self, client_id, event)?;
73 } else {
74 self.respond_ok(client_id, event, None)?;
75 }
76 }
77 }
78 }
79 }
80 println!("stop polling");
81 println!("we may not be polling but the tcplistner is still listening");
82 Ok(())
83 }
84}
85
86fn get_nevent(message: simple_websockets::Message) -> Result<nostr::Event> {
87 if let simple_websockets::Message::Text(s) = message.clone() {
88 let cm_result = ClientMessage::from_json(s);
89 if let Ok(ClientMessage::Event(event)) = cm_result {
90 let e = *event;
91 return Ok(e.clone());
92 }
93 }
94 bail!("not nostr event")
95}
96
97pub enum Message {
98 Event,
99 // Request,
100}
101
102/// leaves trailing whitespace and only compatible with --no-cli-spinners flag
103/// relays tuple: (title,successful,message)
104pub fn expect_send_with_progress(
105 p: &mut CliTester,
106 relays: Vec<(&str, bool, &str)>,
107 event_count: u16,
108) -> Result<()> {
109 p.expect(format!(
110 " - {} -------------------- 0/{event_count}",
111 &relays[0].0
112 ))?;
113 for relay in &relays {
114 // if successful
115 if relay.1 {
116 p.expect_eventually(format!(" y {}", relay.0))?;
117 } else {
118 p.expect_eventually(format!(" x {} {}", relay.0, relay.2))?;
119 }
120 // could check that before only contains titles:
121 // - # y x n/n and whitespace
122 // let before = p.expect_eventually(format!(" â {title}"))?;
123 // assert_eq!("", before.trim());
124 }
125 Ok(())
126}
127
128pub fn expect_send_with_progress_exact_interaction(
129 p: &mut CliTester,
130 titles: Vec<&str>,
131 count: u16,
132) -> Result<()> {
133 let whitespace_mid = " \r\n";
134 let whitespace_end = " \r\r\r";
135
136 p.expect(format!(
137 " - {} -------------------- 0/{count} \r",
138 titles[0]
139 ))?;
140 p.expect(format!(
141 " - {} -------------------- 0/{count}{whitespace_mid}",
142 titles[0]
143 ))?;
144 p.expect(format!(
145 " - {} -------------------- 0/{count} \r\r",
146 titles[1]
147 ))?;
148
149 let generate_text = |title: &str, num: u16, confirmed_complete: bool| -> String {
150 let symbol = if confirmed_complete && num.eq(&count) {
151 "â"
152 } else {
153 "-"
154 };
155 let bar = match (num, count) {
156 (0, _) => "--------------------",
157 (1, 2) => "###########---------",
158 (x, y) => {
159 if x.eq(&y) {
160 "####################"
161 } else {
162 "--unknown--"
163 }
164 }
165 };
166 format!(
167 " {symbol} {title} {bar} {num}/{count}{}",
168 if (&title).eq(titles.last().unwrap()) {
169 whitespace_end
170 } else {
171 whitespace_mid
172 }
173 )
174 };
175 let mut nums: HashMap<&str, u16> = HashMap::new();
176 for title in &titles {
177 nums.insert(title, 0);
178 p.expect(generate_text(title, 0, false))?;
179 }
180 loop {
181 for selected_title in &titles {
182 for title in &titles {
183 if title.eq(selected_title) {
184 let new_num = nums.get(title).unwrap() + 1;
185 if new_num.gt(&count) {
186 return Ok(());
187 }
188 nums.insert(title, new_num);
189 p.expect(generate_text(title, *nums.get(title).unwrap(), false))?;
190 } else {
191 p.expect(generate_text(title, *nums.get(title).unwrap(), true))?;
192 }
193 }
194 }
195 }
196}
diff --git a/tests/prs_create.rs b/tests/prs_create.rs
index d598e34..0863496 100644
--- a/tests/prs_create.rs
+++ b/tests/prs_create.rs
@@ -121,12 +121,36 @@ mod when_commits_behind_ask_to_proceed {
121 } 121 }
122} 122}
123 123
124mod when_no_commits_behind { 124#[test]
125#[serial]
126fn cli_message_creating_patches() -> Result<()> {
127 let test_repo = GitTestRepo::default();
128 test_repo.populate()?;
129 // create feature branch with 2 commit ahead
130 test_repo.create_branch("feature")?;
131 test_repo.checkout("feature")?;
132 std::fs::write(test_repo.dir.join("t3.md"), "some content")?;
133 test_repo.stage_and_commit("add t3.md")?;
134 std::fs::write(test_repo.dir.join("t4.md"), "some content")?;
135 test_repo.stage_and_commit("add t4.md")?;
136
137 let mut p = CliTester::new_from_dir(&test_repo.dir, ["prs", "create"]);
138
139 p.expect("creating patch for 2 commits from 'head' that can be merged into 'main'")?;
140 p.exit()?;
141 Ok(())
142}
143
144mod sends_pr_and_2_patches_to_3_relays {
145 use futures::join;
146 use test_utils::relay::Relay;
147
125 use super::*; 148 use super::*;
126 149
127 #[test] 150 static PR_KIND: u64 = 318;
128 #[serial] 151 static PATCH_KIND: u64 = 317;
129 fn message_for_creating_patches() -> Result<()> { 152
153 fn prep_git_repo() -> Result<GitTestRepo> {
130 let test_repo = GitTestRepo::default(); 154 let test_repo = GitTestRepo::default();
131 test_repo.populate()?; 155 test_repo.populate()?;
132 // create feature branch with 2 commit ahead 156 // create feature branch with 2 commit ahead
@@ -136,28 +160,417 @@ mod when_no_commits_behind {
136 test_repo.stage_and_commit("add t3.md")?; 160 test_repo.stage_and_commit("add t3.md")?;
137 std::fs::write(test_repo.dir.join("t4.md"), "some content")?; 161 std::fs::write(test_repo.dir.join("t4.md"), "some content")?;
138 test_repo.stage_and_commit("add t4.md")?; 162 test_repo.stage_and_commit("add t4.md")?;
163 Ok(test_repo)
164 }
139 165
140 let mut p = CliTester::new_from_dir(&test_repo.dir, ["prs", "create"]); 166 fn cli_tester_create_pr(git_repo: &GitTestRepo) -> CliTester {
167 CliTester::new_from_dir(
168 &git_repo.dir,
169 [
170 "--nsec",
171 TEST_KEY_1_NSEC,
172 "--disable-cli-spinners",
173 "prs",
174 "create",
175 "--title",
176 "example",
177 "--description",
178 "example",
179 ],
180 )
181 }
141 182
142 p.expect("creating patch for 2 commits from 'head' that can be merged into 'main'")?; 183 fn expect_msgs_first(p: &mut CliTester) -> Result<()> {
143 p.exit()?; 184 p.expect("creating patch for 2 commits from 'head' that can be merged into 'main'\r\n")?;
185 p.expect(
186 "logged in as npub175lyhnt6nn00qjw0v3navw9pxgv43txnku0tpxprl4h6mvpr6a5qlphudg\r\n",
187 )?;
188 p.expect("connecting to relays...\r\n")?;
189 p.expect("\r")?;
190 p.expect("posting 1 pull request with 2 commits...\r\n")?;
191 Ok(())
192 }
193
194 async fn prep_run_create_pr() -> Result<(Relay<'static>, Relay<'static>, Relay<'static>)> {
195 let git_repo = prep_git_repo()?;
196
197 let (mut r51, mut r52, mut r53) = (
198 Relay::new(8051, None),
199 Relay::new(8052, None),
200 Relay::new(8053, None),
201 );
202
203 // // check relay had the right number of events
204 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
205 let mut p = cli_tester_create_pr(&git_repo);
206 p.expect_end_eventually()?;
207 Ok(())
208 });
209
210 // launch relay
211 let _ = join!(
212 r51.listen_until_close(),
213 r52.listen_until_close(),
214 r53.listen_until_close(),
215 );
216 cli_tester_handle.join().unwrap()?;
217 Ok((r51, r52, r53))
218 }
219
220 #[test]
221 #[serial]
222 fn only_1_pr_kind_event_sent_to_each_relay() -> Result<()> {
223 let (r51, r52, r53) = futures::executor::block_on(prep_run_create_pr())?;
224 for relay in [&r51, &r52, &r53] {
225 assert_eq!(
226 relay
227 .events
228 .iter()
229 .filter(|e| e.kind.as_u64().eq(&PR_KIND))
230 .count(),
231 1,
232 );
233 }
234 Ok(())
235 }
236
237 #[test]
238 #[serial]
239 fn only_2_patch_kind_events_sent_to_each_relay() -> Result<()> {
240 let (r51, r52, r53) = futures::executor::block_on(prep_run_create_pr())?;
241 for relay in [&r51, &r52, &r53] {
242 assert_eq!(
243 relay
244 .events
245 .iter()
246 .filter(|e| e.kind.as_u64().eq(&PATCH_KIND))
247 .count(),
248 2,
249 );
250 }
144 Ok(()) 251 Ok(())
145 } 252 }
146}
147 253
148// #[test] 254 #[test]
149// #[serial] 255 #[serial]
150// fn succeeds_with_text_logged_in_as_npub() -> Result<()> { 256 fn patch_content_contains_patch_in_email_format() -> Result<()> {
151// with_fresh_config(|| { 257 let (r51, r52, r53) = futures::executor::block_on(prep_run_create_pr())?;
152// let mut p = CliTester::new(["login"]); 258 for relay in [&r51, &r52, &r53] {
259 let patch_events: Vec<&nostr::Event> = relay
260 .events
261 .iter()
262 .filter(|e| e.kind.as_u64().eq(&PATCH_KIND))
263 .collect();
264
265 assert_eq!(
266 patch_events[0].content,
267 "\
268 From fe973a840fba2a8ab37dd505c154854a69a6505c Mon Sep 17 00:00:00 2001\n\
269 From: Joe Bloggs <joe.bloggs@pm.me>\n\
270 Date: Thu, 1 Jan 1970 00:00:00 +0000\n\
271 Subject: [PATCH] add t4.md\n\
272 \n\
273 ---\n \
274 t4.md | 1 +\n \
275 1 file changed, 1 insertion(+)\n \
276 create mode 100644 t4.md\n\
277 \n\
278 diff --git a/t4.md b/t4.md\n\
279 new file mode 100644\n\
280 index 0000000..f0eec86\n\
281 --- /dev/null\n\
282 +++ b/t4.md\n\
283 @@ -0,0 +1 @@\n\
284 +some content\n\\ \
285 No newline at end of file\n\
286 --\n\
287 libgit2 1.7.1\n\
288 \n\
289 ",
290 );
291 assert_eq!(
292 patch_events[1].content,
293 "\
294 From 232efb37ebc67692c9e9ff58b83c0d3d63971a0a Mon Sep 17 00:00:00 2001\n\
295 From: Joe Bloggs <joe.bloggs@pm.me>\n\
296 Date: Thu, 1 Jan 1970 00:00:00 +0000\n\
297 Subject: [PATCH] add t3.md\n\
298 \n\
299 ---\n \
300 t3.md | 1 +\n \
301 1 file changed, 1 insertion(+)\n \
302 create mode 100644 t3.md\n\
303 \n\
304 diff --git a/t3.md b/t3.md\n\
305 new file mode 100644\n\
306 index 0000000..f0eec86\n\
307 --- /dev/null\n\
308 +++ b/t3.md\n\
309 @@ -0,0 +1 @@\n\
310 +some content\n\\ \
311 No newline at end of file\n\
312 --\n\
313 libgit2 1.7.1\n\
314 \n\
315 ",
316 );
317 }
318 Ok(())
319 }
320
321 mod pr_tags {
322 use super::*;
323 #[test]
324 #[serial]
325 fn pr_tags_repo_commit() -> Result<()> {
326 let (r51, r52, r53) = futures::executor::block_on(prep_run_create_pr())?;
327 for relay in [&r51, &r52, &r53] {
328 let pr_event: &nostr::Event = relay
329 .events
330 .iter()
331 .find(|e| e.kind.as_u64().eq(&PR_KIND))
332 .unwrap();
333
334 // root commit 't' tag
335 assert!(pr_event.tags.iter().any(|t| t.as_vec()[0].eq("t")
336 && t.as_vec()[1].eq("r-9ee507fc4357d7ee16a5d8901bedcd103f23c17d")));
337 }
338 Ok(())
339 }
340 }
341
342 mod patch_tags {
343 use super::*;
344 #[test]
345 #[serial]
346 fn patch_tags_correctly_formatted() -> Result<()> {
347 let (r51, r52, r53) = futures::executor::block_on(prep_run_create_pr())?;
348 for relay in [&r51, &r52, &r53] {
349 let patch_events: Vec<&nostr::Event> = relay
350 .events
351 .iter()
352 .filter(|e| e.kind.as_u64().eq(&PATCH_KIND))
353 .collect();
354
355 static COMMIT_ID: &str = "fe973a840fba2a8ab37dd505c154854a69a6505c";
356 let most_recent_patch = patch_events[0];
357
358 // commit 't' and 'commit' tag
359 assert!(
360 most_recent_patch
361 .tags
362 .iter()
363 .any(|t| t.as_vec()[0].eq("t") && t.as_vec()[1].eq(COMMIT_ID))
364 );
365 assert!(
366 most_recent_patch
367 .tags
368 .iter()
369 .any(|t| t.as_vec()[0].eq("commit") && t.as_vec()[1].eq(COMMIT_ID))
370 );
371
372 // commit parent 't' and 'parent-commit' tag
373 static COMMIT_PARENT_ID: &str = "232efb37ebc67692c9e9ff58b83c0d3d63971a0a";
374 assert!(
375 most_recent_patch
376 .tags
377 .iter()
378 .any(|t| t.as_vec()[0].eq("t") && t.as_vec()[1].eq(COMMIT_PARENT_ID))
379 );
380 assert!(most_recent_patch.tags.iter().any(
381 |t| t.as_vec()[0].eq("parent-commit") && t.as_vec()[1].eq(COMMIT_PARENT_ID)
382 ));
383
384 // root commit 't' tag
385 assert!(most_recent_patch.tags.iter().any(|t| t.as_vec()[0].eq("t")
386 && t.as_vec()[1].eq("r-9ee507fc4357d7ee16a5d8901bedcd103f23c17d")));
387 }
388 Ok(())
389 }
390
391 #[test]
392 #[serial]
393 fn patch_tags_pr_event_as_root() -> Result<()> {
394 let (r51, r52, r53) = futures::executor::block_on(prep_run_create_pr())?;
395 for relay in [&r51, &r52, &r53] {
396 let patch_events: Vec<&nostr::Event> = relay
397 .events
398 .iter()
399 .filter(|e| e.kind.as_u64().eq(&PATCH_KIND))
400 .collect();
401
402 let most_recent_patch = patch_events[0];
403 let pr_event = relay
404 .events
405 .iter()
406 .find(|e| e.kind.as_u64().eq(&PR_KIND))
407 .unwrap();
408
409 let root_event_tag = most_recent_patch
410 .tags
411 .iter()
412 .find(|t| {
413 t.as_vec()[0].eq("e") && t.as_vec().len().eq(&4) && t.as_vec()[3].eq("root")
414 })
415 .unwrap();
416
417 assert_eq!(root_event_tag.as_vec()[1], pr_event.id.to_string());
418 }
419 Ok(())
420 }
421 }
422
423 mod cli_ouput {
424 use super::*;
425
426 async fn run_test_async() -> Result<()> {
427 let git_repo = prep_git_repo()?;
153 428
154// p.expect_input(EXPECTED_NSEC_PROMPT)? 429 let (mut r51, mut r52, mut r53) = (
155// .succeeds_with(TEST_KEY_1_NSEC)?; 430 Relay::new(8051, None),
431 Relay::new(8052, None),
432 Relay::new(8053, None),
433 );
156 434
157// p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)? 435 // // check relay had the right number of events
158// .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)? 436 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
159// .succeeds_with(TEST_PASSWORD)?; 437 let mut p = cli_tester_create_pr(&git_repo);
438 expect_msgs_first(&mut p)?;
439 relay::expect_send_with_progress(
440 &mut p,
441 vec![
442 (" [my-relay] [repo-relay] ws://localhost:8051", true, ""),
443 (" [my-relay] ws://localhost:8052", true, ""),
444 (" [repo-relay] ws://localhost:8053", true, ""),
445 ],
446 3,
447 )?;
448 p.expect_end_with_whitespace()?;
449 Ok(())
450 });
160 451
161// p.expect_end_with(format!("logged in as {}\r\n", 452 // launch relay
162// TEST_KEY_1_NPUB).as_str()) }) 453 let _ = join!(
163// } 454 r51.listen_until_close(),
455 r52.listen_until_close(),
456 r53.listen_until_close(),
457 );
458 cli_tester_handle.join().unwrap()?;
459 Ok(())
460 }
461
462 #[test]
463 #[serial]
464 fn check_cli_output() -> Result<()> {
465 futures::executor::block_on(run_test_async())?;
466 Ok(())
467 }
468 }
469
470 mod first_event_rejected_by_1_relay {
471 use super::*;
472
473 mod only_first_rejected_event_sent_to_relay {
474 use super::*;
475
476 async fn run_test_async() -> Result<()> {
477 let git_repo = prep_git_repo()?;
478
479 let (mut r51, mut r52, mut r53) = (
480 Relay::new(8051, None),
481 Relay::new(
482 8052,
483 Some(&|relay, client_id, event| -> Result<()> {
484 relay.respond_ok(client_id, event, Some("Payment Required"))?;
485 Ok(())
486 }),
487 ),
488 Relay::new(8053, None),
489 );
490
491 // // check relay had the right number of events
492 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
493 let mut p = cli_tester_create_pr(&git_repo);
494 p.expect_end_eventually()?;
495 Ok(())
496 });
497
498 // launch relay
499 let _ = join!(
500 r51.listen_until_close(),
501 r52.listen_until_close(),
502 r53.listen_until_close(),
503 );
504 cli_tester_handle.join().unwrap()?;
505
506 assert_eq!(r52.events.len(), 1);
507
508 Ok(())
509 }
510
511 #[test]
512 #[serial]
513 fn only_first_rejected_event_sent_to_relay() -> Result<()> {
514 futures::executor::block_on(run_test_async())?;
515 Ok(())
516 }
517 }
518
519 mod cli_show_rejection_with_comment {
520 use super::*;
521
522 async fn run_test_async() -> Result<(Relay<'static>, Relay<'static>, Relay<'static>)> {
523 let git_repo = prep_git_repo()?;
524
525 let (mut r51, mut r52, mut r53) = (
526 Relay::new(8051, None),
527 Relay::new(
528 8052,
529 Some(&|relay, client_id, event| -> Result<()> {
530 relay.respond_ok(client_id, event, Some("Payment Required"))?;
531 Ok(())
532 }),
533 ),
534 Relay::new(8053, None),
535 );
536
537 // // check relay had the right number of events
538 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
539 let mut p = cli_tester_create_pr(&git_repo);
540 expect_msgs_first(&mut p)?;
541 relay::expect_send_with_progress(
542 &mut p,
543 vec![
544 (" [my-relay] [repo-relay] ws://localhost:8051", true, ""),
545 (
546 " [my-relay] ws://localhost:8052",
547 false,
548 "error: Payment Required",
549 ),
550 (" [repo-relay] ws://localhost:8053", true, ""),
551 ],
552 3,
553 )?;
554 p.expect_end_with_whitespace()?;
555 Ok(())
556 });
557
558 // launch relay
559 let _ = join!(
560 r51.listen_until_close(),
561 r52.listen_until_close(),
562 r53.listen_until_close(),
563 );
564 cli_tester_handle.join().unwrap()?;
565 Ok((r51, r52, r53))
566 }
567
568 #[test]
569 #[serial]
570 fn cli_show_rejection_with_comment() -> Result<()> {
571 futures::executor::block_on(run_test_async())?;
572 Ok(())
573 }
574 }
575 }
576}