diff options
| -rw-r--r-- | Cargo.lock | 130 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/client.rs | 23 | ||||
| -rw-r--r-- | src/git.rs | 17 | ||||
| -rw-r--r-- | src/login.rs | 3 | ||||
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/sub_commands/prs/create.rs | 279 | ||||
| -rw-r--r-- | test_utils/Cargo.toml | 1 | ||||
| -rw-r--r-- | test_utils/src/lib.rs | 39 | ||||
| -rw-r--r-- | test_utils/src/relay.rs | 196 | ||||
| -rw-r--r-- | tests/prs_create.rs | 455 |
11 files changed, 1089 insertions, 59 deletions
| @@ -863,6 +863,19 @@ dependencies = [ | |||
| 863 | ] | 863 | ] |
| 864 | 864 | ||
| 865 | [[package]] | 865 | [[package]] |
| 866 | name = "flume" | ||
| 867 | version = "0.10.14" | ||
| 868 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 869 | checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" | ||
| 870 | dependencies = [ | ||
| 871 | "futures-core", | ||
| 872 | "futures-sink", | ||
| 873 | "nanorand", | ||
| 874 | "pin-project", | ||
| 875 | "spin 0.9.8", | ||
| 876 | ] | ||
| 877 | |||
| 878 | [[package]] | ||
| 866 | name = "fnv" | 879 | name = "fnv" |
| 867 | version = "1.0.7" | 880 | version = "1.0.7" |
| 868 | source = "registry+https://github.com/rust-lang/crates.io-index" | 881 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -1219,6 +1232,19 @@ dependencies = [ | |||
| 1219 | ] | 1232 | ] |
| 1220 | 1233 | ||
| 1221 | [[package]] | 1234 | [[package]] |
| 1235 | name = "indicatif" | ||
| 1236 | version = "0.17.7" | ||
| 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1238 | checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" | ||
| 1239 | dependencies = [ | ||
| 1240 | "console", | ||
| 1241 | "instant", | ||
| 1242 | "number_prefix", | ||
| 1243 | "portable-atomic", | ||
| 1244 | "unicode-width", | ||
| 1245 | ] | ||
| 1246 | |||
| 1247 | [[package]] | ||
| 1222 | name = "inout" | 1248 | name = "inout" |
| 1223 | version = "0.1.3" | 1249 | version = "0.1.3" |
| 1224 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1250 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -1481,6 +1507,15 @@ dependencies = [ | |||
| 1481 | ] | 1507 | ] |
| 1482 | 1508 | ||
| 1483 | [[package]] | 1509 | [[package]] |
| 1510 | name = "nanorand" | ||
| 1511 | version = "0.7.0" | ||
| 1512 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1513 | checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" | ||
| 1514 | dependencies = [ | ||
| 1515 | "getrandom", | ||
| 1516 | ] | ||
| 1517 | |||
| 1518 | [[package]] | ||
| 1484 | name = "ngit" | 1519 | name = "ngit" |
| 1485 | version = "0.0.1" | 1520 | version = "0.0.1" |
| 1486 | dependencies = [ | 1521 | dependencies = [ |
| @@ -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]] |
| 1724 | name = "number_prefix" | ||
| 1725 | version = "0.4.0" | ||
| 1726 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1727 | checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" | ||
| 1728 | |||
| 1729 | [[package]] | ||
| 1687 | name = "object" | 1730 | name = "object" |
| 1688 | version = "0.32.1" | 1731 | version = "0.32.1" |
| 1689 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1732 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -1814,6 +1857,26 @@ dependencies = [ | |||
| 1814 | ] | 1857 | ] |
| 1815 | 1858 | ||
| 1816 | [[package]] | 1859 | [[package]] |
| 1860 | name = "pin-project" | ||
| 1861 | version = "1.1.3" | ||
| 1862 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1863 | checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" | ||
| 1864 | dependencies = [ | ||
| 1865 | "pin-project-internal", | ||
| 1866 | ] | ||
| 1867 | |||
| 1868 | [[package]] | ||
| 1869 | name = "pin-project-internal" | ||
| 1870 | version = "1.1.3" | ||
| 1871 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1872 | checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" | ||
| 1873 | dependencies = [ | ||
| 1874 | "proc-macro2", | ||
| 1875 | "quote", | ||
| 1876 | "syn 2.0.38", | ||
| 1877 | ] | ||
| 1878 | |||
| 1879 | [[package]] | ||
| 1817 | name = "pin-project-lite" | 1880 | name = "pin-project-lite" |
| 1818 | version = "0.2.13" | 1881 | version = "0.2.13" |
| 1819 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1882 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -1870,6 +1933,12 @@ dependencies = [ | |||
| 1870 | ] | 1933 | ] |
| 1871 | 1934 | ||
| 1872 | [[package]] | 1935 | [[package]] |
| 1936 | name = "portable-atomic" | ||
| 1937 | version = "1.4.3" | ||
| 1938 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1939 | checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" | ||
| 1940 | |||
| 1941 | [[package]] | ||
| 1873 | name = "ppv-lite86" | 1942 | name = "ppv-lite86" |
| 1874 | version = "0.2.17" | 1943 | version = "0.2.17" |
| 1875 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1944 | source = "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]] |
| 2555 | name = "simple-websockets" | ||
| 2556 | version = "0.1.6" | ||
| 2557 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2558 | checksum = "7f38cc14717bb624d10e9bb4fff30344e8f540c0d2c0f876f8fb0111d808ee7c" | ||
| 2559 | dependencies = [ | ||
| 2560 | "flume", | ||
| 2561 | "futures-util", | ||
| 2562 | "tokio", | ||
| 2563 | "tokio-tungstenite 0.19.0", | ||
| 2564 | ] | ||
| 2565 | |||
| 2566 | [[package]] | ||
| 2486 | name = "slab" | 2567 | name = "slab" |
| 2487 | version = "0.4.9" | 2568 | version = "0.4.9" |
| 2488 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2569 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -2524,6 +2605,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 2524 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" | 2605 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" |
| 2525 | 2606 | ||
| 2526 | [[package]] | 2607 | [[package]] |
| 2608 | name = "spin" | ||
| 2609 | version = "0.9.8" | ||
| 2610 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2611 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" | ||
| 2612 | dependencies = [ | ||
| 2613 | "lock_api", | ||
| 2614 | ] | ||
| 2615 | |||
| 2616 | [[package]] | ||
| 2527 | name = "static_assertions" | 2617 | name = "static_assertions" |
| 2528 | version = "1.1.0" | 2618 | version = "1.1.0" |
| 2529 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2619 | source = "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]] |
| 2717 | name = "tokio-tungstenite" | 2810 | name = "tokio-tungstenite" |
| 2811 | version = "0.19.0" | ||
| 2812 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2813 | checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c" | ||
| 2814 | dependencies = [ | ||
| 2815 | "futures-util", | ||
| 2816 | "log", | ||
| 2817 | "tokio", | ||
| 2818 | "tungstenite 0.19.0", | ||
| 2819 | ] | ||
| 2820 | |||
| 2821 | [[package]] | ||
| 2822 | name = "tokio-tungstenite" | ||
| 2718 | version = "0.20.1" | 2823 | version = "0.20.1" |
| 2719 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2824 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 2720 | checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" | 2825 | checksum = "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]] |
| 2807 | name = "tungstenite" | 2912 | name = "tungstenite" |
| 2913 | version = "0.19.0" | ||
| 2914 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2915 | checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67" | ||
| 2916 | dependencies = [ | ||
| 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]] | ||
| 2931 | name = "tungstenite" | ||
| 2808 | version = "0.20.1" | 2932 | version = "0.20.1" |
| 2809 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2933 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 2810 | checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" | 2934 | checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" |
| @@ -16,10 +16,12 @@ anyhow = "1.0.75" | |||
| 16 | async-trait = "0.1.73" | 16 | async-trait = "0.1.73" |
| 17 | chacha20poly1305 = "0.10.1" | 17 | chacha20poly1305 = "0.10.1" |
| 18 | clap = { version = "4.3.19", features = ["derive"] } | 18 | clap = { version = "4.3.19", features = ["derive"] } |
| 19 | console = "0.15.7" | ||
| 19 | dialoguer = "0.10.4" | 20 | dialoguer = "0.10.4" |
| 20 | directories = "5.0.1" | 21 | directories = "5.0.1" |
| 21 | futures = "0.3.28" | 22 | futures = "0.3.28" |
| 22 | git2 = "0.18.1" | 23 | git2 = "0.18.1" |
| 24 | indicatif = "0.17.7" | ||
| 23 | keyring = "2.0.5" | 25 | keyring = "2.0.5" |
| 24 | nostr = "0.24.0" | 26 | nostr = "0.24.0" |
| 25 | nostr-sdk = "0.24.0" | 27 | nostr-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 | ||
| 19 | pub struct Client { | 19 | pub 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)] |
| 56 | pub struct Params { | 70 | pub struct Params { |
| 57 | pub keys: Option<nostr::Keys>, | 71 | pub keys: Option<nostr::Keys>, |
| 72 | pub fallback_relays: Vec<String>, | ||
| 58 | } | 73 | } |
| 59 | 74 | ||
| 60 | impl Params { | 75 | impl 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 | } |
| @@ -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 @@ | |||
| 1 | use std::time::Duration; | ||
| 2 | |||
| 1 | use anyhow::{bail, Context, Result}; | 3 | use anyhow::{bail, Context, Result}; |
| 4 | use console::Term; | ||
| 5 | use futures::future::join_all; | ||
| 6 | use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; | ||
| 2 | use nostr::{prelude::sha1::Hash as Sha1Hash, EventBuilder, Marker, Tag, TagKind}; | 7 | use nostr::{prelude::sha1::Hash as Sha1Hash, EventBuilder, Marker, Tag, TagKind}; |
| 3 | 8 | ||
| 4 | use crate::{ | 9 | use 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 | |||
| 126 | async 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)` | ||
| 236 | fn 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>) | ||
| 240 | where | ||
| 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 | |||
| 267 | mod 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 | |||
| 314 | fn 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" | |||
| 13 | once_cell = "1.18.0" | 13 | once_cell = "1.18.0" |
| 14 | rand = "0.8" | 14 | rand = "0.8" |
| 15 | rexpect = { git = "https://github.com/phaer/rexpect.git", branch= "skip-ansi-escape-codes" } | 15 | rexpect = { git = "https://github.com/phaer/rexpect.git", branch= "skip-ansi-escape-codes" } |
| 16 | simple-websockets = "0.1.6" | ||
| 16 | strip-ansi-escapes = "0.2.0" | 17 | strip-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}; | |||
| 9 | use strip_ansi_escapes::strip_str; | 9 | use strip_ansi_escapes::strip_str; |
| 10 | 10 | ||
| 11 | pub mod git; | 11 | pub mod git; |
| 12 | pub mod relay; | ||
| 12 | 13 | ||
| 13 | pub static TEST_KEY_1_NSEC: &str = | 14 | pub 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 @@ | |||
| 1 | use std::collections::HashMap; | ||
| 2 | |||
| 3 | use anyhow::{bail, Result}; | ||
| 4 | use nostr::{ClientMessage, RelayMessage}; | ||
| 5 | |||
| 6 | use crate::CliTester; | ||
| 7 | |||
| 8 | type ListenerFunc<'a> = &'a dyn Fn(&mut Relay, u64, nostr::Event) -> Result<()>; | ||
| 9 | |||
| 10 | pub 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 | |||
| 18 | impl<'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 | |||
| 86 | fn 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 | |||
| 97 | pub 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) | ||
| 104 | pub 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 | |||
| 128 | pub 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 | ||
| 124 | mod when_no_commits_behind { | 124 | #[test] |
| 125 | #[serial] | ||
| 126 | fn 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 | |||
| 144 | mod 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 | } | ||