upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests/ngit
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2024-09-24 19:22:23 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2024-09-24 19:48:14 +0100
commit967de64e19c05a2f14e7c97da000c1f3b8ccc4ed (patch)
treea675629a7d7731815a79beb3dfefa293e8fa9acc /tests/ngit
parentd01380f7b3efebc9c40a2e71c2ddd635fa936be4 (diff)
test: restructure ngit integration tests
so that each file is ran as a seperate crate this makes it easier to see which tests are causing other tests to fail as they are in a smaller group.
Diffstat (limited to 'tests/ngit')
-rw-r--r--tests/ngit/init.rs706
-rw-r--r--tests/ngit/list.rs1549
-rw-r--r--tests/ngit/login.rs1186
-rw-r--r--tests/ngit/main.rs6
-rw-r--r--tests/ngit/pull.rs615
-rw-r--r--tests/ngit/push.rs531
-rw-r--r--tests/ngit/send.rs1901
7 files changed, 0 insertions, 6494 deletions
diff --git a/tests/ngit/init.rs b/tests/ngit/init.rs
deleted file mode 100644
index c8390e3..0000000
--- a/tests/ngit/init.rs
+++ /dev/null
@@ -1,706 +0,0 @@
1use anyhow::Result;
2use nostr_sdk::Kind;
3use serial_test::serial;
4use test_utils::{git::GitTestRepo, *};
5
6fn expect_msgs_first(p: &mut CliTester) -> Result<()> {
7 p.expect("searching for profile...\r\n")?;
8 p.expect("logged in as fred\r\n")?;
9 // // p.expect("searching for existing claims on repository...\r\n")?;
10 p.expect("publishing repostory reference...\r\n")?;
11 Ok(())
12}
13
14fn expect_msgs_after(p: &mut CliTester) -> Result<()> {
15 p.expect_after_whitespace("maintainers.yaml created. commit and push.\r\n")?;
16 p.expect(
17 "this optional file helps in identifying who the maintainers are over time through the commit history\r\n",
18 )?;
19 Ok(())
20}
21
22fn get_cli_args() -> Vec<&'static str> {
23 vec![
24 "--nsec",
25 TEST_KEY_1_NSEC,
26 "--password",
27 TEST_PASSWORD,
28 "--disable-cli-spinners",
29 "init",
30 "--title",
31 "example-name",
32 "--identifier",
33 "example-identifier",
34 "--description",
35 "example-description",
36 "--web",
37 "https://exampleproject.xyz",
38 "https://gitworkshop.dev/123",
39 "--relays",
40 "ws://localhost:8055",
41 "ws://localhost:8056",
42 "--clone-url",
43 "https://git.myhosting.com/my-repo.git",
44 "--earliest-unique-commit",
45 "9ee507fc4357d7ee16a5d8901bedcd103f23c17d",
46 "--other-maintainers",
47 TEST_KEY_1_NPUB,
48 ]
49}
50
51mod when_repo_not_previously_claimed {
52 use super::*;
53
54 mod when_repo_relays_specified_as_arguments {
55 use futures::join;
56 use test_utils::relay::Relay;
57
58 use super::*;
59
60 fn prep_git_repo() -> Result<GitTestRepo> {
61 let test_repo = GitTestRepo::without_repo_in_git_config();
62 test_repo.populate()?;
63 test_repo.add_remote("origin", "https://localhost:1000")?;
64 Ok(test_repo)
65 }
66
67 fn cli_tester_init(git_repo: &GitTestRepo) -> CliTester {
68 CliTester::new_from_dir(&git_repo.dir, get_cli_args())
69 }
70
71 async fn prep_run_init() -> Result<(
72 Relay<'static>,
73 Relay<'static>,
74 Relay<'static>,
75 Relay<'static>,
76 Relay<'static>,
77 Relay<'static>,
78 )> {
79 let git_repo = prep_git_repo()?;
80 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
81 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
82 Relay::new(
83 8051,
84 None,
85 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
86 relay.respond_events(
87 client_id,
88 &subscription_id,
89 &vec![
90 generate_test_key_1_metadata_event("fred"),
91 generate_test_key_1_relay_list_event(),
92 ],
93 )?;
94 Ok(())
95 }),
96 ),
97 Relay::new(8052, None, None),
98 Relay::new(8053, None, None),
99 Relay::new(8055, None, None),
100 Relay::new(8056, None, None),
101 Relay::new(8057, None, None),
102 );
103
104 // // check relay had the right number of events
105 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
106 let mut p = cli_tester_init(&git_repo);
107 p.expect_end_eventually()?;
108 for p in [51, 52, 53, 55, 56, 57] {
109 relay::shutdown_relay(8000 + p)?;
110 }
111 Ok(())
112 });
113
114 // launch relay
115 let _ = join!(
116 r51.listen_until_close(),
117 r52.listen_until_close(),
118 r53.listen_until_close(),
119 r55.listen_until_close(),
120 r56.listen_until_close(),
121 r57.listen_until_close(),
122 );
123 cli_tester_handle.join().unwrap()?;
124 Ok((r51, r52, r53, r55, r56, r57))
125 }
126
127 mod sent_to_correct_relays {
128
129 use super::*;
130
131 #[tokio::test]
132 #[serial]
133 async fn only_1_repository_kind_event_sent_to_user_relays() -> Result<()> {
134 let (_, _, r53, r55, _, _) = prep_run_init().await?;
135 for relay in [&r53, &r55] {
136 assert_eq!(
137 relay
138 .events
139 .iter()
140 .filter(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
141 .count(),
142 1,
143 );
144 }
145 Ok(())
146 }
147
148 #[tokio::test]
149 #[serial]
150 async fn only_1_repository_kind_event_sent_to_specified_repo_relays() -> Result<()> {
151 let (_, _, _, r55, r56, _) = prep_run_init().await?;
152 for relay in [&r55, &r56] {
153 assert_eq!(
154 relay
155 .events
156 .iter()
157 .filter(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
158 .count(),
159 1,
160 );
161 }
162 Ok(())
163 }
164
165 #[tokio::test]
166 #[serial]
167 async fn only_1_repository_kind_event_sent_to_fallback_relays() -> Result<()> {
168 let (r51, r52, _, _, _, _) = prep_run_init().await?;
169 for relay in [&r51, &r52] {
170 assert_eq!(
171 relay
172 .events
173 .iter()
174 .filter(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
175 .count(),
176 1,
177 );
178 }
179 Ok(())
180 }
181
182 #[tokio::test]
183 #[serial]
184 async fn only_1_repository_kind_event_sent_to_blaster_relays() -> Result<()> {
185 let (_, _, _, _, _, r57) = prep_run_init().await?;
186 assert_eq!(
187 r57.events
188 .iter()
189 .filter(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
190 .count(),
191 1,
192 );
193 Ok(())
194 }
195 }
196
197 mod yaml_file {
198 use std::{fs, io::Read};
199
200 use super::*;
201
202 async fn async_run_test() -> Result<()> {
203 let git_repo = prep_git_repo()?;
204 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
205 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
206 Relay::new(
207 8051,
208 None,
209 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
210 relay.respond_events(
211 client_id,
212 &subscription_id,
213 &vec![
214 generate_test_key_1_metadata_event("fred"),
215 generate_test_key_1_relay_list_event(),
216 ],
217 )?;
218 Ok(())
219 }),
220 ),
221 Relay::new(8052, None, None),
222 Relay::new(8053, None, None),
223 Relay::new(8055, None, None),
224 Relay::new(8056, None, None),
225 Relay::new(8057, None, None),
226 );
227
228 // // check relay had the right number of events
229 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
230 let mut p = cli_tester_init(&git_repo);
231 p.expect_end_eventually()?;
232
233 let yaml_path = git_repo.dir.join("maintainers.yaml");
234
235 assert!(yaml_path.exists());
236
237 let mut file = fs::File::open(yaml_path).expect("no such file");
238 let mut file_contents = "".to_string();
239 let _ = file.read_to_string(&mut file_contents);
240
241 for p in [51, 52, 53, 55, 56, 57] {
242 relay::shutdown_relay(8000 + p)?;
243 }
244 assert_eq!(
245 file_contents,
246 format!(
247 "\
248 identifier: example-identifier\n\
249 maintainers:\n\
250 - {TEST_KEY_1_NPUB}\n\
251 relays:\n\
252 - ws://localhost:8055\n\
253 - ws://localhost:8056\n\
254 "
255 ),
256 );
257 Ok(())
258 });
259
260 // launch relay
261 let _ = join!(
262 r51.listen_until_close(),
263 r52.listen_until_close(),
264 r53.listen_until_close(),
265 r55.listen_until_close(),
266 r56.listen_until_close(),
267 r57.listen_until_close(),
268 );
269 cli_tester_handle.join().unwrap()?;
270 Ok(())
271 }
272
273 #[tokio::test]
274 #[serial]
275 async fn contains_identifier_maintainers_and_relays() -> Result<()> {
276 async_run_test().await
277 }
278 mod updates_existing_with_missing_identifier {
279 use std::io::Write;
280
281 use super::*;
282 async fn async_run_test() -> Result<()> {
283 let git_repo = prep_git_repo()?;
284 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
285 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
286 Relay::new(
287 8051,
288 None,
289 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
290 relay.respond_events(
291 client_id,
292 &subscription_id,
293 &vec![
294 generate_test_key_1_metadata_event("fred"),
295 generate_test_key_1_relay_list_event(),
296 ],
297 )?;
298 Ok(())
299 }),
300 ),
301 Relay::new(8052, None, None),
302 Relay::new(8053, None, None),
303 Relay::new(8055, None, None),
304 Relay::new(8056, None, None),
305 Relay::new(8057, None, None),
306 );
307
308 // // check relay had the right number of events
309 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
310 let yaml_path = git_repo.dir.join("maintainers.yaml");
311 let mut file = std::fs::File::create(&yaml_path)
312 .expect("cannot create maintainers.yaml file");
313 write!(
314 file,
315 "\
316 maintainers:\n\
317 - {TEST_KEY_1_NPUB}\n\
318 relays:\n\
319 - ws://localhost:8055\n\
320 - ws://localhost:8056\n\
321 "
322 )?;
323
324 let mut p = cli_tester_init(&git_repo);
325 p.expect_end_eventually()?;
326
327 assert!(yaml_path.exists());
328
329 let mut file = fs::File::open(yaml_path).expect("no such file");
330 let mut file_contents = "".to_string();
331 let _ = file.read_to_string(&mut file_contents);
332
333 for p in [51, 52, 53, 55, 56, 57] {
334 relay::shutdown_relay(8000 + p)?;
335 }
336 assert_eq!(
337 file_contents,
338 format!(
339 "\
340 identifier: example-identifier\n\
341 maintainers:\n\
342 - {TEST_KEY_1_NPUB}\n\
343 relays:\n\
344 - ws://localhost:8055\n\
345 - ws://localhost:8056\n\
346 "
347 ),
348 );
349 Ok(())
350 });
351
352 // launch relay
353 let _ = join!(
354 r51.listen_until_close(),
355 r52.listen_until_close(),
356 r53.listen_until_close(),
357 r55.listen_until_close(),
358 r56.listen_until_close(),
359 r57.listen_until_close(),
360 );
361 cli_tester_handle.join().unwrap()?;
362 Ok(())
363 }
364
365 #[tokio::test]
366 #[serial]
367 async fn adds_missing_identifier() -> Result<()> {
368 async_run_test().await
369 }
370 }
371 }
372
373 mod git_config_updated {
374
375 use nostr::nips::nip01::Coordinate;
376 use nostr_sdk::ToBech32;
377
378 use super::*;
379
380 async fn async_run_test() -> Result<()> {
381 let git_repo = prep_git_repo()?;
382 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
383 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
384 Relay::new(
385 8051,
386 None,
387 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
388 relay.respond_events(
389 client_id,
390 &subscription_id,
391 &vec![
392 generate_test_key_1_metadata_event("fred"),
393 generate_test_key_1_relay_list_event(),
394 ],
395 )?;
396 Ok(())
397 }),
398 ),
399 Relay::new(8052, None, None),
400 Relay::new(8053, None, None),
401 Relay::new(8055, None, None),
402 Relay::new(8056, None, None),
403 Relay::new(8057, None, None),
404 );
405
406 // // check relay had the right number of events
407 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
408 let mut p = cli_tester_init(&git_repo);
409 p.expect_end_eventually()?;
410 for p in [51, 52, 53, 55, 56, 57] {
411 relay::shutdown_relay(8000 + p)?;
412 }
413 assert_eq!(
414 git_repo
415 .git_repo
416 .config()?
417 .get_entry("nostr.repo")?
418 .value()
419 .unwrap(),
420 Coordinate {
421 kind: nostr_sdk::Kind::GitRepoAnnouncement,
422 identifier: "example-identifier".to_string(),
423 public_key: TEST_KEY_1_KEYS.public_key(),
424 relays: vec![],
425 }
426 .to_bech32()?,
427 );
428
429 Ok(())
430 });
431
432 // launch relay
433 let _ = join!(
434 r51.listen_until_close(),
435 r52.listen_until_close(),
436 r53.listen_until_close(),
437 r55.listen_until_close(),
438 r56.listen_until_close(),
439 r57.listen_until_close(),
440 );
441 cli_tester_handle.join().unwrap()?;
442 Ok(())
443 }
444
445 #[tokio::test]
446 #[serial]
447 async fn with_nostr_repo_set_to_user_and_identifer_naddr() -> Result<()> {
448 async_run_test().await?;
449 Ok(())
450 }
451 }
452
453 mod tags_as_specified_in_args {
454 use super::*;
455
456 #[tokio::test]
457 #[serial]
458 async fn d_replaceable_event_identifier() -> Result<()> {
459 let (_, _, r53, r55, r56, r57) = prep_run_init().await?;
460 for relay in [&r53, &r55, &r56, &r57] {
461 let event: &nostr::Event = relay
462 .events
463 .iter()
464 .find(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
465 .unwrap();
466
467 assert!(
468 event.tags.iter().any(
469 |t| t.as_vec()[0].eq("d") && t.as_vec()[1].eq("example-identifier")
470 )
471 );
472 }
473 Ok(())
474 }
475
476 #[tokio::test]
477 #[serial]
478 async fn earliest_unique_commit_as_reference_with_euc_marker() -> Result<()> {
479 let (_, _, r53, r55, r56, r57) = prep_run_init().await?;
480 for relay in [&r53, &r55, &r56, &r57] {
481 let event: &nostr::Event = relay
482 .events
483 .iter()
484 .find(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
485 .unwrap();
486
487 assert!(event.tags.iter().any(|t| t.as_vec()[0].eq("r")
488 && t.as_vec()[1].eq("9ee507fc4357d7ee16a5d8901bedcd103f23c17d")
489 && t.as_vec()[2].eq("euc")));
490 }
491 Ok(())
492 }
493
494 #[tokio::test]
495 #[serial]
496 async fn name() -> Result<()> {
497 let (_, _, r53, r55, r56, r57) = prep_run_init().await?;
498 for relay in [&r53, &r55, &r56, &r57] {
499 let event: &nostr::Event = relay
500 .events
501 .iter()
502 .find(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
503 .unwrap();
504
505 assert!(
506 event
507 .tags
508 .iter()
509 .any(|t| t.as_vec()[0].eq("name") && t.as_vec()[1].eq("example-name"))
510 );
511 }
512 Ok(())
513 }
514
515 #[tokio::test]
516 #[serial]
517 async fn alt() -> Result<()> {
518 let (_, _, r53, r55, r56, r57) = prep_run_init().await?;
519 for relay in [&r53, &r55, &r56, &r57] {
520 let event: &nostr::Event = relay
521 .events
522 .iter()
523 .find(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
524 .unwrap();
525
526 assert!(event.tags.iter().any(|t| t.as_vec()[0].eq("alt")
527 && t.as_vec()[1].eq("git repository: example-name")));
528 }
529 Ok(())
530 }
531
532 #[tokio::test]
533 #[serial]
534 async fn description() -> Result<()> {
535 let (_, _, r53, r55, r56, r57) = prep_run_init().await?;
536 for relay in [&r53, &r55, &r56, &r57] {
537 let event: &nostr::Event = relay
538 .events
539 .iter()
540 .find(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
541 .unwrap();
542
543 assert!(event.tags.iter().any(|t| t.as_vec()[0].eq("description")
544 && t.as_vec()[1].eq("example-description")));
545 }
546 Ok(())
547 }
548
549 #[tokio::test]
550 #[serial]
551 async fn git_server() -> Result<()> {
552 let (_, _, r53, r55, r56, r57) = prep_run_init().await?;
553 for relay in [&r53, &r55, &r56, &r57] {
554 let event: &nostr::Event = relay
555 .events
556 .iter()
557 .find(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
558 .unwrap();
559
560 assert!(
561 event.tags.iter().any(|t| t.as_vec()[0].eq("clone")
562 && t.as_vec()[1].eq("https://git.myhosting.com/my-repo.git")) /* todo check it defaults to origin */
563 );
564 }
565 Ok(())
566 }
567
568 #[tokio::test]
569 #[serial]
570 async fn relays() -> Result<()> {
571 let (_, _, r53, r55, r56, r57) = prep_run_init().await?;
572 for relay in [&r53, &r55, &r56, &r57] {
573 let event: &nostr::Event = relay
574 .events
575 .iter()
576 .find(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
577 .unwrap();
578 let relays_tag = event
579 .tags
580 .iter()
581 .find(|t| t.as_vec()[0].eq("relays"))
582 .unwrap()
583 .as_vec();
584 assert_eq!(relays_tag[1], "ws://localhost:8055",);
585 assert_eq!(relays_tag[2], "ws://localhost:8056",);
586 }
587 Ok(())
588 }
589
590 #[tokio::test]
591 #[serial]
592 async fn web() -> Result<()> {
593 let (_, _, r53, r55, r56, r57) = prep_run_init().await?;
594 for relay in [&r53, &r55, &r56, &r57] {
595 let event: &nostr::Event = relay
596 .events
597 .iter()
598 .find(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
599 .unwrap();
600 let web_tag = event
601 .tags
602 .iter()
603 .find(|t| t.as_vec()[0].eq("web"))
604 .unwrap()
605 .as_vec();
606 assert_eq!(web_tag[1], "https://exampleproject.xyz",);
607 assert_eq!(web_tag[2], "https://gitworkshop.dev/123",);
608 }
609 Ok(())
610 }
611
612 #[tokio::test]
613 #[serial]
614 async fn maintainers() -> Result<()> {
615 let (_, _, r53, r55, r56, r57) = prep_run_init().await?;
616 for relay in [&r53, &r55, &r56, &r57] {
617 let event: &nostr::Event = relay
618 .events
619 .iter()
620 .find(|e| e.kind.eq(&Kind::GitRepoAnnouncement))
621 .unwrap();
622 let maintainers_tag = event
623 .tags
624 .iter()
625 .find(|t| t.as_vec()[0].eq("maintainers"))
626 .unwrap()
627 .as_vec();
628 assert_eq!(maintainers_tag[1], TEST_KEY_1_KEYS.public_key().to_string());
629 }
630 Ok(())
631 }
632 }
633
634 mod cli_ouput {
635 use super::*;
636
637 #[tokio::test]
638 #[serial]
639 async fn check_cli_output() -> Result<()> {
640 let git_repo = prep_git_repo()?;
641
642 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
643 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
644 Relay::new(
645 8051,
646 None,
647 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
648 relay.respond_events(
649 client_id,
650 &subscription_id,
651 &vec![
652 generate_test_key_1_metadata_event("fred"),
653 generate_test_key_1_relay_list_event(),
654 ],
655 )?;
656 Ok(())
657 }),
658 ),
659 Relay::new(8052, None, None),
660 Relay::new(8053, None, None),
661 Relay::new(8055, None, None),
662 Relay::new(8056, None, None),
663 Relay::new(8057, None, None),
664 );
665
666 // // check relay had the right number of events
667 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
668 let mut p = cli_tester_init(&git_repo);
669 expect_msgs_first(&mut p)?;
670 relay::expect_send_with_progress(
671 &mut p,
672 vec![
673 (" [my-relay] [repo-relay] ws://localhost:8055", true, ""),
674 (" [my-relay] ws://localhost:8053", true, ""),
675 (" [repo-relay] ws://localhost:8056", true, ""),
676 (" [default] ws://localhost:8051", true, ""),
677 (" [default] ws://localhost:8052", true, ""),
678 (" [default] ws://localhost:8057", true, ""),
679 ],
680 1,
681 )?;
682 expect_msgs_after(&mut p)?;
683 p.expect_end()?;
684 for p in [51, 52, 53, 55, 56, 57] {
685 relay::shutdown_relay(8000 + p)?;
686 }
687 Ok(())
688 });
689
690 // launch relay
691 let _ = join!(
692 r51.listen_until_close(),
693 r52.listen_until_close(),
694 r53.listen_until_close(),
695 r55.listen_until_close(),
696 r56.listen_until_close(),
697 r57.listen_until_close(),
698 );
699 cli_tester_handle.join().unwrap()?;
700 Ok(())
701 }
702 }
703 }
704 // TODO: cli caputuring input
705}
706// TODO: when_updating_existing_repoistory correct defaults are used
diff --git a/tests/ngit/list.rs b/tests/ngit/list.rs
deleted file mode 100644
index 26cf717..0000000
--- a/tests/ngit/list.rs
+++ /dev/null
@@ -1,1549 +0,0 @@
1use anyhow::Result;
2use futures::join;
3use serial_test::serial;
4use test_utils::{git::GitTestRepo, relay::Relay, *};
5
6async fn prep_proposals_repo_and_repo_with_proposal_pulled_and_checkedout(
7 proposal_number: u16,
8) -> Result<(GitTestRepo, GitTestRepo)> {
9 // fallback (51,52) user write (53, 55) repo (55, 56)
10 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
11 Relay::new(8051, None, None),
12 Relay::new(8052, None, None),
13 Relay::new(8053, None, None),
14 Relay::new(8055, None, None),
15 Relay::new(8056, None, None),
16 );
17
18 r51.events.push(generate_test_key_1_relay_list_event());
19 r51.events.push(generate_test_key_1_metadata_event("fred"));
20 r51.events.push(generate_repo_ref_event());
21
22 r55.events.push(generate_repo_ref_event());
23 r55.events.push(generate_test_key_1_metadata_event("fred"));
24 r55.events.push(generate_test_key_1_relay_list_event());
25
26 let cli_tester_handle = std::thread::spawn(move || -> Result<(GitTestRepo, GitTestRepo)> {
27 let (originating_repo, test_repo) =
28 create_proposals_and_repo_with_proposal_pulled_and_checkedout(proposal_number)?;
29
30 for p in [51, 52, 53, 55, 56] {
31 relay::shutdown_relay(8000 + p)?;
32 }
33 Ok((originating_repo, test_repo))
34 });
35
36 // launch relay
37 let _ = join!(
38 r51.listen_until_close(),
39 r52.listen_until_close(),
40 r53.listen_until_close(),
41 r55.listen_until_close(),
42 r56.listen_until_close(),
43 );
44 let res = cli_tester_handle.join().unwrap()?;
45
46 Ok(res)
47}
48
49mod cannot_find_repo_event {
50 use super::*;
51 mod cli_prompts {
52 use nostr::{nips::nip01::Coordinate, ToBech32};
53
54 use super::*;
55 async fn run_async_repo_event_ref_needed(invalid_input: bool, naddr: bool) -> Result<()> {
56 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
57 Relay::new(8051, None, None),
58 Relay::new(8052, None, None),
59 Relay::new(8053, None, None),
60 Relay::new(8055, None, None),
61 Relay::new(8056, None, None),
62 );
63
64 r51.events.push(generate_test_key_1_relay_list_event());
65 r51.events.push(generate_test_key_1_metadata_event("fred"));
66
67 r55.events.push(generate_test_key_1_relay_list_event());
68 r55.events.push(generate_test_key_1_metadata_event("fred"));
69
70 let repo_event = generate_repo_ref_event();
71 r56.events.push(repo_event.clone());
72
73 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
74 let test_repo = GitTestRepo::without_repo_in_git_config();
75 test_repo.populate()?;
76 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
77 p.expect(
78 "hint: https://gitworkshop.dev/repos lists repositories and their naddr\r\n",
79 )?;
80 if invalid_input {
81 let mut input = p.expect_input("repository naddr")?;
82 input.succeeds_with("dfgvfvfzadvd")?;
83 p.expect("not a valid naddr\r\n")?;
84 let _ = p.expect_input("repository naddr")?;
85 p.exit()?;
86 }
87 if naddr {
88 let mut input = p.expect_input("repository naddr")?;
89 input.succeeds_with(
90 &Coordinate {
91 kind: nostr::Kind::GitRepoAnnouncement,
92 public_key: TEST_KEY_1_KEYS.public_key(),
93 identifier: repo_event.identifier().unwrap().to_string(),
94 relays: vec!["ws://localhost:8056".to_string()],
95 }
96 .to_bech32()?,
97 )?;
98 p.expect("fetching updates...\r\n")?;
99 p.expect_eventually("\r\n")?; // some updates listed here
100 p.expect_end_with("no proposals found... create one? try `ngit send`\r\n")?;
101 }
102
103 for p in [51, 52, 53, 55, 56] {
104 relay::shutdown_relay(8000 + p)?;
105 }
106 Ok(())
107 });
108
109 // launch relay
110 let _ = join!(
111 r51.listen_until_close(),
112 r52.listen_until_close(),
113 r53.listen_until_close(),
114 r55.listen_until_close(),
115 r56.listen_until_close(),
116 );
117 cli_tester_handle.join().unwrap()?;
118 Ok(())
119 }
120
121 #[tokio::test]
122 #[serial]
123 async fn warns_not_valid_input_and_asks_again() -> Result<()> {
124 run_async_repo_event_ref_needed(true, false).await
125 }
126
127 #[tokio::test]
128 #[serial]
129 async fn finds_based_on_naddr_on_embeded_relay() -> Result<()> {
130 run_async_repo_event_ref_needed(false, true).await
131 }
132 }
133}
134mod when_main_branch_is_uptodate {
135 use super::*;
136
137 mod when_proposal_branch_doesnt_exist {
138 use super::*;
139
140 mod when_main_is_checked_out {
141 use super::*;
142
143 mod when_first_proposal_selected {
144 use super::*;
145
146 // TODO: test when other proposals with the same name but from other
147 // repositories are present on relays
148
149 mod cli_prompts {
150 use super::*;
151 #[tokio::test]
152 #[serial]
153 async fn prompts_to_choose_from_proposal_titles() -> Result<()> {
154 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
155 Relay::new(8051, None, None),
156 Relay::new(8052, None, None),
157 Relay::new(8053, None, None),
158 Relay::new(8055, None, None),
159 Relay::new(8056, None, None),
160 );
161
162 r51.events.push(generate_test_key_1_relay_list_event());
163 r51.events.push(generate_test_key_1_metadata_event("fred"));
164 r51.events.push(generate_repo_ref_event());
165
166 r55.events.push(generate_repo_ref_event());
167 r55.events.push(generate_test_key_1_metadata_event("fred"));
168 r55.events.push(generate_test_key_1_relay_list_event());
169
170 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
171 cli_tester_create_proposals()?;
172
173 let test_repo = GitTestRepo::default();
174 test_repo.populate()?;
175 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
176
177 p.expect("fetching updates...\r\n")?;
178 p.expect_eventually("\r\n")?; // some updates listed here
179 let mut c = p.expect_choice(
180 "all proposals",
181 vec![
182 format!("\"{PROPOSAL_TITLE_3}\""),
183 format!("\"{PROPOSAL_TITLE_2}\""),
184 format!("\"{PROPOSAL_TITLE_1}\""),
185 ],
186 )?;
187 c.succeeds_with(2, true, None)?;
188 let mut c = p.expect_choice(
189 "",
190 vec![
191 format!(
192 "create and checkout proposal branch (2 ahead 0 behind 'main')" ),
193 format!("apply to current branch with `git am`"),
194 format!("download to ./patches"),
195 format!("back"),
196 ],
197 )?;
198 c.succeeds_with(0, false, None)?;
199 p.expect(format!(
200 "checked out proposal as 'pr/{}(",
201 FEATURE_BRANCH_NAME_1,
202 ))?;
203 p.expect_end_eventually_with(")' branch\r\n")?;
204
205 for p in [51, 52, 53, 55, 56] {
206 relay::shutdown_relay(8000 + p)?;
207 }
208 Ok(())
209 });
210
211 // launch relay
212 let _ = join!(
213 r51.listen_until_close(),
214 r52.listen_until_close(),
215 r53.listen_until_close(),
216 r55.listen_until_close(),
217 r56.listen_until_close(),
218 );
219 cli_tester_handle.join().unwrap()?;
220 println!("{:?}", r55.events);
221 Ok(())
222 }
223 }
224
225 #[tokio::test]
226 #[serial]
227 async fn proposal_branch_created_with_correct_name() -> Result<()> {
228 let (_, test_repo) =
229 prep_proposals_repo_and_repo_with_proposal_pulled_and_checkedout(1).await?;
230 assert_eq!(
231 vec![
232 "main",
233 &get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?,
234 ],
235 test_repo.get_local_branch_names()?
236 );
237 Ok(())
238 }
239
240 #[tokio::test]
241 #[serial]
242 async fn proposal_branch_checked_out() -> Result<()> {
243 let (_, test_repo) =
244 prep_proposals_repo_and_repo_with_proposal_pulled_and_checkedout(1).await?;
245 assert_eq!(
246 get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?,
247 test_repo.get_checked_out_branch_name()?,
248 );
249 Ok(())
250 }
251
252 #[tokio::test]
253 #[serial]
254 async fn proposal_branch_tip_is_most_recent_patch() -> Result<()> {
255 let (originating_repo, test_repo) =
256 prep_proposals_repo_and_repo_with_proposal_pulled_and_checkedout(1).await?;
257 assert_eq!(
258 originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?,
259 test_repo.get_tip_of_local_branch(&get_proposal_branch_name(
260 &test_repo,
261 FEATURE_BRANCH_NAME_1
262 )?)?,
263 );
264 Ok(())
265 }
266 }
267 mod when_third_proposal_selected {
268 use super::*;
269
270 mod cli_prompts {
271 use super::*;
272
273 #[tokio::test]
274 #[serial]
275 async fn prompts_to_choose_from_proposal_titles() -> Result<()> {
276 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
277 Relay::new(8051, None, None),
278 Relay::new(8052, None, None),
279 Relay::new(8053, None, None),
280 Relay::new(8055, None, None),
281 Relay::new(8056, None, None),
282 );
283
284 r51.events.push(generate_test_key_1_relay_list_event());
285 r51.events.push(generate_test_key_1_metadata_event("fred"));
286 r51.events.push(generate_repo_ref_event());
287
288 r55.events.push(generate_repo_ref_event());
289 r55.events.push(generate_test_key_1_metadata_event("fred"));
290 r55.events.push(generate_test_key_1_relay_list_event());
291
292 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
293 cli_tester_create_proposals()?;
294
295 let test_repo = GitTestRepo::default();
296 test_repo.populate()?;
297 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
298
299 p.expect("fetching updates...\r\n")?;
300 p.expect_eventually("\r\n")?; // some updates listed here
301 let mut c = p.expect_choice(
302 "all proposals",
303 vec![
304 format!("\"{PROPOSAL_TITLE_3}\""),
305 format!("\"{PROPOSAL_TITLE_2}\""),
306 format!("\"{PROPOSAL_TITLE_1}\""),
307 ],
308 )?;
309 c.succeeds_with(0, true, None)?;
310 let mut c = p.expect_choice(
311 "",
312 vec![
313 format!(
314 "create and checkout proposal branch (2 ahead 0 behind 'main')" ),
315 format!("apply to current branch with `git am`"),
316 format!("download to ./patches"),
317 format!("back"),
318 ],
319 )?;
320 c.succeeds_with(0, false, Some(0))?;
321 p.expect(format!(
322 "checked out proposal as 'pr/{}(",
323 FEATURE_BRANCH_NAME_3,
324 ))?;
325 p.expect_end_eventually_with(")' branch\r\n")?;
326
327 for p in [51, 52, 53, 55, 56] {
328 relay::shutdown_relay(8000 + p)?;
329 }
330 Ok(())
331 });
332
333 // launch relay
334 let _ = join!(
335 r51.listen_until_close(),
336 r52.listen_until_close(),
337 r53.listen_until_close(),
338 r55.listen_until_close(),
339 r56.listen_until_close(),
340 );
341 cli_tester_handle.join().unwrap()?;
342 println!("{:?}", r55.events);
343 Ok(())
344 }
345 }
346
347 #[tokio::test]
348 #[serial]
349 async fn proposal_branch_created_with_correct_name() -> Result<()> {
350 let (_, test_repo) =
351 prep_proposals_repo_and_repo_with_proposal_pulled_and_checkedout(3).await?;
352 assert_eq!(
353 vec![
354 "main",
355 &get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_3)?,
356 ],
357 test_repo.get_local_branch_names()?
358 );
359 Ok(())
360 }
361
362 #[tokio::test]
363 #[serial]
364 async fn proposal_branch_checked_out() -> Result<()> {
365 let (_, test_repo) =
366 prep_proposals_repo_and_repo_with_proposal_pulled_and_checkedout(3).await?;
367 assert_eq!(
368 get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_3)?,
369 test_repo.get_checked_out_branch_name()?,
370 );
371 Ok(())
372 }
373
374 #[tokio::test]
375 #[serial]
376 async fn proposal_branch_tip_is_most_recent_patch() -> Result<()> {
377 let (originating_repo, test_repo) =
378 prep_proposals_repo_and_repo_with_proposal_pulled_and_checkedout(3).await?;
379 assert_eq!(
380 originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_3)?,
381 test_repo.get_tip_of_local_branch(&get_proposal_branch_name(
382 &test_repo,
383 FEATURE_BRANCH_NAME_3
384 )?)?,
385 );
386 Ok(())
387 }
388 }
389 mod when_forth_proposal_has_no_cover_letter {
390 use super::*;
391
392 async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> {
393 // fallback (51,52) user write (53, 55) repo (55, 56)
394 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
395 Relay::new(8051, None, None),
396 Relay::new(8052, None, None),
397 Relay::new(8053, None, None),
398 Relay::new(8055, None, None),
399 Relay::new(8056, None, None),
400 );
401
402 r51.events.push(generate_test_key_1_relay_list_event());
403 r51.events.push(generate_test_key_1_metadata_event("fred"));
404 r51.events.push(generate_repo_ref_event());
405
406 r55.events.push(generate_repo_ref_event());
407 r55.events.push(generate_test_key_1_metadata_event("fred"));
408 r55.events.push(generate_test_key_1_relay_list_event());
409
410 let cli_tester_handle = std::thread::spawn(
411 move || -> Result<(GitTestRepo, GitTestRepo)> {
412 let originating_repo = cli_tester_create_proposals()?;
413 cli_tester_create_proposal(
414 &originating_repo,
415 FEATURE_BRANCH_NAME_4,
416 "d",
417 None,
418 None,
419 )?;
420 let test_repo = GitTestRepo::default();
421 test_repo.populate()?;
422 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
423
424 p.expect("fetching updates...\r\n")?;
425 p.expect_eventually("\r\n")?; // some updates listed here
426 let mut c = p.expect_choice(
427 "all proposals",
428 vec![
429 format!("add d3.md"), // commit msg title
430 format!("\"{PROPOSAL_TITLE_3}\""),
431 format!("\"{PROPOSAL_TITLE_2}\""),
432 format!("\"{PROPOSAL_TITLE_1}\""),
433 ],
434 )?;
435 c.succeeds_with(0, true, None)?;
436 let mut c = p.expect_choice(
437 "",
438 vec![
439 format!(
440 "create and checkout proposal branch (2 ahead 0 behind 'main')" ),
441 format!("apply to current branch with `git am`"),
442 format!("download to ./patches"),
443 format!("back"),
444 ],
445 )?;
446 c.succeeds_with(0, false, Some(0))?;
447 p.expect_end_eventually_and_print()?;
448
449 for p in [51, 52, 53, 55, 56] {
450 relay::shutdown_relay(8000 + p)?;
451 }
452 Ok((originating_repo, test_repo))
453 },
454 );
455
456 // launch relay
457 let _ = join!(
458 r51.listen_until_close(),
459 r52.listen_until_close(),
460 r53.listen_until_close(),
461 r55.listen_until_close(),
462 r56.listen_until_close(),
463 );
464 let res = cli_tester_handle.join().unwrap()?;
465
466 Ok(res)
467 }
468
469 mod cli_prompts {
470 use super::*;
471
472 #[tokio::test]
473 #[serial]
474 async fn prompts_to_choose_from_proposal_titles() -> Result<()> {
475 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
476 Relay::new(8051, None, None),
477 Relay::new(8052, None, None),
478 Relay::new(8053, None, None),
479 Relay::new(8055, None, None),
480 Relay::new(8056, None, None),
481 );
482
483 r51.events.push(generate_test_key_1_relay_list_event());
484 r51.events.push(generate_test_key_1_metadata_event("fred"));
485 r51.events.push(generate_repo_ref_event());
486
487 r55.events.push(generate_repo_ref_event());
488 r55.events.push(generate_test_key_1_metadata_event("fred"));
489 r55.events.push(generate_test_key_1_relay_list_event());
490
491 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
492 let originating_repo = cli_tester_create_proposals()?;
493 std::thread::sleep(std::time::Duration::from_millis(1000));
494 cli_tester_create_proposal(
495 &originating_repo,
496 FEATURE_BRANCH_NAME_4,
497 "d",
498 None,
499 None,
500 )?;
501 let test_repo = GitTestRepo::default();
502 test_repo.populate()?;
503 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
504
505 p.expect("fetching updates...\r\n")?;
506 p.expect_eventually("\r\n")?; // some updates listed here
507 let mut c = p.expect_choice(
508 "all proposals",
509 vec![
510 format!("add d3.md"), // commit msg title
511 format!("\"{PROPOSAL_TITLE_3}\""),
512 format!("\"{PROPOSAL_TITLE_2}\""),
513 format!("\"{PROPOSAL_TITLE_1}\""),
514 ],
515 )?;
516 c.succeeds_with(0, true, None)?;
517 let mut c = p.expect_choice(
518 "",
519 vec![
520 format!(
521 "create and checkout proposal branch (2 ahead 0 behind 'main')" ),
522 format!("apply to current branch with `git am`"),
523 format!("download to ./patches"),
524 format!("back"),
525 ],
526 )?;
527 c.succeeds_with(0, false, Some(0))?;
528 p.expect(format!(
529 "checked out proposal as 'pr/{}(",
530 FEATURE_BRANCH_NAME_4,
531 ))?;
532 p.expect_end_eventually_with(")' branch\r\n")?;
533
534 for p in [51, 52, 53, 55, 56] {
535 relay::shutdown_relay(8000 + p)?;
536 }
537 Ok(())
538 });
539
540 // launch relay
541 let _ = join!(
542 r51.listen_until_close(),
543 r52.listen_until_close(),
544 r53.listen_until_close(),
545 r55.listen_until_close(),
546 r56.listen_until_close(),
547 );
548 cli_tester_handle.join().unwrap()?;
549 println!("{:?}", r55.events);
550 Ok(())
551 }
552 }
553
554 #[tokio::test]
555 #[serial]
556 async fn proposal_branch_created_with_correct_name() -> Result<()> {
557 let (_, test_repo) = prep_and_run().await?;
558 assert_eq!(
559 vec![
560 "main",
561 &get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_4)?,
562 ],
563 test_repo.get_local_branch_names()?
564 );
565 Ok(())
566 }
567
568 #[tokio::test]
569 #[serial]
570 async fn proposal_branch_checked_out() -> Result<()> {
571 let (_, test_repo) = prep_and_run().await?;
572 assert_eq!(
573 get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_4)?,
574 test_repo.get_checked_out_branch_name()?,
575 );
576 Ok(())
577 }
578
579 #[tokio::test]
580 #[serial]
581 async fn proposal_branch_tip_is_most_recent_patch() -> Result<()> {
582 let (originating_repo, test_repo) = prep_and_run().await?;
583 assert_eq!(
584 originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_4)?,
585 test_repo.get_tip_of_local_branch(&get_proposal_branch_name(
586 &test_repo,
587 FEATURE_BRANCH_NAME_4
588 )?)?,
589 );
590 Ok(())
591 }
592 }
593 }
594 }
595
596 mod when_proposal_branch_exists {
597 use super::*;
598
599 mod when_main_is_checked_out {
600 use super::*;
601
602 mod when_branch_is_up_to_date {
603 use super::*;
604 async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> {
605 // fallback (51,52) user write (53, 55) repo (55, 56)
606 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
607 Relay::new(8051, None, None),
608 Relay::new(8052, None, None),
609 Relay::new(8053, None, None),
610 Relay::new(8055, None, None),
611 Relay::new(8056, None, None),
612 );
613
614 r51.events.push(generate_test_key_1_relay_list_event());
615 r51.events.push(generate_test_key_1_metadata_event("fred"));
616 r51.events.push(generate_repo_ref_event());
617
618 r55.events.push(generate_repo_ref_event());
619 r55.events.push(generate_test_key_1_metadata_event("fred"));
620 r55.events.push(generate_test_key_1_relay_list_event());
621
622 let cli_tester_handle = std::thread::spawn(
623 move || -> Result<(GitTestRepo, GitTestRepo)> {
624 let originating_repo = cli_tester_create_proposals()?;
625
626 let test_repo = GitTestRepo::default();
627 test_repo.populate()?;
628 // create proposal branch
629 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
630 p.expect("fetching updates...\r\n")?;
631 p.expect_eventually("\r\n")?; // some updates listed here
632 let mut c = p.expect_choice(
633 "all proposals",
634 vec![
635 format!("\"{PROPOSAL_TITLE_3}\""),
636 format!("\"{PROPOSAL_TITLE_2}\""),
637 format!("\"{PROPOSAL_TITLE_1}\""),
638 ],
639 )?;
640 c.succeeds_with(2, true, None)?;
641 let mut c = p.expect_choice(
642 "",
643 vec![
644 format!("create and checkout proposal branch (2 ahead 0 behind 'main')"),
645 format!("apply to current branch with `git am`"),
646 format!("download to ./patches"),
647 format!("back"),
648 ],
649 )?;
650 c.succeeds_with(0, false, Some(0))?;
651 p.expect_end_eventually()?;
652
653 test_repo.checkout("main")?;
654 // run test
655 p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
656 p.expect("fetching updates...\r\n")?;
657 p.expect_eventually("\r\n")?; // some updates listed here
658 let mut c = p.expect_choice(
659 "all proposals",
660 vec![
661 format!("\"{PROPOSAL_TITLE_3}\""),
662 format!("\"{PROPOSAL_TITLE_2}\""),
663 format!("\"{PROPOSAL_TITLE_1}\""),
664 ],
665 )?;
666 c.succeeds_with(2, true, None)?;
667 let mut c = p.expect_choice(
668 "",
669 vec![
670 format!("checkout proposal branch (2 ahead 0 behind 'main')"),
671 format!("apply to current branch with `git am`"),
672 format!("download to ./patches"),
673 format!("back"),
674 ],
675 )?;
676 c.succeeds_with(0, false, Some(0))?;
677 p.expect_end_eventually_and_print()?;
678
679 for p in [51, 52, 53, 55, 56] {
680 relay::shutdown_relay(8000 + p)?;
681 }
682 Ok((originating_repo, test_repo))
683 },
684 );
685
686 // launch relay
687 let _ = join!(
688 r51.listen_until_close(),
689 r52.listen_until_close(),
690 r53.listen_until_close(),
691 r55.listen_until_close(),
692 r56.listen_until_close(),
693 );
694 let res = cli_tester_handle.join().unwrap()?;
695
696 Ok(res)
697 }
698
699 mod cli_prompts {
700 use super::*;
701
702 #[tokio::test]
703 #[serial]
704 async fn prompts_to_choose_from_proposal_titles() -> Result<()> {
705 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
706 Relay::new(8051, None, None),
707 Relay::new(8052, None, None),
708 Relay::new(8053, None, None),
709 Relay::new(8055, None, None),
710 Relay::new(8056, None, None),
711 );
712
713 r51.events.push(generate_test_key_1_relay_list_event());
714 r51.events.push(generate_test_key_1_metadata_event("fred"));
715 r51.events.push(generate_repo_ref_event());
716
717 r55.events.push(generate_repo_ref_event());
718 r55.events.push(generate_test_key_1_metadata_event("fred"));
719 r55.events.push(generate_test_key_1_relay_list_event());
720
721 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
722 cli_tester_create_proposals()?;
723
724 let test_repo = GitTestRepo::default();
725 test_repo.populate()?;
726 // create proposal branch
727 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
728 p.expect("fetching updates...\r\n")?;
729 p.expect_eventually("\r\n")?; // some updates listed here
730 let mut c = p.expect_choice(
731 "all proposals",
732 vec![
733 format!("\"{PROPOSAL_TITLE_3}\""),
734 format!("\"{PROPOSAL_TITLE_2}\""),
735 format!("\"{PROPOSAL_TITLE_1}\""),
736 ],
737 )?;
738 c.succeeds_with(2, true, None)?;
739 let mut c = p.expect_choice(
740 "",
741 vec![
742 format!("create and checkout proposal branch (2 ahead 0 behind 'main')"),
743 format!("apply to current branch with `git am`"),
744 format!("download to ./patches"),
745 format!("back"),
746 ],
747 )?;
748 c.succeeds_with(0, false, Some(0))?;
749 p.expect_end_eventually()?;
750
751 test_repo.checkout("main")?;
752 // run test
753 p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
754 p.expect("fetching updates...\r\n")?;
755 p.expect_eventually("\r\n")?; // some updates listed here
756 let mut c = p.expect_choice(
757 "all proposals",
758 vec![
759 format!("\"{PROPOSAL_TITLE_3}\""),
760 format!("\"{PROPOSAL_TITLE_2}\""),
761 format!("\"{PROPOSAL_TITLE_1}\""),
762 ],
763 )?;
764 c.succeeds_with(2, true, None)?;
765 let mut c = p.expect_choice(
766 "",
767 vec![
768 format!("checkout proposal branch (2 ahead 0 behind 'main')"),
769 format!("apply to current branch with `git am`"),
770 format!("download to ./patches"),
771 format!("back"),
772 ],
773 )?;
774 c.succeeds_with(0, false, Some(0))?;
775 p.expect(format!(
776 "checked out proposal as 'pr/{}(",
777 FEATURE_BRANCH_NAME_1,
778 ))?;
779 p.expect_end_eventually_with(")' branch\r\n")?;
780
781 for p in [51, 52, 53, 55, 56] {
782 relay::shutdown_relay(8000 + p)?;
783 }
784 Ok(())
785 });
786
787 // launch relay
788 let _ = join!(
789 r51.listen_until_close(),
790 r52.listen_until_close(),
791 r53.listen_until_close(),
792 r55.listen_until_close(),
793 r56.listen_until_close(),
794 );
795 cli_tester_handle.join().unwrap()?;
796 println!("{:?}", r55.events);
797 Ok(())
798 }
799 }
800
801 #[tokio::test]
802 #[serial]
803 async fn proposal_branch_checked_out() -> Result<()> {
804 let (_, test_repo) = prep_and_run().await?;
805 assert_eq!(
806 get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?,
807 test_repo.get_checked_out_branch_name()?,
808 );
809 Ok(())
810 }
811 }
812
813 mod when_branch_is_behind {
814 use super::*;
815
816 async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> {
817 // fallback (51,52) user write (53, 55) repo (55, 56)
818 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
819 Relay::new(8051, None, None),
820 Relay::new(8052, None, None),
821 Relay::new(8053, None, None),
822 Relay::new(8055, None, None),
823 Relay::new(8056, None, None),
824 );
825
826 r51.events.push(generate_test_key_1_relay_list_event());
827 r51.events.push(generate_test_key_1_metadata_event("fred"));
828 r51.events.push(generate_repo_ref_event());
829
830 r55.events.push(generate_repo_ref_event());
831 r55.events.push(generate_test_key_1_metadata_event("fred"));
832 r55.events.push(generate_test_key_1_relay_list_event());
833
834 let cli_tester_handle = std::thread::spawn(
835 move || -> Result<(GitTestRepo, GitTestRepo)> {
836 let (originating_repo, test_repo) =
837 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
838
839 remove_latest_commit_so_proposal_branch_is_behind_and_checkout_main(
840 &test_repo,
841 )?;
842
843 // run test
844 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
845 p.expect("fetching updates...\r\n")?;
846 p.expect_eventually("\r\n")?; // some updates listed here
847 let mut c = p.expect_choice(
848 "all proposals",
849 vec![
850 format!("\"{PROPOSAL_TITLE_3}\""),
851 format!("\"{PROPOSAL_TITLE_2}\""),
852 format!("\"{PROPOSAL_TITLE_1}\""),
853 ],
854 )?;
855 c.succeeds_with(2, true, None)?;
856 let mut c = p.expect_choice(
857 "",
858 vec![
859 format!("checkout proposal branch and apply 1 appendments"),
860 format!("apply to current branch with `git am`"),
861 format!("download to ./patches"),
862 format!("back"),
863 ],
864 )?;
865 c.succeeds_with(0, false, Some(0))?;
866 p.expect("checked out proposal branch and applied 1 appendments (2 ahead 0 behind 'main')\r\n")?;
867 p.expect_end()?;
868
869 for p in [51, 52, 53, 55, 56] {
870 relay::shutdown_relay(8000 + p)?;
871 }
872 Ok((originating_repo, test_repo))
873 },
874 );
875
876 // launch relay
877 let _ = join!(
878 r51.listen_until_close(),
879 r52.listen_until_close(),
880 r53.listen_until_close(),
881 r55.listen_until_close(),
882 r56.listen_until_close(),
883 );
884 let res = cli_tester_handle.join().unwrap()?;
885
886 Ok(res)
887 }
888
889 mod cli_prompts {
890 use super::*;
891
892 #[tokio::test]
893 #[serial]
894 async fn prompts_to_choose_from_proposal_titles() -> Result<()> {
895 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
896 Relay::new(8051, None, None),
897 Relay::new(8052, None, None),
898 Relay::new(8053, None, None),
899 Relay::new(8055, None, None),
900 Relay::new(8056, None, None),
901 );
902
903 r51.events.push(generate_test_key_1_relay_list_event());
904 r51.events.push(generate_test_key_1_metadata_event("fred"));
905 r51.events.push(generate_repo_ref_event());
906
907 r55.events.push(generate_repo_ref_event());
908 r55.events.push(generate_test_key_1_metadata_event("fred"));
909 r55.events.push(generate_test_key_1_relay_list_event());
910
911 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
912 let (_, test_repo) =
913 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
914
915 remove_latest_commit_so_proposal_branch_is_behind_and_checkout_main(
916 &test_repo,
917 )?;
918
919 // run test
920 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
921 p.expect("fetching updates...\r\n")?;
922 p.expect_eventually("\r\n")?; // some updates listed here
923 let mut c = p.expect_choice(
924 "all proposals",
925 vec![
926 format!("\"{PROPOSAL_TITLE_3}\""),
927 format!("\"{PROPOSAL_TITLE_2}\""),
928 format!("\"{PROPOSAL_TITLE_1}\""),
929 ],
930 )?;
931 c.succeeds_with(2, true, None)?;
932 let mut c = p.expect_choice(
933 "",
934 vec![
935 format!("checkout proposal branch and apply 1 appendments"),
936 format!("apply to current branch with `git am`"),
937 format!("download to ./patches"),
938 format!("back"),
939 ],
940 )?;
941 c.succeeds_with(0, false, Some(0))?;
942 p.expect("checked out proposal branch and applied 1 appendments (2 ahead 0 behind 'main')\r\n")?;
943 p.expect_end()?;
944
945 for p in [51, 52, 53, 55, 56] {
946 relay::shutdown_relay(8000 + p)?;
947 }
948 Ok(())
949 });
950
951 // launch relay
952 let _ = join!(
953 r51.listen_until_close(),
954 r52.listen_until_close(),
955 r53.listen_until_close(),
956 r55.listen_until_close(),
957 r56.listen_until_close(),
958 );
959 cli_tester_handle.join().unwrap()?;
960 println!("{:?}", r55.events);
961 Ok(())
962 }
963 }
964
965 #[tokio::test]
966 #[serial]
967 async fn proposal_branch_checked_out() -> Result<()> {
968 let (_, test_repo) = prep_and_run().await?;
969 assert_eq!(
970 get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?,
971 test_repo.get_checked_out_branch_name()?,
972 );
973 Ok(())
974 }
975
976 #[tokio::test]
977 #[serial]
978 async fn proposal_branch_tip_is_most_recent_patch() -> Result<()> {
979 let (originating_repo, test_repo) = prep_and_run().await?;
980 assert_eq!(
981 originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?,
982 test_repo.get_tip_of_local_branch(&get_proposal_branch_name(
983 &test_repo,
984 FEATURE_BRANCH_NAME_1
985 )?)?,
986 );
987 Ok(())
988 }
989 }
990
991 mod when_latest_proposal_amended_locally {
992 // other rebase scenarios should work if this test passes
993 use super::*;
994 async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> {
995 // fallback (51,52) user write (53, 55) repo (55, 56)
996 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
997 Relay::new(8051, None, None),
998 Relay::new(8052, None, None),
999 Relay::new(8053, None, None),
1000 Relay::new(8055, None, None),
1001 Relay::new(8056, None, None),
1002 );
1003
1004 r51.events.push(generate_test_key_1_relay_list_event());
1005 r51.events.push(generate_test_key_1_metadata_event("fred"));
1006 r51.events.push(generate_repo_ref_event());
1007
1008 r55.events.push(generate_repo_ref_event());
1009 r55.events.push(generate_test_key_1_metadata_event("fred"));
1010 r55.events.push(generate_test_key_1_relay_list_event());
1011
1012 let cli_tester_handle =
1013 std::thread::spawn(move || -> Result<(GitTestRepo, GitTestRepo)> {
1014 let (originating_repo, test_repo) =
1015 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
1016
1017 let branch_name = test_repo.get_checked_out_branch_name()?;
1018
1019 remove_latest_commit_so_proposal_branch_is_behind_and_checkout_main(
1020 &test_repo,
1021 )?;
1022
1023 // add another commit (so we have an ammened local branch)
1024 test_repo.checkout(&branch_name)?;
1025 std::fs::write(
1026 test_repo.dir.join("ammended-commit.md"),
1027 "some content",
1028 )?;
1029 test_repo.stage_and_commit("add ammended-commit.md")?;
1030 test_repo.checkout("main")?;
1031
1032 // run test
1033 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
1034 p.expect("fetching updates...\r\n")?;
1035 p.expect_eventually("\r\n")?; // some updates listed here
1036 let mut c = p.expect_choice(
1037 "all proposals",
1038 vec![
1039 format!("\"{PROPOSAL_TITLE_3}\""),
1040 format!("\"{PROPOSAL_TITLE_2}\""),
1041 format!("\"{PROPOSAL_TITLE_1}\""),
1042 ],
1043 )?;
1044 c.succeeds_with(2, true, None)?;
1045 p.expect_eventually("--force`\r\n")?;
1046
1047 let mut c = p.expect_choice(
1048 "",
1049 vec![
1050 format!("checkout local branch with unpublished changes"),
1051 format!(
1052 "discard unpublished changes and checkout new revision"
1053 ),
1054 format!("apply to current branch with `git am`"),
1055 format!("download to ./patches"),
1056 "back".to_string(),
1057 ],
1058 )?;
1059 c.succeeds_with(1, false, Some(0))?;
1060
1061 p.expect_end_eventually_and_print()?;
1062
1063 for p in [51, 52, 53, 55, 56] {
1064 relay::shutdown_relay(8000 + p)?;
1065 }
1066 Ok((originating_repo, test_repo))
1067 });
1068 // launch relay
1069 let _ = join!(
1070 r51.listen_until_close(),
1071 r52.listen_until_close(),
1072 r53.listen_until_close(),
1073 r55.listen_until_close(),
1074 r56.listen_until_close(),
1075 );
1076 let res = cli_tester_handle.join().unwrap()?;
1077
1078 Ok(res)
1079 }
1080
1081 mod cli_prompts {
1082 use super::*;
1083
1084 #[tokio::test]
1085 #[serial]
1086 async fn out_reflects_second_choice_discarding_old_and_applying_new()
1087 -> Result<()> {
1088 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
1089 Relay::new(8051, None, None),
1090 Relay::new(8052, None, None),
1091 Relay::new(8053, None, None),
1092 Relay::new(8055, None, None),
1093 Relay::new(8056, None, None),
1094 );
1095
1096 r51.events.push(generate_test_key_1_relay_list_event());
1097 r51.events.push(generate_test_key_1_metadata_event("fred"));
1098 r51.events.push(generate_repo_ref_event());
1099
1100 r55.events.push(generate_repo_ref_event());
1101 r55.events.push(generate_test_key_1_metadata_event("fred"));
1102 r55.events.push(generate_test_key_1_relay_list_event());
1103
1104 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1105 let (_, test_repo) =
1106 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
1107
1108 amend_last_commit(&test_repo, "add ammended-commit.md")?;
1109 test_repo.checkout("main")?;
1110
1111 // run test
1112 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
1113 p.expect("fetching updates...\r\n")?;
1114 p.expect_eventually("\r\n")?; // some updates listed here
1115 let mut c = p.expect_choice(
1116 "all proposals",
1117 vec![
1118 format!("\"{PROPOSAL_TITLE_3}\""),
1119 format!("\"{PROPOSAL_TITLE_2}\""),
1120 format!("\"{PROPOSAL_TITLE_1}\""),
1121 ],
1122 )?;
1123 c.succeeds_with(2, true, None)?;
1124 p.expect("you have an amended/rebase version the proposal that is unpublished\r\n")?;
1125 p.expect("you have previously applied the latest version of the proposal (2 ahead 0 behind 'main') but your local proposal branch has amended or rebased it (2 ahead 0 behind 'main')\r\n")?;
1126 p.expect("to view the latest proposal but retain your changes:\r\n")?;
1127 p.expect(" 1) create a new branch off the tip commit of this one to store your changes\r\n")?;
1128 p.expect(" 2) run `ngit list` and checkout the latest published version of this proposal\r\n")?;
1129 p.expect("if you are confident in your changes consider running `ngit push --force`\r\n")?;
1130
1131 let mut c = p.expect_choice(
1132 "",
1133 vec![
1134 format!("checkout local branch with unpublished changes"),
1135 format!(
1136 "discard unpublished changes and checkout new revision"
1137 ),
1138 format!("apply to current branch with `git am`"),
1139 format!("download to ./patches"),
1140 "back".to_string(),
1141 ],
1142 )?;
1143 c.succeeds_with(1, false, Some(1))?;
1144 p.expect_end_with("checked out latest version of proposal (2 ahead 0 behind 'main'), replacing unpublished version (2 ahead 0 behind 'main')\r\n")?;
1145
1146 for p in [51, 52, 53, 55, 56] {
1147 relay::shutdown_relay(8000 + p)?;
1148 }
1149 Ok(())
1150 });
1151
1152 // launch relay
1153 let _ = join!(
1154 r51.listen_until_close(),
1155 r52.listen_until_close(),
1156 r53.listen_until_close(),
1157 r55.listen_until_close(),
1158 r56.listen_until_close(),
1159 );
1160 cli_tester_handle.join().unwrap()?;
1161 println!("{:?}", r55.events);
1162 Ok(())
1163 }
1164 }
1165
1166 #[tokio::test]
1167 #[serial]
1168 async fn second_choice_discarded_unpublished_commits_and_checked_out_latest_revision()
1169 -> Result<()> {
1170 let (originating_repo, test_repo) = prep_and_run().await?;
1171 println!("test_dir: {:?}", test_repo.dir);
1172 assert_eq!(
1173 test_repo.get_tip_of_local_branch(&get_proposal_branch_name(
1174 &test_repo,
1175 FEATURE_BRANCH_NAME_1
1176 )?)?,
1177 originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?,
1178 );
1179 Ok(())
1180 }
1181 }
1182
1183 mod when_local_commits_on_uptodate_proposal {
1184 use super::*;
1185 async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> {
1186 // fallback (51,52) user write (53, 55) repo (55, 56)
1187 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
1188 Relay::new(8051, None, None),
1189 Relay::new(8052, None, None),
1190 Relay::new(8053, None, None),
1191 Relay::new(8055, None, None),
1192 Relay::new(8056, None, None),
1193 );
1194
1195 r51.events.push(generate_test_key_1_relay_list_event());
1196 r51.events.push(generate_test_key_1_metadata_event("fred"));
1197 r51.events.push(generate_repo_ref_event());
1198
1199 r55.events.push(generate_repo_ref_event());
1200 r55.events.push(generate_test_key_1_metadata_event("fred"));
1201 r55.events.push(generate_test_key_1_relay_list_event());
1202
1203 let cli_tester_handle = std::thread::spawn(
1204 move || -> Result<(GitTestRepo, GitTestRepo)> {
1205 let (originating_repo, test_repo) =
1206 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
1207
1208 // add another commit (so we have a local branch 1 ahead)
1209 std::fs::write(
1210 test_repo.dir.join("ammended-commit.md"),
1211 "some content",
1212 )?;
1213 test_repo.stage_and_commit("add ammended-commit.md")?;
1214 test_repo.checkout("main")?;
1215
1216 // run test
1217 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
1218 p.expect("fetching updates...\r\n")?;
1219 p.expect_eventually("\r\n")?; // some updates listed here
1220 let mut c = p.expect_choice(
1221 "all proposals",
1222 vec![
1223 format!("\"{PROPOSAL_TITLE_3}\""),
1224 format!("\"{PROPOSAL_TITLE_2}\""),
1225 format!("\"{PROPOSAL_TITLE_1}\""),
1226 ],
1227 )?;
1228 c.succeeds_with(2, true, None)?;
1229 p.expect(
1230 "local proposal branch exists with 1 unpublished commits on top of the most up-to-date version of the proposal (3 ahead 0 behind 'main')\r\n",
1231 )?;
1232
1233 let mut c = p.expect_choice(
1234 "",
1235 vec![
1236 format!("checkout proposal branch with 1 unpublished commits"),
1237 format!("back"),
1238 ],
1239 )?;
1240 c.succeeds_with(0, false, Some(0))?;
1241 p.expect("checked out proposal branch with 1 unpublished commits (3 ahead 0 behind 'main')\r\n")?;
1242 p.expect_end()?;
1243
1244 for p in [51, 52, 53, 55, 56] {
1245 relay::shutdown_relay(8000 + p)?;
1246 }
1247 Ok((originating_repo, test_repo))
1248 },
1249 );
1250
1251 // launch relay
1252 let _ = join!(
1253 r51.listen_until_close(),
1254 r52.listen_until_close(),
1255 r53.listen_until_close(),
1256 r55.listen_until_close(),
1257 r56.listen_until_close(),
1258 );
1259 let res = cli_tester_handle.join().unwrap()?;
1260
1261 Ok(res)
1262 }
1263
1264 mod cli_prompts {
1265 use super::*;
1266
1267 #[tokio::test]
1268 #[serial]
1269 async fn prompts_to_choose_from_proposal_titles() -> Result<()> {
1270 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
1271 Relay::new(8051, None, None),
1272 Relay::new(8052, None, None),
1273 Relay::new(8053, None, None),
1274 Relay::new(8055, None, None),
1275 Relay::new(8056, None, None),
1276 );
1277
1278 r51.events.push(generate_test_key_1_relay_list_event());
1279 r51.events.push(generate_test_key_1_metadata_event("fred"));
1280 r51.events.push(generate_repo_ref_event());
1281
1282 r55.events.push(generate_repo_ref_event());
1283 r55.events.push(generate_test_key_1_metadata_event("fred"));
1284 r55.events.push(generate_test_key_1_relay_list_event());
1285
1286 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1287 let (_, test_repo) =
1288 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
1289
1290 // add another commit (so we have a local branch 1 ahead)
1291 std::fs::write(
1292 test_repo.dir.join("ammended-commit.md"),
1293 "some content",
1294 )?;
1295 test_repo.stage_and_commit("add ammended-commit.md")?;
1296 test_repo.checkout("main")?;
1297
1298 // run test
1299 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
1300 p.expect("fetching updates...\r\n")?;
1301 p.expect_eventually("\r\n")?; // some updates listed here
1302 let mut c = p.expect_choice(
1303 "all proposals",
1304 vec![
1305 format!("\"{PROPOSAL_TITLE_3}\""),
1306 format!("\"{PROPOSAL_TITLE_2}\""),
1307 format!("\"{PROPOSAL_TITLE_1}\""),
1308 ],
1309 )?;
1310 c.succeeds_with(2, true, None)?;
1311 p.expect(
1312 "local proposal branch exists with 1 unpublished commits on top of the most up-to-date version of the proposal (3 ahead 0 behind 'main')\r\n",
1313 )?;
1314
1315 let mut c = p.expect_choice(
1316 "",
1317 vec![
1318 format!("checkout proposal branch with 1 unpublished commits"),
1319 format!("back"),
1320 ],
1321 )?;
1322 c.succeeds_with(0, false, Some(0))?;
1323 p.expect("checked out proposal branch with 1 unpublished commits (3 ahead 0 behind 'main')\r\n")?;
1324 p.expect_end()?;
1325
1326 for p in [51, 52, 53, 55, 56] {
1327 relay::shutdown_relay(8000 + p)?;
1328 }
1329 Ok(())
1330 });
1331
1332 // launch relay
1333 let _ = join!(
1334 r51.listen_until_close(),
1335 r52.listen_until_close(),
1336 r53.listen_until_close(),
1337 r55.listen_until_close(),
1338 r56.listen_until_close(),
1339 );
1340 cli_tester_handle.join().unwrap()?;
1341 println!("{:?}", r55.events);
1342 Ok(())
1343 }
1344 }
1345
1346 #[tokio::test]
1347 #[serial]
1348 async fn proposal_branch_checked_out() -> Result<()> {
1349 let (_, test_repo) = prep_and_run().await?;
1350 assert_eq!(
1351 get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?,
1352 test_repo.get_checked_out_branch_name()?,
1353 );
1354 Ok(())
1355 }
1356
1357 #[tokio::test]
1358 #[serial]
1359 async fn didnt_overwrite_local_appendments() -> Result<()> {
1360 let (originating_repo, test_repo) = prep_and_run().await?;
1361 assert_ne!(
1362 test_repo.get_tip_of_local_branch(&get_proposal_branch_name(
1363 &test_repo,
1364 FEATURE_BRANCH_NAME_1
1365 )?)?,
1366 originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?,
1367 );
1368 Ok(())
1369 }
1370 }
1371
1372 mod when_latest_revision_rebases_branch {
1373
1374 use tokio::task::JoinHandle;
1375
1376 use super::*;
1377
1378 async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> {
1379 // fallback (51,52) user write (53, 55) repo (55, 56)
1380 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
1381 Relay::new(8051, None, None),
1382 Relay::new(8052, None, None),
1383 Relay::new(8053, None, None),
1384 Relay::new(8055, None, None),
1385 Relay::new(8056, None, None),
1386 );
1387
1388 r51.events.push(generate_test_key_1_relay_list_event());
1389 r51.events.push(generate_test_key_1_metadata_event("fred"));
1390 r51.events.push(generate_repo_ref_event());
1391
1392 r55.events.push(generate_repo_ref_event());
1393 r55.events.push(generate_test_key_1_metadata_event("fred"));
1394 r55.events.push(generate_test_key_1_relay_list_event());
1395
1396 let cli_tester_handle: JoinHandle<Result<(GitTestRepo, GitTestRepo)>> =
1397 tokio::task::spawn_blocking(move || {
1398 let (originating_repo, test_repo) = create_proposals_with_first_rebased_and_repo_with_latest_main_and_unrebased_proposal()?;
1399 test_repo.checkout("main")?;
1400
1401 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
1402 p.expect("fetching updates...\r\n")?;
1403 p.expect_eventually("\r\n")?; // some updates listed here
1404 let mut c = p.expect_choice(
1405 "all proposals",
1406 vec![
1407 format!("\"{PROPOSAL_TITLE_3}\""),
1408 format!("\"{PROPOSAL_TITLE_2}\""),
1409 format!("\"{PROPOSAL_TITLE_1}\""),
1410 ],
1411 )?;
1412 c.succeeds_with(2, true, None)?;
1413 p.expect("updated proposal available (2 ahead 0 behind 'main'). existing version is 2 ahead 1 behind 'main'\r\n")?;
1414 let mut c = p.expect_choice(
1415 "",
1416 vec![
1417 format!("checkout and overwrite existing proposal branch"),
1418 format!("checkout existing outdated proposal branch"),
1419 format!("apply to current branch with `git am`"),
1420 format!("download to ./patches"),
1421 format!("back"),
1422 ],
1423 )?;
1424 c.succeeds_with(0, false, Some(0))?;
1425 p.expect("checked out new version of proposal (2 ahead 0 behind 'main'), replacing old version (2 ahead 1 behind 'main')\r\n")?;
1426 p.expect_end()?;
1427
1428 for p in [51, 52, 53, 55, 56] {
1429 relay::shutdown_relay(8000 + p)?;
1430 }
1431 Ok((originating_repo, test_repo))
1432 });
1433
1434 // launch relay
1435 let _ = join!(
1436 r51.listen_until_close(),
1437 r52.listen_until_close(),
1438 r53.listen_until_close(),
1439 r55.listen_until_close(),
1440 r56.listen_until_close(),
1441 );
1442 let res = cli_tester_handle.await??;
1443
1444 Ok(res)
1445 }
1446
1447 mod cli_prompts {
1448 use super::*;
1449
1450 #[tokio::test]
1451 #[serial]
1452 async fn prompts_to_choose_from_proposal_titles() -> Result<()> {
1453 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
1454 Relay::new(8051, None, None),
1455 Relay::new(8052, None, None),
1456 Relay::new(8053, None, None),
1457 Relay::new(8055, None, None),
1458 Relay::new(8056, None, None),
1459 );
1460
1461 r51.events.push(generate_test_key_1_relay_list_event());
1462 r51.events.push(generate_test_key_1_metadata_event("fred"));
1463 r51.events.push(generate_repo_ref_event());
1464
1465 r55.events.push(generate_repo_ref_event());
1466 r55.events.push(generate_test_key_1_metadata_event("fred"));
1467 r55.events.push(generate_test_key_1_relay_list_event());
1468
1469 let cli_tester_handle: JoinHandle<Result<()>> = tokio::task::spawn_blocking(
1470 move || {
1471 let (_, test_repo) = create_proposals_with_first_rebased_and_repo_with_latest_main_and_unrebased_proposal()?;
1472 test_repo.checkout("main")?;
1473
1474 let mut p = CliTester::new_from_dir(&test_repo.dir, ["list"]);
1475 p.expect("fetching updates...\r\n")?;
1476 p.expect_eventually("\r\n")?; // some updates listed here
1477 let mut c = p.expect_choice(
1478 "all proposals",
1479 vec![
1480 format!("\"{PROPOSAL_TITLE_3}\""),
1481 format!("\"{PROPOSAL_TITLE_2}\""),
1482 format!("\"{PROPOSAL_TITLE_1}\""),
1483 ],
1484 )?;
1485 c.succeeds_with(2, true, None)?;
1486 p.expect("updated proposal available (2 ahead 0 behind 'main'). existing version is 2 ahead 1 behind 'main'\r\n")?;
1487 let mut c = p.expect_choice(
1488 "",
1489 vec![
1490 format!("checkout and overwrite existing proposal branch"),
1491 format!("checkout existing outdated proposal branch"),
1492 format!("apply to current branch with `git am`"),
1493 format!("download to ./patches"),
1494 format!("back"),
1495 ],
1496 )?;
1497 c.succeeds_with(0, false, Some(0))?;
1498 p.expect("checked out new version of proposal (2 ahead 0 behind 'main'), replacing old version (2 ahead 1 behind 'main')\r\n")?;
1499 p.expect_end()?;
1500
1501 for p in [51, 52, 53, 55, 56] {
1502 relay::shutdown_relay(8000 + p)?;
1503 }
1504 Ok(())
1505 },
1506 );
1507
1508 // launch relay
1509 let _ = join!(
1510 r51.listen_until_close(),
1511 r52.listen_until_close(),
1512 r53.listen_until_close(),
1513 r55.listen_until_close(),
1514 r56.listen_until_close(),
1515 );
1516 cli_tester_handle.await??;
1517 println!("{:?}", r55.events);
1518 Ok(())
1519 }
1520 }
1521
1522 #[tokio::test]
1523 #[serial]
1524 async fn proposal_branch_checked_out() -> Result<()> {
1525 let (_, test_repo) = prep_and_run().await?;
1526 assert_eq!(
1527 get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?,
1528 test_repo.get_checked_out_branch_name()?,
1529 );
1530 Ok(())
1531 }
1532
1533 #[tokio::test]
1534 #[serial]
1535 async fn proposal_branch_tip_is_most_recent_proposal_revision_tip() -> Result<()> {
1536 let (originating_repo, test_repo) = prep_and_run().await?;
1537 assert_eq!(
1538 originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?,
1539 test_repo.get_tip_of_local_branch(&get_proposal_branch_name(
1540 &test_repo,
1541 FEATURE_BRANCH_NAME_1
1542 )?)?,
1543 );
1544 Ok(())
1545 }
1546 }
1547 }
1548 }
1549}
diff --git a/tests/ngit/login.rs b/tests/ngit/login.rs
deleted file mode 100644
index 477b25b..0000000
--- a/tests/ngit/login.rs
+++ /dev/null
@@ -1,1186 +0,0 @@
1use anyhow::Result;
2use git::GitTestRepo;
3use serial_test::serial;
4use test_utils::*;
5
6static EXPECTED_NSEC_PROMPT: &str = "login with nsec / bunker url / nostr address";
7static EXPECTED_LOCAL_REPOSITORY_PROMPT: &str = "just for this repository?";
8static EXPECTED_REQUIRE_PASSWORD_PROMPT: &str = "require password?";
9static EXPECTED_SET_PASSWORD_PROMPT: &str = "encrypt with password";
10static EXPECTED_SET_PASSWORD_CONFIRM_PROMPT: &str = "confirm password";
11
12fn standard_first_time_login_encrypting_nsec() -> Result<CliTester> {
13 let test_repo = GitTestRepo::default();
14 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login", "--offline"]);
15
16 p.expect_input_eventually(EXPECTED_NSEC_PROMPT)?
17 .succeeds_with(TEST_KEY_1_NSEC)?;
18
19 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
20 .succeeds_with(Some(true))?;
21
22 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
23 .succeeds_with(Some(true))?;
24
25 p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)?
26 .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)?
27 .succeeds_with(TEST_PASSWORD)?;
28
29 p.expect_end_eventually()?;
30 Ok(p)
31}
32
33fn expect_qr_prompt_opt_for_other_methods(p: &mut CliTester) -> Result<()> {
34 p.expect_eventually("scan QR or paste into remote signer")?;
35 p.expect_eventually("\r\n")?;
36 p.expect_eventually("login with nsec / bunker url / nostr address instead")?;
37 p.expect_eventually("\r\n")?;
38 p.send_line("")?;
39 // p.expect_eventually("\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r")?;
40 // p.expect_eventually("\r\r\r\r\r\r\r\r\r\r\r\r\r")?;
41
42 Ok(())
43}
44mod with_relays {
45 use anyhow::Ok;
46 use futures::join;
47 use test_utils::relay::{shutdown_relay, ListenerReqFunc, Relay};
48
49 use super::*;
50
51 mod when_user_relay_list_aligns_with_fallback_relays {
52 // this simplifies testing
53 use super::*;
54
55 mod when_first_time_login {
56 use super::*;
57
58 // falls_back_to_fallback_relays - this is implict in the tests
59
60 mod dislays_logged_in_with_correct_name {
61
62 use super::*;
63
64 async fn run_test_displays_correct_name(
65 relay_listener1: Option<ListenerReqFunc<'_>>,
66 relay_listener2: Option<ListenerReqFunc<'_>>,
67 ) -> Result<()> {
68 let (mut r51, mut r52) = (
69 Relay::new(8051, None, relay_listener1),
70 Relay::new(8052, None, relay_listener2),
71 );
72
73 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
74 let test_repo = GitTestRepo::default();
75 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login"]);
76 expect_qr_prompt_opt_for_other_methods(&mut p)?;
77 p.expect_input_eventually(EXPECTED_NSEC_PROMPT)?
78 .succeeds_with(TEST_KEY_1_NSEC)?;
79
80 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
81 .succeeds_with(Some(true))?;
82
83 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
84 .succeeds_with(Some(false))?;
85
86 p.expect("saved login details to local git config\r\n")?;
87
88 p.expect("searching for profile...\r\n")?;
89
90 p.expect_end_with("logged in as fred\r\n")?;
91 for p in [51, 52] {
92 shutdown_relay(8000 + p)?;
93 }
94 Ok(())
95 });
96
97 // launch relay
98 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
99
100 cli_tester_handle.join().unwrap()?;
101 Ok(())
102 }
103
104 async fn run_test_displays_fallback_to_npub(
105 relay_listener1: Option<ListenerReqFunc<'_>>,
106 relay_listener2: Option<ListenerReqFunc<'_>>,
107 ) -> Result<()> {
108 let (mut r51, mut r52) = (
109 Relay::new(8051, None, relay_listener1),
110 Relay::new(8052, None, relay_listener2),
111 );
112
113 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
114 let test_repo = GitTestRepo::default();
115 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login"]);
116
117 expect_qr_prompt_opt_for_other_methods(&mut p)?;
118 p.expect_input_eventually(EXPECTED_NSEC_PROMPT)?
119 .succeeds_with(TEST_KEY_1_NSEC)?;
120
121 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
122 .succeeds_with(Some(true))?;
123
124 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
125 .succeeds_with(Some(false))?;
126
127 p.expect("saved login details to local git config\r\n")?;
128
129 p.expect("searching for profile...\r\n")?;
130
131 p.expect("cannot extract account name from account metadata...\r\n")?;
132
133 p.expect_end_with(
134 format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str(),
135 )?;
136 for p in [51, 52] {
137 shutdown_relay(8000 + p)?;
138 }
139 Ok(())
140 });
141
142 // launch relay
143 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
144
145 cli_tester_handle.join().unwrap()?;
146 Ok(())
147 }
148
149 #[tokio::test]
150 #[serial]
151 async fn when_latest_metadata_and_relay_list_on_all_relays() -> Result<()> {
152 run_test_displays_correct_name(
153 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
154 relay.respond_events(
155 client_id,
156 &subscription_id,
157 &vec![
158 generate_test_key_1_metadata_event("fred"),
159 generate_test_key_1_relay_list_event_same_as_fallback(),
160 ],
161 )?;
162 Ok(())
163 }),
164 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
165 relay.respond_events(
166 client_id,
167 &subscription_id,
168 &vec![
169 generate_test_key_1_metadata_event("fred"),
170 generate_test_key_1_relay_list_event_same_as_fallback(),
171 ],
172 )?;
173 Ok(())
174 }),
175 )
176 .await
177 }
178
179 mod poorly_quality_metadata_event {
180 use super::*;
181
182 #[tokio::test]
183 #[serial]
184 async fn when_metadata_contains_only_display_name() -> Result<()> {
185 run_test_displays_correct_name(
186 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
187 relay.respond_events(
188 client_id,
189 &subscription_id,
190 &vec![
191 nostr::event::EventBuilder::metadata(
192 &nostr::Metadata::new().display_name("fred"),
193 )
194 .to_event(&TEST_KEY_1_KEYS)
195 .unwrap(),
196 generate_test_key_1_relay_list_event_same_as_fallback(),
197 ],
198 )?;
199 Ok(())
200 }),
201 None,
202 )
203 .await
204 }
205
206 #[tokio::test]
207 #[serial]
208 async fn when_metadata_contains_only_displayname() -> Result<()> {
209 println!(
210 "displayName: {}",
211 nostr::Metadata::new()
212 .custom_field("displayName", "fred")
213 .custom
214 .get("displayName")
215 .unwrap()
216 );
217 println!(
218 "name: {}",
219 nostr::Metadata::new().name("fred").name.unwrap()
220 );
221
222 run_test_displays_correct_name(
223 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
224 relay.respond_events(
225 client_id,
226 &subscription_id,
227 &vec![
228 nostr::event::EventBuilder::metadata(
229 &nostr::Metadata::new()
230 .custom_field("displayName", "fred"),
231 )
232 .to_event(&TEST_KEY_1_KEYS)
233 .unwrap(),
234 generate_test_key_1_relay_list_event_same_as_fallback(),
235 ],
236 )?;
237 Ok(())
238 }),
239 None,
240 )
241 .await
242 }
243
244 #[tokio::test]
245 #[serial]
246 async fn displays_npub_when_metadata_contains_no_name_displayname_or_display_name()
247 -> Result<()> {
248 run_test_displays_fallback_to_npub(
249 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
250 relay.respond_events(
251 client_id,
252 &subscription_id,
253 &vec![
254 nostr::event::EventBuilder::metadata(
255 &nostr::Metadata::new().about("other info in metadata"),
256 )
257 .to_event(&TEST_KEY_1_KEYS)
258 .unwrap(),
259 generate_test_key_1_relay_list_event_same_as_fallback(),
260 ],
261 )?;
262 Ok(())
263 }),
264 None,
265 )
266 .await
267 }
268 }
269
270 #[tokio::test]
271 #[serial]
272 async fn when_latest_metadata_and_relay_list_on_some_relays_but_others_have_none()
273 -> Result<()> {
274 run_test_displays_correct_name(
275 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
276 relay.respond_events(
277 client_id,
278 &subscription_id,
279 &vec![
280 generate_test_key_1_metadata_event("fred"),
281 generate_test_key_1_relay_list_event_same_as_fallback(),
282 ],
283 )?;
284 Ok(())
285 }),
286 None,
287 )
288 .await
289 }
290
291 #[tokio::test]
292 #[serial]
293 async fn when_latest_metadata_only_on_relay_and_relay_list_on_another() -> Result<()>
294 {
295 run_test_displays_correct_name(
296 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
297 relay.respond_events(
298 client_id,
299 &subscription_id,
300 &vec![generate_test_key_1_metadata_event("fred")],
301 )?;
302 Ok(())
303 }),
304 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
305 relay.respond_events(
306 client_id,
307 &subscription_id,
308 &vec![generate_test_key_1_relay_list_event_same_as_fallback()],
309 )?;
310 Ok(())
311 }),
312 )
313 .await
314 }
315
316 #[tokio::test]
317 #[serial]
318 async fn when_some_relays_return_old_metadata_event() -> Result<()> {
319 run_test_displays_correct_name(
320 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
321 relay.respond_events(
322 client_id,
323 &subscription_id,
324 &vec![
325 generate_test_key_1_metadata_event("fred"),
326 generate_test_key_1_relay_list_event_same_as_fallback(),
327 ],
328 )?;
329 Ok(())
330 }),
331 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
332 relay.respond_events(
333 client_id,
334 &subscription_id,
335 &vec![generate_test_key_1_metadata_event_old("fred old")],
336 )?;
337 Ok(())
338 }),
339 )
340 .await
341 }
342
343 #[tokio::test]
344 #[serial]
345 async fn when_some_relays_return_other_users_metadata() -> Result<()> {
346 run_test_displays_correct_name(
347 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
348 relay.respond_events(
349 client_id,
350 &subscription_id,
351 &vec![generate_test_key_2_metadata_event("carole")],
352 )?;
353 Ok(())
354 }),
355 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
356 relay.respond_events(
357 client_id,
358 &subscription_id,
359 &vec![
360 generate_test_key_1_metadata_event_old("fred"),
361 generate_test_key_1_relay_list_event_same_as_fallback(),
362 ],
363 )?;
364 Ok(())
365 }),
366 )
367 .await
368 }
369
370 #[tokio::test]
371 #[serial]
372 async fn when_some_relays_return_other_event_kinds() -> Result<()> {
373 run_test_displays_correct_name(
374 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
375 let event = generate_test_key_1_kind_event(nostr::Kind::TextNote);
376 relay.respond_events(
377 client_id,
378 &subscription_id,
379 &vec![make_event_old_or_change_user(event, &TEST_KEY_1_KEYS, 0)],
380 )?;
381 Ok(())
382 }),
383 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
384 relay.respond_events(
385 client_id,
386 &subscription_id,
387 &vec![
388 generate_test_key_1_metadata_event_old("fred"),
389 generate_test_key_1_relay_list_event_same_as_fallback(),
390 ],
391 )?;
392 Ok(())
393 }),
394 )
395 .await
396 }
397
398 mod when_specifying_command_line_nsec_only {
399 use super::*;
400
401 #[tokio::test]
402 #[serial]
403 async fn displays_correct_name() -> Result<()> {
404 run_test_when_specifying_command_line_nsec_only_displays_correct_name(
405 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
406 relay.respond_events(
407 client_id,
408 &subscription_id,
409 &vec![
410 generate_test_key_1_metadata_event("fred"),
411 generate_test_key_1_relay_list_event_same_as_fallback(),
412 ],
413 )?;
414 Ok(())
415 }),
416 None,
417 )
418 .await
419 }
420 async fn run_test_when_specifying_command_line_nsec_only_displays_correct_name(
421 relay_listener1: Option<ListenerReqFunc<'_>>,
422 relay_listener2: Option<ListenerReqFunc<'_>>,
423 ) -> Result<()> {
424 let (mut r51, mut r52) = (
425 Relay::new(8051, None, relay_listener1),
426 Relay::new(8052, None, relay_listener2),
427 );
428
429 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
430 let test_repo = GitTestRepo::default();
431 let mut p = CliTester::new_from_dir(
432 &test_repo.dir,
433 ["login", "--nsec", TEST_KEY_1_NSEC],
434 );
435
436 p.expect("saved login details to local git config\r\n")?;
437
438 p.expect("searching for profile...\r\n")?;
439
440 p.expect_end_with("logged in as fred\r\n")?;
441 for p in [51, 52] {
442 shutdown_relay(8000 + p)?;
443 }
444 Ok(())
445 });
446
447 // launch relay
448 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
449
450 cli_tester_handle.join().unwrap()?;
451 Ok(())
452 }
453 }
454 mod when_specifying_command_line_password_only {
455 use super::*;
456
457 #[tokio::test]
458 #[serial]
459 async fn displays_correct_name() -> Result<()> {
460 run_test_when_specifying_command_line_password_only_displays_correct_name(
461 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
462 relay.respond_events(
463 client_id,
464 &subscription_id,
465 &vec![
466 generate_test_key_1_metadata_event("fred"),
467 generate_test_key_1_relay_list_event_same_as_fallback(),
468 ],
469 )?;
470 Ok(())
471 }),
472 None,
473 )
474 .await
475 }
476 async fn run_test_when_specifying_command_line_password_only_displays_correct_name(
477 relay_listener1: Option<ListenerReqFunc<'_>>,
478 relay_listener2: Option<ListenerReqFunc<'_>>,
479 ) -> Result<()> {
480 let (mut r51, mut r52) = (
481 Relay::new(8051, None, relay_listener1),
482 Relay::new(8052, None, relay_listener2),
483 );
484
485 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
486 let test_repo = GitTestRepo::default();
487 CliTester::new_from_dir(
488 &test_repo.dir,
489 [
490 "login",
491 "--offline",
492 "--nsec",
493 TEST_KEY_1_NSEC,
494 "--password",
495 TEST_PASSWORD,
496 ],
497 )
498 .expect_end_eventually()?;
499
500 let mut p = CliTester::new_from_dir(
501 &test_repo.dir,
502 ["login", "--password", TEST_PASSWORD],
503 );
504
505 p.expect("searching for profile...\r\n")?;
506
507 p.expect_end_with("logged in as fred\r\n")?;
508 for p in [51, 52] {
509 shutdown_relay(8000 + p)?;
510 }
511 Ok(())
512 });
513
514 // launch relay
515 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
516
517 cli_tester_handle.join().unwrap()?;
518 Ok(())
519 }
520 }
521
522 mod when_specifying_command_line_nsec_and_password {
523 use super::*;
524
525 #[tokio::test]
526 #[serial]
527 async fn displays_correct_name() -> Result<()> {
528 run_test_when_specifying_command_line_nsec_and_password_displays_correct_name(
529 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
530 relay.respond_events(
531 client_id,
532 &subscription_id,
533 &vec![
534 generate_test_key_1_metadata_event("fred"),
535 generate_test_key_1_relay_list_event_same_as_fallback(),
536 ],
537 )?;
538 Ok(())
539 }),
540 None,
541 ).await
542 }
543 async fn run_test_when_specifying_command_line_nsec_and_password_displays_correct_name(
544 relay_listener1: Option<ListenerReqFunc<'_>>,
545 relay_listener2: Option<ListenerReqFunc<'_>>,
546 ) -> Result<()> {
547 let (mut r51, mut r52) = (
548 Relay::new(8051, None, relay_listener1),
549 Relay::new(8052, None, relay_listener2),
550 );
551
552 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
553 let test_repo = GitTestRepo::default();
554 let mut p = CliTester::new_from_dir(
555 &test_repo.dir,
556 [
557 "login",
558 "--nsec",
559 TEST_KEY_1_NSEC,
560 "--password",
561 TEST_PASSWORD,
562 ],
563 );
564
565 p.expect("saved login details to local git config\r\n")?;
566
567 p.expect("searching for profile...\r\n")?;
568
569 p.expect_end_with("logged in as fred\r\n")?;
570 for p in [51, 52] {
571 shutdown_relay(8000 + p)?;
572 }
573 Ok(())
574 });
575
576 // launch relay
577 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
578
579 cli_tester_handle.join().unwrap()?;
580 Ok(())
581 }
582 }
583 }
584
585 mod when_no_metadata_found {
586 use super::*;
587
588 #[tokio::test]
589 #[serial]
590 async fn warm_user_and_displays_npub() -> Result<()> {
591 run_test_when_no_metadata_found_warns_user_and_uses_npub(None, None).await
592 }
593
594 async fn run_test_when_no_metadata_found_warns_user_and_uses_npub(
595 relay_listener1: Option<ListenerReqFunc<'_>>,
596 relay_listener2: Option<ListenerReqFunc<'_>>,
597 ) -> Result<()> {
598 let (mut r51, mut r52) = (
599 Relay::new(8051, None, relay_listener1),
600 Relay::new(8052, None, relay_listener2),
601 );
602
603 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
604 let test_repo = GitTestRepo::default();
605 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login"]);
606
607 expect_qr_prompt_opt_for_other_methods(&mut p)?;
608 p.expect_input_eventually(EXPECTED_NSEC_PROMPT)?
609 .succeeds_with(TEST_KEY_1_NSEC)?;
610
611 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
612 .succeeds_with(Some(true))?;
613
614 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
615 .succeeds_with(Some(false))?;
616
617 p.expect("saved login details to local git config\r\n")?;
618
619 p.expect("searching for profile...\r\n")?;
620
621 p.expect("cannot find profile...\r\n")?;
622
623 p.expect_end_with(format!("logged in as {TEST_KEY_1_NPUB}\r\n").as_str())?;
624 for p in [51, 52] {
625 shutdown_relay(8000 + p)?;
626 }
627 Ok(())
628 });
629
630 // launch relay
631 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
632
633 cli_tester_handle.join().unwrap()?;
634 Ok(())
635 }
636 }
637
638 mod when_metadata_but_no_relay_list_found {
639 use super::*;
640
641 #[tokio::test]
642 #[serial]
643 async fn warm_user_and_displays_name() -> Result<()> {
644 run_test_when_no_relay_list_found_warns_user_and_uses_npub(
645 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
646 relay.respond_events(
647 client_id,
648 &subscription_id,
649 &vec![generate_test_key_1_metadata_event("fred")],
650 )?;
651 Ok(())
652 }),
653 None,
654 )
655 .await
656 }
657
658 async fn run_test_when_no_relay_list_found_warns_user_and_uses_npub(
659 relay_listener1: Option<ListenerReqFunc<'_>>,
660 relay_listener2: Option<ListenerReqFunc<'_>>,
661 ) -> Result<()> {
662 let (mut r51, mut r52) = (
663 Relay::new(8051, None, relay_listener1),
664 Relay::new(8052, None, relay_listener2),
665 );
666
667 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
668 let test_repo = GitTestRepo::default();
669 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login"]);
670
671 expect_qr_prompt_opt_for_other_methods(&mut p)?;
672 p.expect_input_eventually(EXPECTED_NSEC_PROMPT)?
673 .succeeds_with(TEST_KEY_1_NSEC)?;
674
675 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
676 .succeeds_with(Some(true))?;
677
678 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
679 .succeeds_with(Some(false))?;
680
681 p.expect("saved login details to local git config\r\n")?;
682
683 p.expect("searching for profile...\r\n")?;
684
685 p.expect("cannot find your relay list. consider using another nostr client to create one to enhance your nostr experience.\r\n")?;
686
687 p.expect_end_with("logged in as fred\r\n")?;
688 for p in [51, 52] {
689 shutdown_relay(8000 + p)?;
690 }
691 Ok(())
692 });
693
694 // launch relay
695 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
696
697 cli_tester_handle.join().unwrap()?;
698 Ok(())
699 }
700 }
701 }
702
703 mod when_second_time_login_and_details_already_fetched {
704 use super::*;
705
706 mod uses_cache_and_stores_and_retrieves_ncryptsec_from_local_git_config {
707 use super::*;
708
709 #[tokio::test]
710 #[serial]
711 async fn dislays_logged_in_with_correct_name() -> Result<()> {
712 run_test_dislays_logged_in_with_correct_name(Some(
713 &|relay, client_id, subscription_id, _| -> Result<()> {
714 relay.respond_events(
715 client_id,
716 &subscription_id,
717 &vec![
718 generate_test_key_1_metadata_event("fred"),
719 generate_test_key_1_relay_list_event_same_as_fallback(),
720 ],
721 )?;
722 Ok(())
723 },
724 ))
725 .await
726 }
727 async fn run_test_dislays_logged_in_with_correct_name(
728 relay_listener: Option<ListenerReqFunc<'_>>,
729 ) -> Result<()> {
730 let (mut r51, mut r52) = (
731 Relay::new(8051, None, relay_listener),
732 Relay::new(8052, None, None),
733 );
734
735 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
736 let test_repo = GitTestRepo::default();
737 let mut p = CliTester::new_from_dir(
738 &test_repo.dir,
739 [
740 "login",
741 "--nsec",
742 TEST_KEY_1_NSEC,
743 "--password",
744 TEST_PASSWORD,
745 ],
746 );
747
748 p.expect("saved login details to local git config\r\n")?;
749
750 p.expect_end_eventually_with("logged in as fred\r\n")?;
751
752 for p in [51, 52] {
753 shutdown_relay(8000 + p)?;
754 }
755
756 let mut p = CliTester::new_from_dir(
757 &test_repo.dir,
758 ["login", "--password", TEST_PASSWORD],
759 );
760
761 p.expect_end_eventually_with("logged in as fred\r\n")?;
762
763 Ok(())
764 });
765
766 // launch relay
767 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
768
769 cli_tester_handle.join().unwrap()?;
770
771 Ok(())
772 }
773 }
774 }
775 }
776 mod when_user_relay_list_contains_write_relays_not_in_fallback_list {
777 use super::*;
778 mod when_latest_metadata_not_on_fallback_relays_only_on_relays_in_user_list {
779 use super::*;
780 async fn run_test_displays_correct_name(
781 relay_listener1: Option<ListenerReqFunc<'_>>,
782 relay_listener2: Option<ListenerReqFunc<'_>>,
783 ) -> Result<()> {
784 let (mut r51, mut r52, mut r53, mut r55) = (
785 Relay::new(8051, None, relay_listener1),
786 Relay::new(8052, None, None),
787 Relay::new(8053, None, relay_listener2),
788 Relay::new(8055, None, None),
789 );
790
791 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
792 let test_repo = GitTestRepo::default();
793 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login"]);
794
795 expect_qr_prompt_opt_for_other_methods(&mut p)?;
796 p.expect_input_eventually(EXPECTED_NSEC_PROMPT)?
797 .succeeds_with(TEST_KEY_1_NSEC)?;
798
799 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
800 .succeeds_with(Some(true))?;
801
802 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
803 .succeeds_with(Some(false))?;
804
805 p.expect("saved login details to local git config\r\n")?;
806
807 p.expect("searching for profile...\r\n")?;
808
809 p.expect_end_with("logged in as fred\r\n")?;
810 for p in [51, 52, 53, 55] {
811 shutdown_relay(8000 + p)?;
812 }
813 Ok(())
814 });
815
816 // launch relay
817 let _ = join!(
818 r51.listen_until_close(),
819 r52.listen_until_close(),
820 r53.listen_until_close(),
821 r55.listen_until_close(),
822 );
823
824 cli_tester_handle.join().unwrap()?;
825 Ok(())
826 }
827
828 /// this also tests that additional relays are queried
829 #[tokio::test]
830 #[serial]
831 async fn displays_correct_name() -> Result<()> {
832 run_test_displays_correct_name(
833 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
834 relay.respond_events(
835 client_id,
836 &subscription_id,
837 &vec![
838 generate_test_key_1_metadata_event_old("Fred"),
839 generate_test_key_1_relay_list_event(),
840 ],
841 )?;
842 Ok(())
843 }),
844 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
845 relay.respond_events(
846 client_id,
847 &subscription_id,
848 &vec![
849 generate_test_key_1_metadata_event("fred"),
850 generate_test_key_1_relay_list_event(),
851 ],
852 )?;
853 Ok(())
854 }),
855 )
856 .await
857 }
858 }
859 }
860}
861
862/// using the offline flag simplifies the test. relay interaction is tested
863/// seperately
864mod with_offline_flag {
865 use super::*;
866 mod when_first_time_login {
867 use super::*;
868
869 #[test]
870 fn prompts_for_nsec_and_password() -> Result<()> {
871 standard_first_time_login_encrypting_nsec()?;
872 Ok(())
873 }
874
875 #[test]
876 fn succeeds_with_text_logged_in_as_npub() -> Result<()> {
877 let test_repo = GitTestRepo::default();
878 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login", "--offline"]);
879
880 p.expect_input(EXPECTED_NSEC_PROMPT)?
881 .succeeds_with(TEST_KEY_1_NSEC)?;
882
883 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
884 .succeeds_with(Some(true))?;
885
886 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
887 .succeeds_with(Some(true))?;
888
889 p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)?
890 .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)?
891 .succeeds_with(TEST_PASSWORD)?;
892
893 p.expect("saved login details to local git config\r\n")?;
894
895 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
896 }
897
898 #[test]
899 fn succeeds_with_hex_secret_key_in_place_of_nsec() -> Result<()> {
900 let test_repo = GitTestRepo::default();
901 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login", "--offline"]);
902
903 p.expect_input(EXPECTED_NSEC_PROMPT)?
904 .succeeds_with(TEST_KEY_1_SK_HEX)?;
905
906 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
907 .succeeds_with(Some(true))?;
908
909 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
910 .succeeds_with(Some(true))?;
911
912 p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)?
913 .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)?
914 .succeeds_with(TEST_PASSWORD)?;
915
916 p.expect("saved login details to local git config\r\n")?;
917
918 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
919 }
920
921 mod when_invalid_nsec {
922 use super::*;
923
924 #[test]
925 fn prompts_for_nsec_until_valid() -> Result<()> {
926 let invalid_nsec_response =
927 "invalid. try again with nostr address / bunker uri / nsec";
928
929 let test_repo = GitTestRepo::default();
930 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login", "--offline"]);
931
932 p.expect_input(EXPECTED_NSEC_PROMPT)?
933 // this behaviour is intentional. rejecting the response with dialoguer
934 // hides the original input from the user so they
935 // cannot see the mistake they made.
936 .succeeds_with(TEST_INVALID_NSEC)?;
937
938 p.expect_input(invalid_nsec_response)?
939 .succeeds_with(TEST_INVALID_NSEC)?;
940
941 p.expect_input(invalid_nsec_response)?
942 .succeeds_with(TEST_KEY_1_NSEC)?;
943
944 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
945 .succeeds_with(Some(true))?;
946
947 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
948 .succeeds_with(Some(true))?;
949
950 p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)?
951 .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)?
952 .succeeds_with(TEST_PASSWORD)?;
953
954 p.expect("saved login details to local git config\r\n")?;
955
956 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
957 }
958 }
959 }
960
961 mod when_called_with_nsec_parameter_only {
962 use super::*;
963
964 #[test]
965 fn valid_nsec_param_succeeds_without_prompts() -> Result<()> {
966 let test_repo = GitTestRepo::default();
967 let mut p = CliTester::new_from_dir(
968 &test_repo.dir,
969 ["login", "--offline", "--nsec", TEST_KEY_1_NSEC],
970 );
971
972 p.expect("saved login details to local git config\r\n")?;
973
974 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
975 }
976
977 #[test]
978 fn forgets_identity() -> Result<()> {
979 let test_repo = GitTestRepo::default();
980 let mut p = CliTester::new_from_dir(
981 &test_repo.dir,
982 ["login", "--offline", "--nsec", TEST_KEY_1_NSEC],
983 );
984
985 p.expect("saved login details to local git config\r\n")?;
986
987 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())?;
988
989 p = CliTester::new_from_dir(&test_repo.dir, ["login", "--offline"]);
990
991 p.expect_input(EXPECTED_NSEC_PROMPT)?
992 .succeeds_with(TEST_KEY_1_NSEC)?;
993
994 p.exit()
995 }
996
997 mod when_logging_in_as_different_nsec {
998 use super::*;
999
1000 #[test]
1001 fn valid_nsec_param_succeeds_without_prompts_and_logs_in() -> Result<()> {
1002 standard_first_time_login_encrypting_nsec()?.exit()?;
1003 let test_repo = GitTestRepo::default();
1004 let mut p = CliTester::new_from_dir(
1005 &test_repo.dir,
1006 ["login", "--offline", "--nsec", TEST_KEY_2_NSEC],
1007 );
1008
1009 p.expect("saved login details to local git config\r\n")?;
1010
1011 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NPUB).as_str())
1012 }
1013 }
1014 #[test]
1015 fn invalid_nsec_param_fails_without_prompts() -> Result<()> {
1016 let test_repo = GitTestRepo::default();
1017 let mut p = CliTester::new_from_dir(
1018 &test_repo.dir,
1019 ["login", "--offline", "--nsec", TEST_INVALID_NSEC],
1020 );
1021
1022 p.expect_end_with(
1023 "Error: invalid nsec parameter\r\n\r\nCaused by:\r\n Invalid secret key\r\n",
1024 )
1025 }
1026 }
1027
1028 mod when_called_with_nsec_and_password_parameter {
1029 use super::*;
1030
1031 #[test]
1032 fn valid_nsec_param_succeeds_without_prompts() -> Result<()> {
1033 let test_repo = GitTestRepo::default();
1034 let mut p = CliTester::new_from_dir(
1035 &test_repo.dir,
1036 [
1037 "login",
1038 "--offline",
1039 "--nsec",
1040 TEST_KEY_1_NSEC,
1041 "--password",
1042 TEST_PASSWORD,
1043 ],
1044 );
1045 p.expect("saved login details to local git config\r\n")?;
1046 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
1047 }
1048
1049 #[test]
1050 fn parameters_can_be_called_globally() -> Result<()> {
1051 let test_repo = GitTestRepo::default();
1052 let mut p = CliTester::new_from_dir(
1053 &test_repo.dir,
1054 [
1055 "--nsec",
1056 TEST_KEY_1_NSEC,
1057 "--password",
1058 TEST_PASSWORD,
1059 "login",
1060 "--offline",
1061 ],
1062 );
1063 p.expect("saved login details to local git config\r\n")?;
1064 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
1065 }
1066
1067 mod when_logging_in_as_different_nsec {
1068 use super::*;
1069
1070 #[test]
1071 fn valid_nsec_param_succeeds_without_prompts_and_logs_in() -> Result<()> {
1072 standard_first_time_login_encrypting_nsec()?.exit()?;
1073 let test_repo = GitTestRepo::default();
1074 let mut p = CliTester::new_from_dir(
1075 &test_repo.dir,
1076 [
1077 "login",
1078 "--offline",
1079 "--nsec",
1080 TEST_KEY_2_NSEC,
1081 "--password",
1082 TEST_PASSWORD,
1083 ],
1084 );
1085 p.expect("saved login details to local git config\r\n")?;
1086 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NPUB).as_str())
1087 }
1088 }
1089
1090 mod when_provided_with_new_password {
1091 use super::*;
1092
1093 #[test]
1094 fn password_changes() -> Result<()> {
1095 standard_first_time_login_encrypting_nsec()?.exit()?;
1096 let test_repo = GitTestRepo::default();
1097 let mut p = CliTester::new_from_dir(
1098 &test_repo.dir,
1099 [
1100 "login",
1101 "--offline",
1102 "--nsec",
1103 TEST_KEY_1_NSEC,
1104 "--password",
1105 TEST_INVALID_PASSWORD,
1106 ],
1107 );
1108 p.expect("saved login details to local git config\r\n")?;
1109 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())?;
1110
1111 CliTester::new_from_dir(
1112 &test_repo.dir,
1113 ["--password", TEST_INVALID_PASSWORD, "login", "--offline"],
1114 )
1115 .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
1116 }
1117 }
1118
1119 #[test]
1120 fn invalid_nsec_param_fails_without_prompts() -> Result<()> {
1121 let test_repo = GitTestRepo::default();
1122 let mut p = CliTester::new_from_dir(
1123 &test_repo.dir,
1124 [
1125 "login",
1126 "--offline",
1127 "--nsec",
1128 TEST_INVALID_NSEC,
1129 "--password",
1130 TEST_PASSWORD,
1131 ],
1132 );
1133 p.expect_end_with(
1134 "Error: invalid nsec parameter\r\n\r\nCaused by:\r\n Invalid secret key\r\n",
1135 )
1136 }
1137 }
1138
1139 mod when_weak_password {
1140 use super::*;
1141
1142 #[test]
1143 #[serial]
1144 // combined into a single test as it is computationally expensive to run
1145 fn warns_it_might_take_a_few_seconds_then_succeeds_then_second_login_prompts_for_password_then_warns_again_then_succeeds()
1146 -> Result<()> {
1147 let test_repo = GitTestRepo::default();
1148 let mut p =
1149 CliTester::new_with_timeout_from_dir(15000, &test_repo.dir, ["login", "--offline"]);
1150
1151 p.expect_input(EXPECTED_NSEC_PROMPT)?
1152 .succeeds_with(TEST_KEY_1_NSEC)?;
1153
1154 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
1155 .succeeds_with(Some(true))?;
1156
1157 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
1158 .succeeds_with(Some(true))?;
1159
1160 p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)?
1161 .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)?
1162 .succeeds_with(TEST_WEAK_PASSWORD)?;
1163
1164 p.expect("this may take a few seconds...\r\n")?;
1165
1166 p.expect("saved login details to local git config\r\n")?;
1167
1168 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
1169
1170 // commented out as 'login' command now assumes you want to
1171 // login as a new user
1172 // p = CliTester::new_with_timeout(10000, ["login",
1173 // "--offline"]);
1174
1175 // p.expect(format!("login as {}\r\n",
1176 // TEST_KEY_1_NPUB).as_str())?
1177 // .expect_password(EXPECTED_PASSWORD_PROMPT)?
1178 // .succeeds_with(TEST_WEAK_PASSWORD)?;
1179
1180 // p.expect("this may take a few seconds...\r\n")?;
1181
1182 // p.expect_end_with(format!("logged in as {}\r\n",
1183 // TEST_KEY_1_NPUB).as_str())
1184 }
1185 }
1186}
diff --git a/tests/ngit/main.rs b/tests/ngit/main.rs
deleted file mode 100644
index fe852df..0000000
--- a/tests/ngit/main.rs
+++ /dev/null
@@ -1,6 +0,0 @@
1mod init;
2mod list;
3mod login;
4mod pull;
5mod push;
6mod send;
diff --git a/tests/ngit/pull.rs b/tests/ngit/pull.rs
deleted file mode 100644
index 6637859..0000000
--- a/tests/ngit/pull.rs
+++ /dev/null
@@ -1,615 +0,0 @@
1use anyhow::Result;
2use futures::join;
3use serial_test::serial;
4use test_utils::{git::GitTestRepo, relay::Relay, *};
5
6mod when_main_is_checked_out {
7 use super::*;
8
9 mod cli_prompts {
10 use super::*;
11
12 #[tokio::test]
13 #[serial]
14 async fn cli_show_error() -> Result<()> {
15 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
16 Relay::new(8051, None, None),
17 Relay::new(8052, None, None),
18 Relay::new(8053, None, None),
19 Relay::new(8055, None, None),
20 Relay::new(8056, None, None),
21 );
22
23 r51.events.push(generate_test_key_1_relay_list_event());
24 r51.events.push(generate_test_key_1_metadata_event("fred"));
25 r51.events.push(generate_repo_ref_event());
26
27 r55.events.push(generate_repo_ref_event());
28 r55.events.push(generate_test_key_1_metadata_event("fred"));
29 r55.events.push(generate_test_key_1_relay_list_event());
30
31 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
32 cli_tester_create_proposals()?;
33
34 let test_repo = create_repo_with_proposal_branch_pulled_and_checkedout(1)?;
35
36 test_repo.checkout("main")?;
37
38 let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]);
39 p.expect("Error: checkout a branch associated with a proposal first\r\n")?;
40 p.expect_end()?;
41
42 for p in [51, 52, 53, 55, 56] {
43 relay::shutdown_relay(8000 + p)?;
44 }
45 Ok(())
46 });
47
48 // launch relay
49 let _ = join!(
50 r51.listen_until_close(),
51 r52.listen_until_close(),
52 r53.listen_until_close(),
53 r55.listen_until_close(),
54 r56.listen_until_close(),
55 );
56 cli_tester_handle.join().unwrap()?;
57 Ok(())
58 }
59 }
60}
61
62mod when_branch_doesnt_exist {
63 use super::*;
64
65 mod cli_prompts {
66 use super::*;
67
68 #[tokio::test]
69 #[serial]
70 async fn cli_show_error() -> Result<()> {
71 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
72 Relay::new(8051, None, None),
73 Relay::new(8052, None, None),
74 Relay::new(8053, None, None),
75 Relay::new(8055, None, None),
76 Relay::new(8056, None, None),
77 );
78
79 r51.events.push(generate_test_key_1_relay_list_event());
80 r51.events.push(generate_test_key_1_metadata_event("fred"));
81 r51.events.push(generate_repo_ref_event());
82
83 r55.events.push(generate_repo_ref_event());
84 r55.events.push(generate_test_key_1_metadata_event("fred"));
85 r55.events.push(generate_test_key_1_relay_list_event());
86
87 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
88 cli_tester_create_proposals()?;
89
90 let test_repo = GitTestRepo::default();
91 test_repo.populate()?;
92
93 test_repo.create_branch("random-name")?;
94 test_repo.checkout("random-name")?;
95
96 let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]);
97 p.expect("fetching updates...\r\n")?;
98 p.expect_eventually("\r\n")?; // some updates listed here
99 p.expect("Error: cannot find proposal that matches the current branch name\r\n")?;
100
101 p.expect_end()?;
102
103 for p in [51, 52, 53, 55, 56] {
104 relay::shutdown_relay(8000 + p)?;
105 }
106 Ok(())
107 });
108
109 // launch relay
110 let _ = join!(
111 r51.listen_until_close(),
112 r52.listen_until_close(),
113 r53.listen_until_close(),
114 r55.listen_until_close(),
115 r56.listen_until_close(),
116 );
117 cli_tester_handle.join().unwrap()?;
118 Ok(())
119 }
120 }
121}
122
123mod when_branch_is_checked_out {
124 use super::*;
125
126 mod when_branch_is_up_to_date {
127 use super::*;
128
129 mod cli_prompts {
130 use super::*;
131 #[tokio::test]
132 #[serial]
133 async fn cli_show_up_to_date() -> Result<()> {
134 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
135 Relay::new(8051, None, None),
136 Relay::new(8052, None, None),
137 Relay::new(8053, None, None),
138 Relay::new(8055, None, None),
139 Relay::new(8056, None, None),
140 );
141
142 r51.events.push(generate_test_key_1_relay_list_event());
143 r51.events.push(generate_test_key_1_metadata_event("fred"));
144 r51.events.push(generate_repo_ref_event());
145
146 r55.events.push(generate_repo_ref_event());
147 r55.events.push(generate_test_key_1_metadata_event("fred"));
148 r55.events.push(generate_test_key_1_relay_list_event());
149
150 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
151 let (_, test_repo) =
152 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
153
154 let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]);
155 p.expect("fetching updates...\r\n")?;
156 p.expect_eventually("\r\n")?; // some updates listed here
157 p.expect("branch already up-to-date\r\n")?;
158 p.expect_end()?;
159
160 for p in [51, 52, 53, 55, 56] {
161 relay::shutdown_relay(8000 + p)?;
162 }
163 Ok(())
164 });
165
166 // launch relay
167 let _ = join!(
168 r51.listen_until_close(),
169 r52.listen_until_close(),
170 r53.listen_until_close(),
171 r55.listen_until_close(),
172 r56.listen_until_close(),
173 );
174 cli_tester_handle.join().unwrap()?;
175 Ok(())
176 }
177 }
178 }
179
180 mod when_branch_is_behind {
181 use super::*;
182
183 async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> {
184 // fallback (51,52) user write (53, 55) repo (55, 56)
185 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
186 Relay::new(8051, None, None),
187 Relay::new(8052, None, None),
188 Relay::new(8053, None, None),
189 Relay::new(8055, None, None),
190 Relay::new(8056, None, None),
191 );
192
193 r51.events.push(generate_test_key_1_relay_list_event());
194 r51.events.push(generate_test_key_1_metadata_event("fred"));
195 r51.events.push(generate_repo_ref_event());
196
197 r55.events.push(generate_repo_ref_event());
198 r55.events.push(generate_test_key_1_metadata_event("fred"));
199 r55.events.push(generate_test_key_1_relay_list_event());
200
201 let cli_tester_handle =
202 std::thread::spawn(move || -> Result<(GitTestRepo, GitTestRepo)> {
203 let (originating_repo, test_repo) =
204 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
205
206 let branch_name =
207 remove_latest_commit_so_proposal_branch_is_behind_and_checkout_main(
208 &test_repo,
209 )?;
210 test_repo.checkout(&branch_name)?;
211
212 let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]);
213 p.expect_end_eventually()?;
214
215 for p in [51, 52, 53, 55, 56] {
216 relay::shutdown_relay(8000 + p)?;
217 }
218 Ok((originating_repo, test_repo))
219 });
220
221 // launch relay
222 let _ = join!(
223 r51.listen_until_close(),
224 r52.listen_until_close(),
225 r53.listen_until_close(),
226 r55.listen_until_close(),
227 r56.listen_until_close(),
228 );
229 let res = cli_tester_handle.join().unwrap()?;
230
231 Ok(res)
232 }
233
234 mod cli_prompts {
235 use super::*;
236
237 #[tokio::test]
238 #[serial]
239 async fn cli_applied_1_commit() -> Result<()> {
240 // fallback (51,52) user write (53, 55) repo (55, 56)
241 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
242 Relay::new(8051, None, None),
243 Relay::new(8052, None, None),
244 Relay::new(8053, None, None),
245 Relay::new(8055, None, None),
246 Relay::new(8056, None, None),
247 );
248
249 r51.events.push(generate_test_key_1_relay_list_event());
250 r51.events.push(generate_test_key_1_metadata_event("fred"));
251 r51.events.push(generate_repo_ref_event());
252
253 r55.events.push(generate_repo_ref_event());
254 r55.events.push(generate_test_key_1_metadata_event("fred"));
255 r55.events.push(generate_test_key_1_relay_list_event());
256
257 let cli_tester_handle =
258 std::thread::spawn(move || -> Result<(GitTestRepo, GitTestRepo)> {
259 let (originating_repo, test_repo) =
260 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
261
262 let branch_name =
263 remove_latest_commit_so_proposal_branch_is_behind_and_checkout_main(
264 &test_repo,
265 )?;
266 test_repo.checkout(&branch_name)?;
267
268 let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]);
269 p.expect("fetching updates...\r\n")?;
270 p.expect_eventually("\r\n")?; // some updates listed here
271 p.expect_end_with("applied 1 new commits\r\n")?;
272
273 for p in [51, 52, 53, 55, 56] {
274 relay::shutdown_relay(8000 + p)?;
275 }
276 Ok((originating_repo, test_repo))
277 });
278
279 // launch relay
280 let _ = join!(
281 r51.listen_until_close(),
282 r52.listen_until_close(),
283 r53.listen_until_close(),
284 r55.listen_until_close(),
285 r56.listen_until_close(),
286 );
287 cli_tester_handle.join().unwrap()?;
288
289 Ok(())
290 }
291 }
292
293 #[tokio::test]
294 #[serial]
295 async fn proposal_branch_tip_is_most_recent_patch() -> Result<()> {
296 let (originating_repo, test_repo) = prep_and_run().await?;
297 assert_eq!(
298 originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?,
299 test_repo.get_tip_of_local_branch(&get_proposal_branch_name(
300 &test_repo,
301 FEATURE_BRANCH_NAME_1
302 )?)?,
303 );
304 Ok(())
305 }
306 }
307
308 mod when_latest_proposal_amended_locally {
309 use super::*;
310
311 mod cli_prompts {
312 use super::*;
313
314 #[tokio::test]
315 #[serial]
316 async fn cli_output_correct() -> Result<()> {
317 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
318 Relay::new(8051, None, None),
319 Relay::new(8052, None, None),
320 Relay::new(8053, None, None),
321 Relay::new(8055, None, None),
322 Relay::new(8056, None, None),
323 );
324
325 r51.events.push(generate_test_key_1_relay_list_event());
326 r51.events.push(generate_test_key_1_metadata_event("fred"));
327 r51.events.push(generate_repo_ref_event());
328
329 r55.events.push(generate_repo_ref_event());
330 r55.events.push(generate_test_key_1_metadata_event("fred"));
331 r55.events.push(generate_test_key_1_relay_list_event());
332
333 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
334 let (_, test_repo) =
335 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
336
337 amend_last_commit(&test_repo, "add ammended-commit.md")?;
338
339 // run test
340 let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]);
341 p.expect("fetching updates...\r\n")?;
342 p.expect_eventually("\r\n")?; // some updates listed here
343 p.expect(
344 "you have an amended/rebase version the proposal that is unpublished\r\n",
345 )?;
346 p.expect("you have previously applied the latest version of the proposal (2 ahead 0 behind 'main') but your local proposal branch has amended or rebased it (2 ahead 0 behind 'main')\r\n")?;
347 p.expect("to view the latest proposal but retain your changes:\r\n")?;
348 p.expect(" 1) create a new branch off the tip commit of this one to store your changes\r\n")?;
349 p.expect(" 2) run `ngit list` and checkout the latest published version of this proposal\r\n")?;
350 p.expect("if you are confident in your changes consider running `ngit push --force`\r\n")?;
351 p.expect_end()?;
352
353 for p in [51, 52, 53, 55, 56] {
354 relay::shutdown_relay(8000 + p)?;
355 }
356 Ok(())
357 });
358
359 // launch relay
360 let _ = join!(
361 r51.listen_until_close(),
362 r52.listen_until_close(),
363 r53.listen_until_close(),
364 r55.listen_until_close(),
365 r56.listen_until_close(),
366 );
367 cli_tester_handle.join().unwrap()?;
368 println!("{:?}", r55.events);
369 Ok(())
370 }
371 }
372 }
373
374 mod when_local_commits_on_uptodate_proposal {
375 use super::*;
376 async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> {
377 // fallback (51,52) user write (53, 55) repo (55, 56)
378 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
379 Relay::new(8051, None, None),
380 Relay::new(8052, None, None),
381 Relay::new(8053, None, None),
382 Relay::new(8055, None, None),
383 Relay::new(8056, None, None),
384 );
385
386 r51.events.push(generate_test_key_1_relay_list_event());
387 r51.events.push(generate_test_key_1_metadata_event("fred"));
388 r51.events.push(generate_repo_ref_event());
389
390 r55.events.push(generate_repo_ref_event());
391 r55.events.push(generate_test_key_1_metadata_event("fred"));
392 r55.events.push(generate_test_key_1_relay_list_event());
393
394 let cli_tester_handle = std::thread::spawn(
395 move || -> Result<(GitTestRepo, GitTestRepo)> {
396 let (originating_repo, test_repo) =
397 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
398
399 // add another commit (so we have a local branch 1 ahead)
400 std::fs::write(test_repo.dir.join("ammended-commit.md"), "some content")?;
401 test_repo.stage_and_commit("add ammended-commit.md")?;
402
403 // run test
404 let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]);
405 p.expect("fetching updates...\r\n")?;
406 p.expect_eventually("\r\n")?; // some updates listed here
407 p.expect("local proposal branch exists with 1 unpublished commits on top of the most up-to-date version of the proposal\r\n")?;
408 p.expect_end()?;
409
410 for p in [51, 52, 53, 55, 56] {
411 relay::shutdown_relay(8000 + p)?;
412 }
413 Ok((originating_repo, test_repo))
414 },
415 );
416
417 // launch relay
418 let _ = join!(
419 r51.listen_until_close(),
420 r52.listen_until_close(),
421 r53.listen_until_close(),
422 r55.listen_until_close(),
423 r56.listen_until_close(),
424 );
425 let res = cli_tester_handle.join().unwrap()?;
426
427 Ok(res)
428 }
429
430 mod cli_prompts {
431 use super::*;
432
433 #[tokio::test]
434 #[serial]
435 async fn prompts_to_choose_from_proposal_titles() -> Result<()> {
436 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
437 Relay::new(8051, None, None),
438 Relay::new(8052, None, None),
439 Relay::new(8053, None, None),
440 Relay::new(8055, None, None),
441 Relay::new(8056, None, None),
442 );
443
444 r51.events.push(generate_test_key_1_relay_list_event());
445 r51.events.push(generate_test_key_1_metadata_event("fred"));
446 r51.events.push(generate_repo_ref_event());
447
448 r55.events.push(generate_repo_ref_event());
449 r55.events.push(generate_test_key_1_metadata_event("fred"));
450 r55.events.push(generate_test_key_1_relay_list_event());
451
452 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
453 let (_, test_repo) =
454 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
455
456 // add another commit (so we have a local branch 1 ahead)
457 std::fs::write(test_repo.dir.join("ammended-commit.md"), "some content")?;
458 test_repo.stage_and_commit("add ammended-commit.md")?;
459
460 // run test
461 let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]);
462 p.expect("fetching updates...\r\n")?;
463 p.expect_eventually("\r\n")?; // some updates listed here
464 p.expect("local proposal branch exists with 1 unpublished commits on top of the most up-to-date version of the proposal\r\n")?;
465 p.expect_end()?;
466
467 for p in [51, 52, 53, 55, 56] {
468 relay::shutdown_relay(8000 + p)?;
469 }
470 Ok(())
471 });
472
473 // launch relay
474 let _ = join!(
475 r51.listen_until_close(),
476 r52.listen_until_close(),
477 r53.listen_until_close(),
478 r55.listen_until_close(),
479 r56.listen_until_close(),
480 );
481 cli_tester_handle.join().unwrap()?;
482 println!("{:?}", r55.events);
483 Ok(())
484 }
485 }
486
487 #[tokio::test]
488 #[serial]
489 async fn didnt_overwrite_local_appendments() -> Result<()> {
490 let (originating_repo, test_repo) = prep_and_run().await?;
491 assert_ne!(
492 test_repo.get_tip_of_local_branch(&get_proposal_branch_name(
493 &test_repo,
494 FEATURE_BRANCH_NAME_1
495 )?)?,
496 originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?,
497 );
498 Ok(())
499 }
500 }
501 mod when_latest_event_rebases_branch {
502 use tokio::task::JoinHandle;
503
504 use super::*;
505
506 async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> {
507 // fallback (51,52) user write (53, 55) repo (55, 56)
508 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
509 Relay::new(8051, None, None),
510 Relay::new(8052, None, None),
511 Relay::new(8053, None, None),
512 Relay::new(8055, None, None),
513 Relay::new(8056, None, None),
514 );
515
516 r51.events.push(generate_test_key_1_relay_list_event());
517 r51.events.push(generate_test_key_1_metadata_event("fred"));
518 r51.events.push(generate_repo_ref_event());
519
520 r55.events.push(generate_repo_ref_event());
521 r55.events.push(generate_test_key_1_metadata_event("fred"));
522 r55.events.push(generate_test_key_1_relay_list_event());
523
524 let cli_tester_handle: JoinHandle<Result<(GitTestRepo, GitTestRepo)>> =
525 tokio::task::spawn_blocking(move || {
526 let (originating_repo, test_repo) = create_proposals_with_first_rebased_and_repo_with_latest_main_and_unrebased_proposal()?;
527
528 let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]);
529 p.expect_end_eventually_and_print()?;
530
531 for p in [51, 52, 53, 55, 56] {
532 relay::shutdown_relay(8000 + p)?;
533 }
534 Ok((originating_repo, test_repo))
535 });
536
537 // launch relay
538 let _ = join!(
539 r51.listen_until_close(),
540 r52.listen_until_close(),
541 r53.listen_until_close(),
542 r55.listen_until_close(),
543 r56.listen_until_close(),
544 );
545 let res = cli_tester_handle.await??;
546
547 Ok(res)
548 }
549
550 mod cli_prompts {
551 use super::*;
552
553 #[tokio::test]
554 #[serial]
555 async fn prompts_to_choose_from_proposal_titles() -> Result<()> {
556 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
557 Relay::new(8051, None, None),
558 Relay::new(8052, None, None),
559 Relay::new(8053, None, None),
560 Relay::new(8055, None, None),
561 Relay::new(8056, None, None),
562 );
563
564 r51.events.push(generate_test_key_1_relay_list_event());
565 r51.events.push(generate_test_key_1_metadata_event("fred"));
566 r51.events.push(generate_repo_ref_event());
567
568 r55.events.push(generate_repo_ref_event());
569 r55.events.push(generate_test_key_1_metadata_event("fred"));
570 r55.events.push(generate_test_key_1_relay_list_event());
571
572 let cli_tester_handle: JoinHandle<Result<()>> = tokio::task::spawn_blocking(
573 move || {
574 let (_, test_repo) = create_proposals_with_first_rebased_and_repo_with_latest_main_and_unrebased_proposal()?;
575
576 let mut p = CliTester::new_from_dir(&test_repo.dir, ["pull"]);
577 p.expect("fetching updates...\r\n")?;
578 p.expect_eventually("\r\n")?; // some updates listed here
579 p.expect_end_with("pulled new version of proposal (2 ahead 0 behind 'main'), replacing old version (2 ahead 1 behind 'main')\r\n")?;
580 for p in [51, 52, 53, 55, 56] {
581 relay::shutdown_relay(8000 + p)?;
582 }
583 Ok(())
584 },
585 );
586
587 // launch relay
588 let _ = join!(
589 r51.listen_until_close(),
590 r52.listen_until_close(),
591 r53.listen_until_close(),
592 r55.listen_until_close(),
593 r56.listen_until_close(),
594 );
595 cli_tester_handle.await??;
596 println!("{:?}", r55.events);
597 Ok(())
598 }
599 }
600
601 #[tokio::test]
602 #[serial]
603 async fn proposal_branch_tip_is_most_recent_proposal_revision_tip() -> Result<()> {
604 let (originating_repo, test_repo) = prep_and_run().await?;
605 assert_eq!(
606 originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?,
607 test_repo.get_tip_of_local_branch(&get_proposal_branch_name(
608 &test_repo,
609 FEATURE_BRANCH_NAME_1
610 )?)?,
611 );
612 Ok(())
613 }
614 }
615}
diff --git a/tests/ngit/push.rs b/tests/ngit/push.rs
deleted file mode 100644
index eb452cd..0000000
--- a/tests/ngit/push.rs
+++ /dev/null
@@ -1,531 +0,0 @@
1use anyhow::Result;
2use futures::join;
3use serial_test::serial;
4use test_utils::{git::GitTestRepo, relay::Relay, *};
5
6mod when_main_is_checked_out {
7 use super::*;
8
9 #[test]
10 fn cli_returns_error() -> Result<()> {
11 let test_repo = GitTestRepo::default();
12 test_repo.populate()?;
13 create_and_populate_branch(&test_repo, FEATURE_BRANCH_NAME_1, "a", false)?;
14 test_repo.checkout("main")?;
15 let mut p = CliTester::new_from_dir(&test_repo.dir, ["push"]);
16 p.expect("Error: checkout a branch associated with a proposal first\r\n")?;
17 p.expect_end()?;
18 Ok(())
19 }
20}
21
22mod when_proposal_isnt_associated_with_branch_name {
23 use super::*;
24
25 mod cli_prompts {
26
27 use super::*;
28
29 #[tokio::test]
30 #[serial]
31 async fn cli_show_error() -> Result<()> {
32 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
33 Relay::new(8051, None, None),
34 Relay::new(8052, None, None),
35 Relay::new(8053, None, None),
36 Relay::new(8055, None, None),
37 Relay::new(8056, None, None),
38 );
39
40 r51.events.push(generate_test_key_1_relay_list_event());
41 r51.events.push(generate_test_key_1_metadata_event("fred"));
42 r51.events.push(generate_repo_ref_event());
43
44 r55.events.push(generate_repo_ref_event());
45 r55.events.push(generate_test_key_1_metadata_event("fred"));
46 r55.events.push(generate_test_key_1_relay_list_event());
47
48 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
49 cli_tester_create_proposals()?;
50
51 let test_repo = GitTestRepo::default();
52 test_repo.populate()?;
53
54 test_repo.create_branch("random-name")?;
55 test_repo.checkout("random-name")?;
56
57 let mut p = CliTester::new_from_dir(&test_repo.dir, ["push"]);
58 p.expect("fetching updates...\r\n")?;
59 p.expect_eventually("\r\n")?; // some updates listed here
60 p.expect_end_with(
61 "Error: cannot find proposal that matches the current branch name\r\n",
62 )?;
63 for p in [51, 52, 53, 55, 56] {
64 relay::shutdown_relay(8000 + p)?;
65 }
66 Ok(())
67 });
68
69 // launch relay
70 let _ = join!(
71 r51.listen_until_close(),
72 r52.listen_until_close(),
73 r53.listen_until_close(),
74 r55.listen_until_close(),
75 r56.listen_until_close(),
76 );
77 cli_tester_handle.join().unwrap()?;
78 Ok(())
79 }
80 }
81}
82
83mod when_branch_is_checked_out {
84 use super::*;
85
86 mod when_branch_is_up_to_date {
87 use super::*;
88
89 mod cli_prompts {
90 use super::*;
91 #[tokio::test]
92 #[serial]
93 async fn cli_show_up_to_date() -> Result<()> {
94 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
95 Relay::new(8051, None, None),
96 Relay::new(8052, None, None),
97 Relay::new(8053, None, None),
98 Relay::new(8055, None, None),
99 Relay::new(8056, None, None),
100 );
101
102 r51.events.push(generate_test_key_1_relay_list_event());
103 r51.events.push(generate_test_key_1_metadata_event("fred"));
104 r51.events.push(generate_repo_ref_event());
105
106 r55.events.push(generate_repo_ref_event());
107 r55.events.push(generate_test_key_1_metadata_event("fred"));
108 r55.events.push(generate_test_key_1_relay_list_event());
109
110 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
111 let (_, test_repo) =
112 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
113
114 let mut p = CliTester::new_from_dir(&test_repo.dir, ["push"]);
115 p.expect("fetching updates...\r\n")?;
116 p.expect_eventually("\r\n")?; // some updates listed here
117 p.expect_end_with("Error: proposal already up-to-date with local branch\r\n")?;
118
119 for p in [51, 52, 53, 55, 56] {
120 relay::shutdown_relay(8000 + p)?;
121 }
122 Ok(())
123 });
124
125 // launch relay
126 let _ = join!(
127 r51.listen_until_close(),
128 r52.listen_until_close(),
129 r53.listen_until_close(),
130 r55.listen_until_close(),
131 r56.listen_until_close(),
132 );
133 cli_tester_handle.join().unwrap()?;
134 Ok(())
135 }
136 }
137 }
138
139 mod when_branch_is_behind {
140 use super::*;
141
142 mod cli_prompts {
143 use super::*;
144
145 #[tokio::test]
146 #[serial]
147 async fn cli_show_proposal_ahead_error() -> Result<()> {
148 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
149 Relay::new(8051, None, None),
150 Relay::new(8052, None, None),
151 Relay::new(8053, None, None),
152 Relay::new(8055, None, None),
153 Relay::new(8056, None, None),
154 );
155
156 r51.events.push(generate_test_key_1_relay_list_event());
157 r51.events.push(generate_test_key_1_metadata_event("fred"));
158 r51.events.push(generate_repo_ref_event());
159
160 r55.events.push(generate_repo_ref_event());
161 r55.events.push(generate_test_key_1_metadata_event("fred"));
162 r55.events.push(generate_test_key_1_relay_list_event());
163
164 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
165 let (_, test_repo) =
166 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
167
168 let branch_name =
169 remove_latest_commit_so_proposal_branch_is_behind_and_checkout_main(
170 &test_repo,
171 )?;
172
173 test_repo.checkout(&branch_name)?;
174 // run test
175 let mut p = CliTester::new_from_dir(&test_repo.dir, ["push"]);
176 p.expect("fetching updates...\r\n")?;
177 p.expect_eventually("\r\n")?; // some updates listed here
178 p.expect("Error: proposal is ahead of local branch\r\n")?;
179 p.expect_end()?;
180
181 for p in [51, 52, 53, 55, 56] {
182 relay::shutdown_relay(8000 + p)?;
183 }
184 Ok(())
185 });
186
187 // launch relay
188 let _ = join!(
189 r51.listen_until_close(),
190 r52.listen_until_close(),
191 r53.listen_until_close(),
192 r55.listen_until_close(),
193 r56.listen_until_close(),
194 );
195 cli_tester_handle.join().unwrap()?;
196 Ok(())
197 }
198 }
199 }
200
201 mod when_branch_is_ahead {
202 use super::*;
203
204 mod cli_prompts {
205 use test_utils::relay::expect_send_with_progress;
206
207 use super::*;
208
209 #[tokio::test]
210 #[serial]
211 async fn cli_applied_1_commit() -> Result<()> {
212 // fallback (51,52) user write (53, 55) repo (55, 56)
213 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
214 Relay::new(8051, None, None),
215 Relay::new(8052, None, None),
216 Relay::new(8053, None, None),
217 Relay::new(8055, None, None),
218 Relay::new(8056, None, None),
219 );
220
221 r51.events.push(generate_test_key_1_relay_list_event());
222 r51.events.push(generate_test_key_1_metadata_event("fred"));
223 r51.events.push(generate_repo_ref_event());
224
225 r55.events.push(generate_repo_ref_event());
226 r55.events.push(generate_test_key_1_metadata_event("fred"));
227 r55.events.push(generate_test_key_1_relay_list_event());
228
229 let cli_tester_handle =
230 std::thread::spawn(move || -> Result<(GitTestRepo, GitTestRepo)> {
231 let (originating_repo, test_repo) =
232 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
233
234 // add another commit (so we have an ammened local branch)
235 std::fs::write(test_repo.dir.join("ammended-commit.md"), "some content")?;
236 test_repo.stage_and_commit("add ammended-commit.md")?;
237
238 // run test
239 let mut p = CliTester::new_from_dir(
240 &test_repo.dir,
241 [
242 "--nsec",
243 TEST_KEY_1_NSEC,
244 "--password",
245 TEST_PASSWORD,
246 "--disable-cli-spinners",
247 "push",
248 ],
249 );
250 p.expect("fetching updates...\r\n")?;
251 p.expect_eventually("\r\n")?; // some updates listed here
252 p.expect(
253 "1 commits ahead. preparing to create creating patch events.\r\n",
254 )?;
255 p.expect("logged in as fred\r\n")?;
256 p.expect("pushing 1 commits\r\n")?;
257
258 expect_send_with_progress(
259 &mut p,
260 vec![
261 (" [my-relay] [repo-relay] ws://localhost:8055", true, ""),
262 (" [my-relay] ws://localhost:8053", true, ""),
263 (" [repo-relay] ws://localhost:8056", true, ""),
264 (" [default] ws://localhost:8051", true, ""),
265 (" [default] ws://localhost:8052", true, ""),
266 ],
267 1,
268 )?;
269 p.expect_eventually("pushed 1 commits\r\n")?;
270 p.expect_end()?;
271
272 for p in [51, 52, 53, 55, 56] {
273 relay::shutdown_relay(8000 + p)?;
274 }
275 Ok((originating_repo, test_repo))
276 });
277
278 // launch relay
279 let _ = join!(
280 r51.listen_until_close(),
281 r52.listen_until_close(),
282 r53.listen_until_close(),
283 r55.listen_until_close(),
284 r56.listen_until_close(),
285 );
286 cli_tester_handle.join().unwrap()?;
287
288 Ok(())
289 }
290 }
291
292 async fn prep_and_run() -> Result<(GitTestRepo, Vec<nostr::Event>)> {
293 // fallback (51,52) user write (53, 55) repo (55, 56)
294 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
295 Relay::new(8051, None, None),
296 Relay::new(8052, None, None),
297 Relay::new(8053, None, None),
298 Relay::new(8055, None, None),
299 Relay::new(8056, None, None),
300 );
301
302 r51.events.push(generate_test_key_1_relay_list_event());
303 r51.events.push(generate_test_key_1_metadata_event("fred"));
304 r51.events.push(generate_repo_ref_event());
305
306 r55.events.push(generate_repo_ref_event());
307 r55.events.push(generate_test_key_1_metadata_event("fred"));
308 r55.events.push(generate_test_key_1_relay_list_event());
309
310 let cli_tester_handle = std::thread::spawn(move || -> Result<GitTestRepo> {
311 let (_, test_repo) =
312 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
313
314 // add another commit (so we have an ammened local branch)
315 std::fs::write(test_repo.dir.join("ammended-commit.md"), "some content")?;
316 test_repo.stage_and_commit("add ammended-commit.md")?;
317
318 // run test
319
320 let mut p = CliTester::new_from_dir(
321 &test_repo.dir,
322 [
323 "--nsec",
324 TEST_KEY_1_NSEC,
325 "--password",
326 TEST_PASSWORD,
327 "--disable-cli-spinners",
328 "push",
329 ],
330 );
331 p.expect_end_eventually()?;
332
333 for p in [51, 52, 53, 55, 56] {
334 relay::shutdown_relay(8000 + p)?;
335 }
336 Ok(test_repo)
337 });
338
339 // launch relay
340 let _ = join!(
341 r51.listen_until_close(),
342 r52.listen_until_close(),
343 r53.listen_until_close(),
344 r55.listen_until_close(),
345 r56.listen_until_close(),
346 );
347 let res = cli_tester_handle.join().unwrap()?;
348
349 Ok((res, r55.events.clone()))
350 }
351 #[tokio::test]
352 #[serial]
353 async fn commits_issued_as_patch_event() -> Result<()> {
354 let (test_repo, r55_events) = prep_and_run().await?;
355
356 let commit_id = test_repo
357 .get_tip_of_local_branch(&test_repo.get_checked_out_branch_name()?)?
358 .to_string();
359 assert!(r55_events.iter().any(|e| {
360 e.tags
361 .iter()
362 .any(|t| t.as_vec()[0].eq("commit") && t.as_vec()[1].eq(&commit_id))
363 }));
364 Ok(())
365 }
366 }
367
368 mod when_branch_has_been_rebased {
369 use super::*;
370
371 mod cli_prompts {
372 use super::*;
373
374 #[tokio::test]
375 #[serial]
376 async fn cli_shows_unpublished_rebase_error() -> Result<()> {
377 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
378 Relay::new(8051, None, None),
379 Relay::new(8052, None, None),
380 Relay::new(8053, None, None),
381 Relay::new(8055, None, None),
382 Relay::new(8056, None, None),
383 );
384
385 r51.events.push(generate_test_key_1_relay_list_event());
386 r51.events.push(generate_test_key_1_metadata_event("fred"));
387 r51.events.push(generate_repo_ref_event());
388
389 r55.events.push(generate_repo_ref_event());
390 r55.events.push(generate_test_key_1_metadata_event("fred"));
391 r55.events.push(generate_test_key_1_relay_list_event());
392
393 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
394 let (_, tmp_repo) =
395 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
396 let branch_name = tmp_repo.get_checked_out_branch_name()?;
397
398 let test_repo = GitTestRepo::default();
399 test_repo.populate()?;
400
401 // simulate rebase
402 std::fs::write(test_repo.dir.join("amazing.md"), "some content")?;
403 test_repo.stage_and_commit("commit for rebasing on top of")?;
404 create_and_populate_branch(&test_repo, &branch_name, "a", true)?;
405
406 let mut p = CliTester::new_from_dir(&test_repo.dir, ["push"]);
407 // p.expect_end_eventually_and_print()?;
408
409 p.expect("fetching updates...\r\n")?;
410 p.expect_eventually("\r\n")?; // some updates listed here
411 p.expect(
412 "Error: local unpublished proposal has been rebased. consider force pushing\r\n",
413 )?;
414 p.expect_end()?;
415
416 for p in [51, 52, 53, 55, 56] {
417 relay::shutdown_relay(8000 + p)?;
418 }
419 Ok(())
420 });
421
422 // launch relay
423 let _ = join!(
424 r51.listen_until_close(),
425 r52.listen_until_close(),
426 r53.listen_until_close(),
427 r55.listen_until_close(),
428 r56.listen_until_close(),
429 );
430 cli_tester_handle.join().unwrap()?;
431 Ok(())
432 }
433 }
434 mod with_force_flag {
435 use super::*;
436
437 mod cli_prompts {
438 use super::*;
439
440 #[tokio::test]
441 #[serial]
442 async fn cli_shows_revision_sent() -> Result<()> {
443 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
444 Relay::new(8051, None, None),
445 Relay::new(8052, None, None),
446 Relay::new(8053, None, None),
447 Relay::new(8055, None, None),
448 Relay::new(8056, None, None),
449 );
450
451 r51.events.push(generate_test_key_1_relay_list_event());
452 r51.events.push(generate_test_key_1_metadata_event("fred"));
453 r51.events.push(generate_repo_ref_event());
454
455 r55.events.push(generate_repo_ref_event());
456 r55.events.push(generate_test_key_1_metadata_event("fred"));
457 r55.events.push(generate_test_key_1_relay_list_event());
458
459 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
460 let (_, tmp_repo) =
461 create_proposals_and_repo_with_proposal_pulled_and_checkedout(1)?;
462 let branch_name = tmp_repo.get_checked_out_branch_name()?;
463
464 let test_repo = GitTestRepo::default();
465 test_repo.populate()?;
466
467 // simulate rebase
468 std::fs::write(test_repo.dir.join("amazing.md"), "some content")?;
469 test_repo.stage_and_commit("commit for rebasing on top of")?;
470 create_and_populate_branch(&test_repo, &branch_name, "a", false)?;
471 let mut p = CliTester::new_from_dir(
472 &test_repo.dir,
473 [
474 "--nsec",
475 TEST_KEY_1_NSEC,
476 "--password",
477 TEST_PASSWORD,
478 "--disable-cli-spinners",
479 "push",
480 "--force",
481 ],
482 );
483 p.expect("fetching updates...\r\n")?;
484 p.expect_eventually("\r\n")?; // some updates listed here
485 p.expect("preparing to force push proposal revision...\r\n")?;
486 // standard output from `ngit send`
487 p.expect("creating proposal revision for: ")?;
488 // proposal id will be printed in this gap
489 p.expect_eventually("\r\n")?;
490 p.expect("creating proposal from 2 commits:\r\n")?;
491 p.expect("355bdf1 add a4.md\r\n")?;
492 p.expect("dbd1115 add a3.md\r\n")?;
493 p.expect("logged in as fred\r\n")?;
494 p.expect("posting 2 patches without a covering letter...\r\n")?;
495
496 relay::expect_send_with_progress(
497 &mut p,
498 vec![
499 (" [my-relay] [repo-relay] ws://localhost:8055", true, ""),
500 (" [my-relay] ws://localhost:8053", true, ""),
501 (" [repo-relay] ws://localhost:8056", true, ""),
502 (" [default] ws://localhost:8051", true, ""),
503 (" [default] ws://localhost:8052", true, ""),
504 ],
505 2,
506 )?;
507 // end standard `ngit send output`
508 p.expect_after_whitespace("force pushed proposal revision\r\n")?;
509 p.expect_end()?;
510
511 for p in [51, 52, 53, 55, 56] {
512 relay::shutdown_relay(8000 + p)?;
513 }
514 Ok(())
515 });
516
517 // launch relay
518 let _ = join!(
519 r51.listen_until_close(),
520 r52.listen_until_close(),
521 r53.listen_until_close(),
522 r55.listen_until_close(),
523 r56.listen_until_close(),
524 );
525 cli_tester_handle.join().unwrap()?;
526 Ok(())
527 }
528 }
529 }
530 }
531}
diff --git a/tests/ngit/send.rs b/tests/ngit/send.rs
deleted file mode 100644
index 2aad232..0000000
--- a/tests/ngit/send.rs
+++ /dev/null
@@ -1,1901 +0,0 @@
1use anyhow::Result;
2use futures::join;
3use nostr_sdk::Kind;
4use serial_test::serial;
5use test_utils::{git::GitTestRepo, relay::Relay, *};
6
7#[test]
8fn when_no_main_or_master_branch_return_error() -> Result<()> {
9 let test_repo = GitTestRepo::new("notmain")?;
10 test_repo.populate()?;
11 let mut p = CliTester::new_from_dir(&test_repo.dir, ["send"]);
12 p.expect("Error: the default branches (main or master) do not exist")?;
13 Ok(())
14}
15
16// TODO when commits ahead of origin/master - test ask to proceed
17// TODO when commits in origin/master - test ask to proceed
18mod when_commits_behind_ask_to_proceed {
19 use super::*;
20
21 fn prep_test_repo() -> Result<GitTestRepo> {
22 let test_repo = GitTestRepo::default();
23 test_repo.populate()?;
24 // create feature branch with 2 commit ahead
25 test_repo.create_branch("feature")?;
26 test_repo.checkout("feature")?;
27 std::fs::write(test_repo.dir.join("t3.md"), "some content")?;
28 test_repo.stage_and_commit("add t3.md")?;
29 std::fs::write(test_repo.dir.join("t4.md"), "some content")?;
30 test_repo.stage_and_commit("add t4.md")?;
31 // checkout main and add 1 commit
32 test_repo.checkout("main")?;
33 std::fs::write(test_repo.dir.join("t5.md"), "some content")?;
34 test_repo.stage_and_commit("add t5.md")?;
35 // checkout feature branch
36 test_repo.checkout("feature")?;
37 Ok(test_repo)
38 }
39
40 fn expect_confirm_prompt(p: &mut CliTester) -> Result<CliTesterConfirmPrompt> {
41 p.expect("fetching updates...\r\n")?;
42 p.expect_eventually("\r\n")?; // may be 'no updates' or some updates
43 p.expect("creating proposal from 2 commits:\r\n")?;
44 p.expect("fe973a8 add t4.md\r\n")?;
45 p.expect("232efb3 add t3.md\r\n")?;
46 p.expect_confirm(
47 "proposal is 1 behind 'main'. consider rebasing before submission. proceed anyway?",
48 Some(false),
49 )
50 }
51
52 #[test]
53 fn asked_with_default_no() -> Result<()> {
54 let test_repo = prep_test_repo()?;
55
56 let mut p = CliTester::new_from_dir(&test_repo.dir, ["send", "HEAD~2"]);
57 expect_confirm_prompt(&mut p)?;
58 p.exit()?;
59 Ok(())
60 }
61
62 #[test]
63 fn when_response_is_false_aborts() -> Result<()> {
64 let test_repo = prep_test_repo()?;
65
66 let mut p = CliTester::new_from_dir(&test_repo.dir, ["send", "HEAD~2"]);
67
68 expect_confirm_prompt(&mut p)?.succeeds_with(Some(false))?;
69
70 p.expect_end_with("Error: aborting so commits can be rebased\r\n")?;
71
72 Ok(())
73 }
74 #[test]
75 #[serial]
76 fn when_response_is_true_proceeds() -> Result<()> {
77 let test_repo = prep_test_repo()?;
78
79 let mut p = CliTester::new_from_dir(&test_repo.dir, ["send", "HEAD~2"]);
80 expect_confirm_prompt(&mut p)?.succeeds_with(Some(true))?;
81 p.expect("? include cover letter")?;
82 p.exit()?;
83 Ok(())
84 }
85}
86
87fn is_cover_letter(event: &nostr::Event) -> bool {
88 event.kind.eq(&Kind::GitPatch)
89 && event
90 .tags()
91 .iter()
92 .any(|t| t.as_vec()[1].eq("cover-letter"))
93}
94
95fn is_patch(event: &nostr::Event) -> bool {
96 event.kind.eq(&Kind::GitPatch)
97 && !event
98 .tags()
99 .iter()
100 .any(|t| t.as_vec()[1].eq("cover-letter"))
101}
102
103fn prep_git_repo() -> Result<GitTestRepo> {
104 let test_repo = GitTestRepo::default();
105 test_repo.populate()?;
106 // create feature branch with 2 commit ahead
107 test_repo.create_branch("feature")?;
108 test_repo.checkout("feature")?;
109 std::fs::write(test_repo.dir.join("t3.md"), "some content")?;
110 test_repo.stage_and_commit("add t3.md")?;
111 std::fs::write(test_repo.dir.join("t4.md"), "some content")?;
112 test_repo.stage_and_commit("add t4.md")?;
113 Ok(test_repo)
114}
115
116fn cli_tester_create_proposal(git_repo: &GitTestRepo, include_cover_letter: bool) -> CliTester {
117 let mut args = vec![
118 "--nsec",
119 TEST_KEY_1_NSEC,
120 "--password",
121 TEST_PASSWORD,
122 "--disable-cli-spinners",
123 "send",
124 "HEAD~2",
125 ];
126 if include_cover_letter {
127 for arg in [
128 "--title",
129 "exampletitle",
130 "--description",
131 "exampledescription",
132 ] {
133 args.push(arg);
134 }
135 } else {
136 args.push("--no-cover-letter");
137 }
138 CliTester::new_from_dir(&git_repo.dir, args)
139}
140
141fn expect_msgs_first(p: &mut CliTester, include_cover_letter: bool) -> Result<()> {
142 p.expect("fetching updates...\r\n")?;
143 p.expect_eventually("\r\n")?; // may be 'no updates' or some updates
144 p.expect("creating proposal from 2 commits:\r\n")?;
145 p.expect("fe973a8 add t4.md\r\n")?;
146 p.expect("232efb3 add t3.md\r\n")?;
147 // sometimes there will be a 'searching for profile...' msg
148 p.expect_eventually("logged in as fred\r\n")?;
149 p.expect(format!(
150 "posting 2 patches {} a covering letter...\r\n",
151 if include_cover_letter {
152 "with"
153 } else {
154 "without"
155 }
156 ))?;
157 Ok(())
158}
159
160fn expect_msgs_after(p: &mut CliTester) -> Result<()> {
161 p.expect_after_whitespace("view in gitworkshop.dev: https://gitworkshop.dev/repo")?;
162 p.expect_eventually("\r\n")?;
163 p.expect("view in another client: https://njump.me/")?;
164 p.expect_eventually("\r\n")?;
165 Ok(())
166}
167
168async fn prep_run_create_proposal(
169 include_cover_letter: bool,
170) -> Result<(
171 Relay<'static>,
172 Relay<'static>,
173 Relay<'static>,
174 Relay<'static>,
175 Relay<'static>,
176)> {
177 let git_repo = prep_git_repo()?;
178 // fallback (51,52) user write (53, 55) repo (55, 56)
179 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
180 Relay::new(
181 8051,
182 None,
183 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
184 relay.respond_events(
185 client_id,
186 &subscription_id,
187 &vec![
188 generate_test_key_1_metadata_event("fred"),
189 generate_test_key_1_relay_list_event(),
190 ],
191 )?;
192 Ok(())
193 }),
194 ),
195 Relay::new(8052, None, None),
196 Relay::new(8053, None, None),
197 Relay::new(
198 8055,
199 None,
200 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
201 relay.respond_events(
202 client_id,
203 &subscription_id,
204 &vec![generate_repo_ref_event()],
205 )?;
206 Ok(())
207 }),
208 ),
209 Relay::new(8056, None, None),
210 );
211
212 // // check relay had the right number of events
213 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
214 let mut p = cli_tester_create_proposal(&git_repo, include_cover_letter);
215 p.expect_end_eventually()?;
216 for p in [51, 52, 53, 55, 56] {
217 relay::shutdown_relay(8000 + p)?;
218 }
219 Ok(())
220 });
221
222 // launch relay
223 let _ = join!(
224 r51.listen_until_close(),
225 r52.listen_until_close(),
226 r53.listen_until_close(),
227 r55.listen_until_close(),
228 r56.listen_until_close(),
229 );
230 cli_tester_handle.join().unwrap()?;
231 Ok((r51, r52, r53, r55, r56))
232}
233
234mod when_cover_letter_details_specified_with_range_of_head_2_sends_cover_letter_and_2_patches_to_3_relays {
235
236 use super::*;
237 #[tokio::test]
238 #[serial]
239 async fn only_1_cover_letter_event_sent_to_each_relay() -> Result<()> {
240 let (_, _, r53, r55, r56) = prep_run_create_proposal(true).await?;
241 for relay in [&r53, &r55, &r56] {
242 assert_eq!(
243 relay.events.iter().filter(|e| is_cover_letter(e)).count(),
244 1,
245 );
246 }
247 Ok(())
248 }
249
250 #[tokio::test]
251 #[serial]
252 async fn only_1_cover_letter_event_sent_to_user_relays() -> Result<()> {
253 let (_, _, r53, r55, _) = prep_run_create_proposal(true).await?;
254 for relay in [&r53, &r55] {
255 assert_eq!(
256 relay.events.iter().filter(|e| is_cover_letter(e)).count(),
257 1,
258 );
259 }
260 Ok(())
261 }
262
263 #[tokio::test]
264 #[serial]
265 async fn only_1_cover_letter_event_sent_to_repo_relays() -> Result<()> {
266 let (_, _, _, r55, r56) = prep_run_create_proposal(true).await?;
267 for relay in [&r55, &r56] {
268 assert_eq!(
269 relay.events.iter().filter(|e| is_cover_letter(e)).count(),
270 1
271 );
272 }
273 Ok(())
274 }
275
276 #[tokio::test]
277 #[serial]
278 async fn only_1_cover_letter_event_sent_to_fallback_relays() -> Result<()> {
279 let (r51, r52, _, _, _) = prep_run_create_proposal(true).await?;
280 for relay in [&r51, &r52] {
281 assert_eq!(
282 relay.events.iter().filter(|e| is_cover_letter(e)).count(),
283 1,
284 );
285 }
286 Ok(())
287 }
288
289 #[tokio::test]
290 #[serial]
291 async fn only_2_patch_kind_events_sent_to_each_relay() -> Result<()> {
292 let (r51, r52, r53, r55, r56) = prep_run_create_proposal(true).await?;
293 for relay in [&r51, &r52, &r53, &r55, &r56] {
294 assert_eq!(relay.events.iter().filter(|e| is_patch(e)).count(), 2,);
295 }
296 Ok(())
297 }
298
299 #[tokio::test]
300 #[serial]
301 async fn patch_content_contains_patch_in_email_format_with_patch_series_numbers() -> Result<()>
302 {
303 let (_, _, r53, r55, r56) = prep_run_create_proposal(true).await?;
304 for relay in [&r53, &r55, &r56] {
305 let patch_events: Vec<&nostr::Event> =
306 relay.events.iter().filter(|e| is_patch(e)).collect();
307
308 assert_eq!(
309 patch_events[1].content,
310 "\
311 From fe973a840fba2a8ab37dd505c154854a69a6505c Mon Sep 17 00:00:00 2001\n\
312 From: Joe Bloggs <joe.bloggs@pm.me>\n\
313 Date: Thu, 1 Jan 1970 00:00:00 +0000\n\
314 Subject: [PATCH 2/2] add t4.md\n\
315 \n\
316 ---\n \
317 t4.md | 1 +\n \
318 1 file changed, 1 insertion(+)\n \
319 create mode 100644 t4.md\n\
320 \n\
321 diff --git a/t4.md b/t4.md\n\
322 new file mode 100644\n\
323 index 0000000..f0eec86\n\
324 --- /dev/null\n\
325 +++ b/t4.md\n\
326 @@ -0,0 +1 @@\n\
327 +some content\n\\ \
328 No newline at end of file\n\
329 --\n\
330 libgit2 1.8.1\n\
331 \n\
332 ",
333 );
334 assert_eq!(
335 patch_events[0].content,
336 "\
337 From 232efb37ebc67692c9e9ff58b83c0d3d63971a0a Mon Sep 17 00:00:00 2001\n\
338 From: Joe Bloggs <joe.bloggs@pm.me>\n\
339 Date: Thu, 1 Jan 1970 00:00:00 +0000\n\
340 Subject: [PATCH 1/2] add t3.md\n\
341 \n\
342 ---\n \
343 t3.md | 1 +\n \
344 1 file changed, 1 insertion(+)\n \
345 create mode 100644 t3.md\n\
346 \n\
347 diff --git a/t3.md b/t3.md\n\
348 new file mode 100644\n\
349 index 0000000..f0eec86\n\
350 --- /dev/null\n\
351 +++ b/t3.md\n\
352 @@ -0,0 +1 @@\n\
353 +some content\n\\ \
354 No newline at end of file\n\
355 --\n\
356 libgit2 1.8.1\n\
357 \n\
358 ",
359 );
360 }
361 Ok(())
362 }
363
364 mod cover_letter_tags {
365 use super::*;
366
367 #[tokio::test]
368 #[serial]
369 async fn root_commit_as_r() -> Result<()> {
370 let (_, _, r53, r55, r56) = prep_run_create_proposal(true).await?;
371 for relay in [&r53, &r55, &r56] {
372 let cover_letter_event: &nostr::Event =
373 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
374
375 assert_eq!(
376 cover_letter_event
377 .tags()
378 .iter()
379 .find(|t| t.as_vec()[0].eq("r"))
380 .unwrap()
381 .as_vec()[1],
382 "9ee507fc4357d7ee16a5d8901bedcd103f23c17d"
383 );
384 }
385 Ok(())
386 }
387
388 #[tokio::test]
389 #[serial]
390 async fn a_tag_for_repo_event_of_each_maintainer() -> Result<()> {
391 let (_, _, r53, r55, r56) = prep_run_create_proposal(true).await?;
392 for relay in [&r53, &r55, &r56] {
393 let cover_letter_event: &nostr::Event =
394 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
395 assert!(
396 cover_letter_event
397 .tags()
398 .iter()
399 .any(|t| t.as_vec()[0].eq("a")
400 && t.as_vec()[1].eq(&format!(
401 "{}:{TEST_KEY_1_PUBKEY_HEX}:{}",
402 Kind::GitRepoAnnouncement,
403 generate_repo_ref_event().identifier().unwrap()
404 )))
405 );
406 assert!(
407 cover_letter_event
408 .tags()
409 .iter()
410 .any(|t| t.as_vec()[0].eq("a")
411 && t.as_vec()[1].eq(&format!(
412 "{}:{TEST_KEY_2_PUBKEY_HEX}:{}",
413 Kind::GitRepoAnnouncement,
414 generate_repo_ref_event().identifier().unwrap()
415 )))
416 );
417 }
418 Ok(())
419 }
420
421 #[tokio::test]
422 #[serial]
423 async fn p_tags_for_maintainers() -> Result<()> {
424 let event = generate_repo_ref_event();
425 let maintainers = &event
426 .tags()
427 .iter()
428 .find(|t| t.as_vec()[0].eq(&"maintainers"))
429 .unwrap()
430 .as_vec()[1..];
431 let (_, _, r53, r55, r56) = prep_run_create_proposal(true).await?;
432 for relay in [&r53, &r55, &r56] {
433 for m in maintainers {
434 let cover_letter_event: &nostr::Event =
435 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
436 assert!(
437 cover_letter_event
438 .tags()
439 .iter()
440 .any(|t| { t.as_vec()[0].eq("p") && t.as_vec()[1].eq(m) })
441 );
442 }
443 }
444 Ok(())
445 }
446
447 #[tokio::test]
448 #[serial]
449 async fn t_tag_cover_letter() -> Result<()> {
450 let (_, _, r53, r55, r56) = prep_run_create_proposal(true).await?;
451 for relay in [&r53, &r55, &r56] {
452 let cover_letter_event: &nostr::Event =
453 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
454 assert!(
455 cover_letter_event
456 .tags()
457 .iter()
458 .any(|t| { t.as_vec()[0].eq("t") && t.as_vec()[1].eq(&"cover-letter") })
459 );
460 }
461 Ok(())
462 }
463
464 #[tokio::test]
465 #[serial]
466 async fn t_tag_root() -> Result<()> {
467 let (_, _, r53, r55, r56) = prep_run_create_proposal(true).await?;
468 for relay in [&r53, &r55, &r56] {
469 let cover_letter_event: &nostr::Event =
470 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
471 assert!(
472 cover_letter_event
473 .tags()
474 .iter()
475 .any(|t| { t.as_vec()[0].eq("t") && t.as_vec()[1].eq(&"root") })
476 );
477 }
478 Ok(())
479 }
480
481 #[tokio::test]
482 #[serial]
483 async fn cover_letter_tags_branch_name() -> Result<()> {
484 let (_, _, r53, r55, r56) = prep_run_create_proposal(true).await?;
485 for relay in [&r53, &r55, &r56] {
486 let cover_letter_event: &nostr::Event =
487 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
488
489 // branch-name tag
490 assert_eq!(
491 cover_letter_event
492 .tags()
493 .iter()
494 .find(|t| t.as_vec()[0].eq("branch-name"))
495 .unwrap()
496 .as_vec()[1],
497 "feature"
498 );
499 }
500 Ok(())
501 }
502
503 #[tokio::test]
504 #[serial]
505 async fn cover_letter_tags_alt() -> Result<()> {
506 let (_, _, r53, r55, r56) = prep_run_create_proposal(true).await?;
507 for relay in [&r53, &r55, &r56] {
508 let cover_letter_event: &nostr::Event =
509 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
510
511 // branch-name tag
512 assert_eq!(
513 cover_letter_event
514 .tags()
515 .iter()
516 .find(|t| t.as_vec()[0].eq("alt"))
517 .unwrap()
518 .as_vec()[1],
519 "git patch cover letter: exampletitle"
520 );
521 }
522 Ok(())
523 }
524 }
525
526 mod patch_tags {
527 use super::*;
528
529 async fn prep() -> Result<nostr::Event> {
530 let (_, _, r53, _, _) = prep_run_create_proposal(true).await?;
531 Ok(r53.events.iter().find(|e| is_patch(e)).unwrap().clone())
532 }
533
534 #[tokio::test]
535 #[serial]
536 async fn commit_and_commit_r() -> Result<()> {
537 static COMMIT_ID: &str = "232efb37ebc67692c9e9ff58b83c0d3d63971a0a";
538 let most_recent_patch = prep().await?;
539 assert!(
540 most_recent_patch
541 .tags
542 .iter()
543 .any(|t| t.as_vec()[0].eq("r") && t.as_vec()[1].eq(COMMIT_ID))
544 );
545 assert!(
546 most_recent_patch
547 .tags
548 .iter()
549 .any(|t| t.as_vec()[0].eq("commit") && t.as_vec()[1].eq(COMMIT_ID))
550 );
551 Ok(())
552 }
553
554 #[tokio::test]
555 #[serial]
556 async fn parent_commit() -> Result<()> {
557 // commit parent 'r' and 'parent-commit' tag
558 static COMMIT_PARENT_ID: &str = "431b84edc0d2fa118d63faa3c2db9c73d630a5ae";
559 let most_recent_patch = prep().await?;
560 assert_eq!(
561 most_recent_patch
562 .tags
563 .iter()
564 .find(|t| t.as_vec()[0].eq("parent-commit"))
565 .unwrap()
566 .as_vec()[1],
567 COMMIT_PARENT_ID,
568 );
569 Ok(())
570 }
571
572 #[tokio::test]
573 #[serial]
574 async fn root_commit_as_r() -> Result<()> {
575 assert!(prep().await?.tags.iter().any(|t| t.as_vec()[0].eq("r")
576 && t.as_vec()[1].eq("9ee507fc4357d7ee16a5d8901bedcd103f23c17d")));
577 Ok(())
578 }
579
580 #[tokio::test]
581 #[serial]
582 async fn p_tags_for_maintainers() -> Result<()> {
583 let event = generate_repo_ref_event();
584 let maintainers = &event
585 .tags()
586 .iter()
587 .find(|t| t.as_vec()[0].eq(&"maintainers"))
588 .unwrap()
589 .as_vec()[1..];
590 for m in maintainers {
591 assert!(
592 prep()
593 .await?
594 .tags()
595 .iter()
596 .any(|t| { t.as_vec()[0].eq("p") && t.as_vec()[1].eq(m) })
597 );
598 }
599 Ok(())
600 }
601
602 #[tokio::test]
603 #[serial]
604 async fn a_tag_for_repo_event_of_each_maintainer() -> Result<()> {
605 assert!(prep().await?.tags.iter().any(|t| {
606 t.as_vec()[0].eq("a")
607 && t.as_vec()[1].eq(&format!(
608 "{}:{TEST_KEY_1_PUBKEY_HEX}:{}",
609 Kind::GitRepoAnnouncement,
610 generate_repo_ref_event().identifier().unwrap()
611 ))
612 }));
613 assert!(prep().await?.tags.iter().any(|t| {
614 t.as_vec()[0].eq("a")
615 && t.as_vec()[1].eq(&format!(
616 "{}:{TEST_KEY_2_PUBKEY_HEX}:{}",
617 Kind::GitRepoAnnouncement,
618 generate_repo_ref_event().identifier().unwrap()
619 ))
620 }));
621 Ok(())
622 }
623
624 #[tokio::test]
625 #[serial]
626 async fn description_with_commit_message() -> Result<()> {
627 assert_eq!(
628 prep()
629 .await?
630 .tags
631 .iter()
632 .find(|t| t.as_vec()[0].eq("description"))
633 .unwrap()
634 .as_vec()[1],
635 "add t3.md"
636 );
637 Ok(())
638 }
639
640 #[tokio::test]
641 #[serial]
642 async fn commit_author() -> Result<()> {
643 assert_eq!(
644 prep()
645 .await?
646 .tags
647 .iter()
648 .find(|t| t.as_vec()[0].eq("author"))
649 .unwrap()
650 .as_vec(),
651 vec!["author", "Joe Bloggs", "joe.bloggs@pm.me", "0", "0"],
652 );
653 Ok(())
654 }
655
656 #[tokio::test]
657 #[serial]
658 async fn commit_committer() -> Result<()> {
659 assert_eq!(
660 prep()
661 .await?
662 .tags
663 .iter()
664 .find(|t| t.as_vec()[0].eq("committer"))
665 .unwrap()
666 .as_vec(),
667 vec!["committer", "Joe Bloggs", "joe.bloggs@pm.me", "0", "0"],
668 );
669 Ok(())
670 }
671
672 #[tokio::test]
673 #[serial]
674 async fn alt() -> Result<()> {
675 assert_eq!(
676 prep()
677 .await?
678 .tags
679 .iter()
680 .find(|t| t.as_vec()[0].eq("alt"))
681 .unwrap()
682 .as_vec(),
683 vec!["alt", "git patch: add t3.md"],
684 );
685 Ok(())
686 }
687
688 #[tokio::test]
689 #[serial]
690 async fn patch_tags_cover_letter_event_as_root() -> Result<()> {
691 let (_, _, r53, r55, r56) = prep_run_create_proposal(true).await?;
692 for relay in [&r53, &r55, &r56] {
693 let patch_events: Vec<&nostr::Event> =
694 relay.events.iter().filter(|e| is_patch(e)).collect();
695
696 let most_recent_patch = patch_events[0];
697 let cover_letter_event = relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
698
699 let root_event_tag = most_recent_patch
700 .tags
701 .iter()
702 .find(|t| {
703 t.as_vec()[0].eq("e") && t.as_vec().len().eq(&4) && t.as_vec()[3].eq("root")
704 })
705 .unwrap();
706
707 assert_eq!(
708 root_event_tag.as_vec()[1],
709 cover_letter_event.id.to_string()
710 );
711 }
712 Ok(())
713 }
714
715 #[tokio::test]
716 #[serial]
717 async fn second_patch_tags_first_with_reply() -> Result<()> {
718 let (_, _, r53, r55, r56) = prep_run_create_proposal(true).await?;
719 for relay in [&r53, &r55, &r56] {
720 let patch_events = relay
721 .events
722 .iter()
723 .filter(|e| is_patch(e))
724 .collect::<Vec<&nostr::Event>>();
725 assert_eq!(
726 patch_events[1]
727 .tags()
728 .iter()
729 .find(|t| t.as_vec()[0].eq("e")
730 && t.as_vec().len().eq(&4)
731 && t.as_vec()[3].eq("reply"))
732 .unwrap()
733 .as_vec()[1],
734 patch_events[0].id.to_string(),
735 );
736 }
737 Ok(())
738 }
739
740 #[tokio::test]
741 #[serial]
742 async fn no_t_root_tag() -> Result<()> {
743 assert!(
744 !prep()
745 .await?
746 .tags
747 .iter()
748 .any(|t| t.as_vec()[0].eq("t") && t.as_vec()[1].eq("root"))
749 );
750 Ok(())
751 }
752 }
753 mod cli_ouput {
754 use super::*;
755
756 #[tokio::test]
757 #[serial]
758 async fn check_cli_output() -> Result<()> {
759 let git_repo = prep_git_repo()?;
760
761 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
762 Relay::new(
763 8051,
764 None,
765 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
766 relay.respond_events(
767 client_id,
768 &subscription_id,
769 &vec![
770 generate_test_key_1_metadata_event("fred"),
771 generate_test_key_1_relay_list_event(),
772 ],
773 )?;
774 Ok(())
775 }),
776 ),
777 Relay::new(8052, None, None),
778 Relay::new(8053, None, None),
779 Relay::new(
780 8055,
781 None,
782 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
783 relay.respond_events(
784 client_id,
785 &subscription_id,
786 &vec![generate_repo_ref_event()],
787 )?;
788 Ok(())
789 }),
790 ),
791 Relay::new(8056, None, None),
792 );
793
794 // // check relay had the right number of events
795 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
796 let mut p = cli_tester_create_proposal(&git_repo, true);
797 expect_msgs_first(&mut p, true)?;
798 relay::expect_send_with_progress(
799 &mut p,
800 vec![
801 (" [my-relay] [repo-relay] ws://localhost:8055", true, ""),
802 (" [my-relay] ws://localhost:8053", true, ""),
803 (" [repo-relay] ws://localhost:8056", true, ""),
804 (" [default] ws://localhost:8051", true, ""),
805 (" [default] ws://localhost:8052", true, ""),
806 ],
807 3,
808 )?;
809 expect_msgs_after(&mut p)?;
810 p.expect_end_with_whitespace()?;
811 for p in [51, 52, 53, 55, 56] {
812 relay::shutdown_relay(8000 + p)?;
813 }
814 Ok(())
815 });
816
817 // launch relay
818 let _ = join!(
819 r51.listen_until_close(),
820 r52.listen_until_close(),
821 r53.listen_until_close(),
822 r55.listen_until_close(),
823 r56.listen_until_close(),
824 );
825 cli_tester_handle.join().unwrap()?;
826 Ok(())
827 }
828 }
829
830 mod first_event_rejected_by_1_relay {
831 use super::*;
832
833 mod only_first_rejected_event_sent_to_relay {
834 use super::*;
835
836 #[tokio::test]
837 #[serial]
838 async fn only_first_rejected_event_sent_to_relay() -> Result<()> {
839 let git_repo = prep_git_repo()?;
840
841 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
842 Relay::new(
843 8051,
844 None,
845 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
846 relay.respond_events(
847 client_id,
848 &subscription_id,
849 &vec![
850 generate_test_key_1_metadata_event("fred"),
851 generate_test_key_1_relay_list_event(),
852 ],
853 )?;
854 Ok(())
855 }),
856 ),
857 Relay::new(8052, None, None),
858 Relay::new(8053, None, None),
859 Relay::new(
860 8055,
861 None,
862 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
863 relay.respond_events(
864 client_id,
865 &subscription_id,
866 &vec![generate_repo_ref_event()],
867 )?;
868 Ok(())
869 }),
870 ),
871 Relay::new(
872 8056,
873 Some(&|relay, client_id, event| -> Result<()> {
874 relay.respond_ok(client_id, event, Some("Payment Required"))?;
875 Ok(())
876 }),
877 None,
878 ),
879 );
880
881 // // check relay had the right number of events
882 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
883 let mut p = cli_tester_create_proposal(&git_repo, true);
884 p.expect_end_eventually()?;
885 for p in [51, 52, 53, 55, 56] {
886 relay::shutdown_relay(8000 + p)?;
887 }
888 Ok(())
889 });
890
891 // launch relay
892 let _ = join!(
893 r51.listen_until_close(),
894 r52.listen_until_close(),
895 r53.listen_until_close(),
896 r55.listen_until_close(),
897 r56.listen_until_close(),
898 );
899 cli_tester_handle.join().unwrap()?;
900
901 assert_eq!(r56.events.len(), 1);
902
903 Ok(())
904 }
905 }
906
907 mod cli_show_rejection_with_comment {
908 use super::*;
909
910 #[tokio::test]
911 #[serial]
912 async fn cli_show_rejection_with_comment() -> Result<()> {
913 let git_repo = prep_git_repo()?;
914
915 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
916 Relay::new(
917 8051,
918 None,
919 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
920 relay.respond_events(
921 client_id,
922 &subscription_id,
923 &vec![
924 generate_test_key_1_metadata_event("fred"),
925 generate_test_key_1_relay_list_event(),
926 ],
927 )?;
928 Ok(())
929 }),
930 ),
931 Relay::new(8052, None, None),
932 Relay::new(8053, None, None),
933 Relay::new(
934 8055,
935 None,
936 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
937 relay.respond_events(
938 client_id,
939 &subscription_id,
940 &vec![generate_repo_ref_event()],
941 )?;
942 Ok(())
943 }),
944 ),
945 Relay::new(
946 8056,
947 Some(&|relay, client_id, event| -> Result<()> {
948 relay.respond_ok(client_id, event, Some("Payment Required"))?;
949 Ok(())
950 }),
951 None,
952 ),
953 );
954
955 // // check relay had the right number of events
956 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
957 let mut p = cli_tester_create_proposal(&git_repo, true);
958 expect_msgs_first(&mut p, true)?;
959 relay::expect_send_with_progress(
960 &mut p,
961 vec![
962 (" [my-relay] [repo-relay] ws://localhost:8055", true, ""),
963 (" [my-relay] ws://localhost:8053", true, ""),
964 (
965 " [repo-relay] ws://localhost:8056",
966 false,
967 "error: Payment Required",
968 ),
969 (" [default] ws://localhost:8051", true, ""),
970 (" [default] ws://localhost:8052", true, ""),
971 ],
972 3,
973 )?;
974 expect_msgs_after(&mut p)?;
975 p.expect_end_with_whitespace()?;
976 for p in [51, 52, 53, 55, 56] {
977 relay::shutdown_relay(8000 + p)?;
978 }
979
980 Ok(())
981 });
982
983 // launch relay
984 let _ = join!(
985 r51.listen_until_close(),
986 r52.listen_until_close(),
987 r53.listen_until_close(),
988 r55.listen_until_close(),
989 r56.listen_until_close(),
990 );
991 cli_tester_handle.join().unwrap()?;
992 Ok(())
993 }
994 }
995 }
996}
997
998mod when_no_cover_letter_flag_set_with_range_of_head_2_sends_2_patches_without_cover_letter {
999 use super::*;
1000
1001 mod cli_ouput {
1002 use super::*;
1003
1004 #[tokio::test]
1005 #[serial]
1006 async fn check_cli_output() -> Result<()> {
1007 let git_repo = prep_git_repo()?;
1008
1009 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
1010 Relay::new(
1011 8051,
1012 None,
1013 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1014 relay.respond_events(
1015 client_id,
1016 &subscription_id,
1017 &vec![
1018 generate_test_key_1_metadata_event("fred"),
1019 generate_test_key_1_relay_list_event(),
1020 ],
1021 )?;
1022 Ok(())
1023 }),
1024 ),
1025 Relay::new(8052, None, None),
1026 Relay::new(8053, None, None),
1027 Relay::new(
1028 8055,
1029 None,
1030 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1031 relay.respond_events(
1032 client_id,
1033 &subscription_id,
1034 &vec![generate_repo_ref_event()],
1035 )?;
1036 Ok(())
1037 }),
1038 ),
1039 Relay::new(8056, None, None),
1040 );
1041
1042 // // check relay had the right number of events
1043 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1044 let mut p = cli_tester_create_proposal(&git_repo, false);
1045
1046 expect_msgs_first(&mut p, false)?;
1047 relay::expect_send_with_progress(
1048 &mut p,
1049 vec![
1050 (" [my-relay] [repo-relay] ws://localhost:8055", true, ""),
1051 (" [my-relay] ws://localhost:8053", true, ""),
1052 (" [repo-relay] ws://localhost:8056", true, ""),
1053 (" [default] ws://localhost:8051", true, ""),
1054 (" [default] ws://localhost:8052", true, ""),
1055 ],
1056 2,
1057 )?;
1058 expect_msgs_after(&mut p)?;
1059 p.expect_end_with_whitespace()?;
1060 for p in [51, 52, 53, 55, 56] {
1061 relay::shutdown_relay(8000 + p)?;
1062 }
1063 Ok(())
1064 });
1065
1066 // launch relay
1067 let _ = join!(
1068 r51.listen_until_close(),
1069 r52.listen_until_close(),
1070 r53.listen_until_close(),
1071 r55.listen_until_close(),
1072 r56.listen_until_close(),
1073 );
1074 cli_tester_handle.join().unwrap()?;
1075 Ok(())
1076 }
1077 }
1078
1079 #[tokio::test]
1080 #[serial]
1081 async fn no_cover_letter_event() -> Result<()> {
1082 let (_, _, r53, r55, r56) = prep_run_create_proposal(false).await?;
1083 for relay in [&r53, &r55, &r56] {
1084 assert_eq!(
1085 relay.events.iter().filter(|e| is_cover_letter(e)).count(),
1086 0,
1087 );
1088 }
1089 Ok(())
1090 }
1091
1092 #[tokio::test]
1093 #[serial]
1094 async fn two_patch_events() -> Result<()> {
1095 let (_, _, r53, r55, r56) = prep_run_create_proposal(false).await?;
1096 for relay in [&r53, &r55, &r56] {
1097 assert_eq!(relay.events.iter().filter(|e| is_patch(e)).count(), 2);
1098 }
1099 Ok(())
1100 }
1101
1102 #[tokio::test]
1103 #[serial]
1104 // TODO check this is the ancestor
1105 async fn first_patch_with_root_t_tag() -> Result<()> {
1106 let (_, _, r53, r55, r56) = prep_run_create_proposal(false).await?;
1107 for relay in [&r53, &r55, &r56] {
1108 let patch_events = relay
1109 .events
1110 .iter()
1111 .filter(|e| is_patch(e))
1112 .collect::<Vec<&nostr::Event>>();
1113
1114 // first patch tagged as root
1115 assert!(
1116 patch_events[0]
1117 .tags()
1118 .iter()
1119 .any(|t| t.as_vec()[0].eq("t") && t.as_vec()[1].eq("root"))
1120 );
1121 // second patch not tagged as root
1122 assert!(
1123 !patch_events[1]
1124 .tags()
1125 .iter()
1126 .any(|t| t.as_vec()[0].eq("t") && t.as_vec()[1].eq("root"))
1127 );
1128 }
1129 Ok(())
1130 }
1131
1132 #[tokio::test]
1133 #[serial]
1134 async fn root_patch_tags_branch_name() -> Result<()> {
1135 let (_, _, r53, r55, r56) = prep_run_create_proposal(false).await?;
1136 for relay in [&r53, &r55, &r56] {
1137 let patch_events = relay
1138 .events
1139 .iter()
1140 .filter(|e| is_patch(e))
1141 .collect::<Vec<&nostr::Event>>();
1142
1143 // branch-name tag
1144 assert_eq!(
1145 patch_events[0]
1146 .tags()
1147 .iter()
1148 .find(|t| t.as_vec()[0].eq("branch-name"))
1149 .unwrap()
1150 .as_vec()[1],
1151 "feature"
1152 );
1153 }
1154 Ok(())
1155 }
1156
1157 #[tokio::test]
1158 #[serial]
1159 async fn second_patch_lists_first_as_root() -> Result<()> {
1160 let (_, _, r53, r55, r56) = prep_run_create_proposal(false).await?;
1161 for relay in [&r53, &r55, &r56] {
1162 let patch_events = relay
1163 .events
1164 .iter()
1165 .filter(|e| is_patch(e))
1166 .collect::<Vec<&nostr::Event>>();
1167
1168 assert_eq!(
1169 patch_events[1]
1170 .tags()
1171 .iter()
1172 .find(|t| t.as_vec()[0].eq("e")
1173 && t.as_vec().len().eq(&4)
1174 && t.as_vec()[3].eq("root"))
1175 .unwrap()
1176 .as_vec()[1],
1177 patch_events[0].id.to_string(),
1178 );
1179 }
1180 Ok(())
1181 }
1182}
1183
1184mod when_range_ommited_prompts_for_selection_defaulting_ahead_of_main {
1185 use super::*;
1186
1187 fn cli_tester_create_proposal(git_repo: &GitTestRepo) -> CliTester {
1188 let args = vec![
1189 "--nsec",
1190 TEST_KEY_1_NSEC,
1191 "--password",
1192 TEST_PASSWORD,
1193 "--disable-cli-spinners",
1194 "send",
1195 "--no-cover-letter",
1196 ];
1197 CliTester::new_from_dir(&git_repo.dir, args)
1198 }
1199 fn expect_msgs_first(p: &mut CliTester) -> Result<()> {
1200 p.expect("fetching updates...\r\n")?;
1201 p.expect_eventually("\r\n")?; // may be 'no updates' or some updates
1202 let mut selector = p.expect_multi_select(
1203 "select commits for proposal",
1204 vec![
1205 "(Joe Bloggs) add t4.md [feature] fe973a8".to_string(),
1206 "(Joe Bloggs) add t3.md 232efb3".to_string(),
1207 "(Joe Bloggs) add t2.md [main] 431b84e".to_string(),
1208 "(Joe Bloggs) add t1.md af474d8".to_string(),
1209 "(Joe Bloggs) Initial commit 9ee507f".to_string(),
1210 ],
1211 )?;
1212 selector.succeeds_with(vec![0, 1], false, vec![0, 1])?;
1213 p.expect("creating proposal from 2 commits:\r\n")?;
1214 p.expect("fe973a8 add t4.md\r\n")?;
1215 p.expect("232efb3 add t3.md\r\n")?;
1216 p.expect("searching for profile...\r\n")?;
1217 p.expect("logged in as fred\r\n")?;
1218 p.expect("posting 2 patches without a covering letter...\r\n")?;
1219 Ok(())
1220 }
1221 async fn prep_run_create_proposal() -> Result<(
1222 Relay<'static>,
1223 Relay<'static>,
1224 Relay<'static>,
1225 Relay<'static>,
1226 Relay<'static>,
1227 )> {
1228 let git_repo = prep_git_repo()?;
1229
1230 // fallback (51,52) user write (53, 55) repo (55, 56)
1231 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
1232 Relay::new(
1233 8051,
1234 None,
1235 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1236 relay.respond_events(
1237 client_id,
1238 &subscription_id,
1239 &vec![
1240 generate_test_key_1_metadata_event("fred"),
1241 generate_test_key_1_relay_list_event(),
1242 ],
1243 )?;
1244 Ok(())
1245 }),
1246 ),
1247 Relay::new(8052, None, None),
1248 Relay::new(8053, None, None),
1249 Relay::new(
1250 8055,
1251 None,
1252 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1253 relay.respond_events(
1254 client_id,
1255 &subscription_id,
1256 &vec![generate_repo_ref_event()],
1257 )?;
1258 Ok(())
1259 }),
1260 ),
1261 Relay::new(8056, None, None),
1262 );
1263
1264 // // check relay had the right number of events
1265 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1266 let mut p = cli_tester_create_proposal(&git_repo);
1267 expect_msgs_first(&mut p)?;
1268 p.expect_end_eventually()?;
1269 for p in [51, 52, 53, 55, 56] {
1270 relay::shutdown_relay(8000 + p)?;
1271 }
1272 Ok(())
1273 });
1274
1275 // launch relay
1276 let _ = join!(
1277 r51.listen_until_close(),
1278 r52.listen_until_close(),
1279 r53.listen_until_close(),
1280 r55.listen_until_close(),
1281 r56.listen_until_close(),
1282 );
1283 cli_tester_handle.join().unwrap()?;
1284 Ok((r51, r52, r53, r55, r56))
1285 }
1286 mod cli_ouput {
1287 use super::*;
1288
1289 #[tokio::test]
1290 #[serial]
1291 async fn check_cli_output() -> Result<()> {
1292 let git_repo = prep_git_repo()?;
1293
1294 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
1295 Relay::new(
1296 8051,
1297 None,
1298 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1299 relay.respond_events(
1300 client_id,
1301 &subscription_id,
1302 &vec![
1303 generate_test_key_1_metadata_event("fred"),
1304 generate_test_key_1_relay_list_event(),
1305 ],
1306 )?;
1307 Ok(())
1308 }),
1309 ),
1310 Relay::new(8052, None, None),
1311 Relay::new(8053, None, None),
1312 Relay::new(
1313 8055,
1314 None,
1315 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1316 relay.respond_events(
1317 client_id,
1318 &subscription_id,
1319 &vec![generate_repo_ref_event()],
1320 )?;
1321 Ok(())
1322 }),
1323 ),
1324 Relay::new(8056, None, None),
1325 );
1326
1327 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1328 let mut p = cli_tester_create_proposal(&git_repo);
1329
1330 expect_msgs_first(&mut p)?;
1331 relay::expect_send_with_progress(
1332 &mut p,
1333 vec![
1334 (" [my-relay] [repo-relay] ws://localhost:8055", true, ""),
1335 (" [my-relay] ws://localhost:8053", true, ""),
1336 (" [repo-relay] ws://localhost:8056", true, ""),
1337 (" [default] ws://localhost:8051", true, ""),
1338 (" [default] ws://localhost:8052", true, ""),
1339 ],
1340 2,
1341 )?;
1342 expect_msgs_after(&mut p)?;
1343 p.expect_end_with_whitespace()?;
1344 for p in [51, 52, 53, 55, 56] {
1345 relay::shutdown_relay(8000 + p)?;
1346 }
1347 Ok(())
1348 });
1349
1350 // launch relay
1351 let _ = join!(
1352 r51.listen_until_close(),
1353 r52.listen_until_close(),
1354 r53.listen_until_close(),
1355 r55.listen_until_close(),
1356 r56.listen_until_close(),
1357 );
1358 cli_tester_handle.join().unwrap()?;
1359 Ok(())
1360 }
1361 }
1362
1363 #[tokio::test]
1364 #[serial]
1365 async fn two_patch_events_sent() -> Result<()> {
1366 let (_, _, r53, r55, r56) = prep_run_create_proposal().await?;
1367 for relay in [&r53, &r55, &r56] {
1368 assert_eq!(relay.events.iter().filter(|e| is_patch(e)).count(), 2);
1369 }
1370 Ok(())
1371 }
1372}
1373
1374mod root_proposal_specified_using_in_reply_to_with_range_of_head_2_and_cover_letter_details_specified {
1375
1376 use nostr::ToBech32;
1377
1378 use super::*;
1379
1380 fn cli_tester_create_proposal(git_repo: &GitTestRepo) -> CliTester {
1381 let proposal_root_bech32 = get_pretend_proposal_root_event().id.to_bech32().unwrap();
1382 let args = vec![
1383 "--nsec",
1384 TEST_KEY_1_NSEC,
1385 "--password",
1386 TEST_PASSWORD,
1387 "--disable-cli-spinners",
1388 "send",
1389 "HEAD~2",
1390 "--in-reply-to",
1391 &proposal_root_bech32,
1392 "--title",
1393 "exampletitle",
1394 "--description",
1395 "exampledescription",
1396 ];
1397 CliTester::new_from_dir(&git_repo.dir, args)
1398 }
1399 fn expect_msgs_first(p: &mut CliTester, include_cover_letter: bool) -> Result<()> {
1400 p.expect("fetching updates...\r\n")?;
1401 p.expect("updates: 1 new maintainer, 1 announcement update, 1 proposal\r\n")?;
1402 let proposal_root_bech32 = get_pretend_proposal_root_event().id.to_bech32().unwrap();
1403 p.expect(format!(
1404 "creating proposal revision for: {}\r\n",
1405 proposal_root_bech32,
1406 ))?;
1407 p.expect("creating proposal from 2 commits:\r\n")?;
1408 p.expect("fe973a8 add t4.md\r\n")?;
1409 p.expect("232efb3 add t3.md\r\n")?;
1410 p.expect("logged in as fred\r\n")?;
1411 p.expect(format!(
1412 "posting 2 patches {} a covering letter...\r\n",
1413 if include_cover_letter {
1414 "with"
1415 } else {
1416 "without"
1417 }
1418 ))?;
1419 Ok(())
1420 }
1421
1422 async fn prep_run_create_proposal() -> Result<(
1423 Relay<'static>,
1424 Relay<'static>,
1425 Relay<'static>,
1426 Relay<'static>,
1427 Relay<'static>,
1428 )> {
1429 let git_repo = prep_git_repo()?;
1430 // fallback (51,52) user write (53, 55) repo (55, 56)
1431 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
1432 Relay::new(
1433 8051,
1434 None,
1435 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1436 relay.respond_events(
1437 client_id,
1438 &subscription_id,
1439 &vec![
1440 generate_test_key_1_metadata_event("fred"),
1441 generate_test_key_1_relay_list_event(),
1442 get_pretend_proposal_root_event(),
1443 ],
1444 )?;
1445 Ok(())
1446 }),
1447 ),
1448 Relay::new(8052, None, None),
1449 Relay::new(8053, None, None),
1450 Relay::new(
1451 8055,
1452 None,
1453 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1454 relay.respond_events(
1455 client_id,
1456 &subscription_id,
1457 &vec![generate_repo_ref_event(), get_pretend_proposal_root_event()],
1458 )?;
1459 Ok(())
1460 }),
1461 ),
1462 Relay::new(8056, None, None),
1463 );
1464
1465 // // check relay had the right number of events
1466 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1467 let mut p = cli_tester_create_proposal(&git_repo);
1468 p.expect_end_eventually()?;
1469 for p in [51, 52, 53, 55, 56] {
1470 relay::shutdown_relay(8000 + p)?;
1471 }
1472 Ok(())
1473 });
1474
1475 // launch relay
1476 let _ = join!(
1477 r51.listen_until_close(),
1478 r52.listen_until_close(),
1479 r53.listen_until_close(),
1480 r55.listen_until_close(),
1481 r56.listen_until_close(),
1482 );
1483 cli_tester_handle.join().unwrap()?;
1484 Ok((r51, r52, r53, r55, r56))
1485 }
1486 mod cli_ouput {
1487 use super::*;
1488
1489 #[tokio::test]
1490 #[serial]
1491 async fn check_cli_output() -> Result<()> {
1492 let git_repo = prep_git_repo()?;
1493
1494 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
1495 Relay::new(
1496 8051,
1497 None,
1498 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1499 relay.respond_events(
1500 client_id,
1501 &subscription_id,
1502 &vec![
1503 generate_test_key_1_metadata_event("fred"),
1504 generate_test_key_1_relay_list_event(),
1505 get_pretend_proposal_root_event(),
1506 ],
1507 )?;
1508 Ok(())
1509 }),
1510 ),
1511 Relay::new(8052, None, None),
1512 Relay::new(8053, None, None),
1513 Relay::new(
1514 8055,
1515 None,
1516 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1517 relay.respond_events(
1518 client_id,
1519 &subscription_id,
1520 &vec![generate_repo_ref_event(), get_pretend_proposal_root_event()],
1521 )?;
1522 Ok(())
1523 }),
1524 ),
1525 Relay::new(8056, None, None),
1526 );
1527
1528 // // check relay had the right number of events
1529 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1530 let mut p = cli_tester_create_proposal(&git_repo);
1531 expect_msgs_first(&mut p, true)?;
1532 relay::expect_send_with_progress(
1533 &mut p,
1534 vec![
1535 (" [my-relay] [repo-relay] ws://localhost:8055", true, ""),
1536 (" [my-relay] ws://localhost:8053", true, ""),
1537 (" [repo-relay] ws://localhost:8056", true, ""),
1538 (" [default] ws://localhost:8051", true, ""),
1539 (" [default] ws://localhost:8052", true, ""),
1540 ],
1541 3,
1542 )?;
1543 p.expect_end_with_whitespace()?;
1544 for p in [51, 52, 53, 55, 56] {
1545 relay::shutdown_relay(8000 + p)?;
1546 }
1547 Ok(())
1548 });
1549
1550 // launch relay
1551 let _ = join!(
1552 r51.listen_until_close(),
1553 r52.listen_until_close(),
1554 r53.listen_until_close(),
1555 r55.listen_until_close(),
1556 r56.listen_until_close(),
1557 );
1558 cli_tester_handle.join().unwrap()?;
1559 Ok(())
1560 }
1561 }
1562
1563 mod cover_letter_tags {
1564 use super::*;
1565
1566 #[tokio::test]
1567 #[serial]
1568 async fn t_tag_root() -> Result<()> {
1569 let (_, _, r53, r55, r56) = prep_run_create_proposal().await?;
1570 for relay in [&r53, &r55, &r56] {
1571 let cover_letter_event: &nostr::Event =
1572 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
1573 assert!(
1574 cover_letter_event
1575 .tags()
1576 .iter()
1577 .any(|t| { t.as_vec()[0].eq("t") && t.as_vec()[1].eq(&"root") })
1578 );
1579 }
1580 Ok(())
1581 }
1582
1583 #[tokio::test]
1584 #[serial]
1585 async fn t_tag_revision_root() -> Result<()> {
1586 let (_, _, r53, r55, r56) = prep_run_create_proposal().await?;
1587 for relay in [&r53, &r55, &r56] {
1588 let cover_letter_event: &nostr::Event =
1589 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
1590 assert!(
1591 cover_letter_event
1592 .tags()
1593 .iter()
1594 .any(|t| { t.as_vec()[0].eq("t") && t.as_vec()[1].eq(&"revision-root") })
1595 );
1596 }
1597 Ok(())
1598 }
1599
1600 #[tokio::test]
1601 #[serial]
1602 async fn e_tag_in_reply_to_event_as_reply() -> Result<()> {
1603 let (_, _, r53, r55, r56) = prep_run_create_proposal().await?;
1604 for relay in [&r53, &r55, &r56] {
1605 let cover_letter_event: &nostr::Event =
1606 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
1607 assert_eq!(
1608 cover_letter_event
1609 .tags()
1610 .iter()
1611 .find(|t| {
1612 t.as_vec()[0].eq("e")
1613 && t.as_vec().len().eq(&4)
1614 && t.as_vec()[3].eq("reply")
1615 })
1616 .unwrap()
1617 .as_vec()[1],
1618 // id of state nevent
1619 "431e58eb8e1b4e20292d1d5bbe81d5cfb042e1bc165de32eddfdd52245a4cce4",
1620 );
1621 }
1622 Ok(())
1623 }
1624 }
1625
1626 #[tokio::test]
1627 #[serial]
1628 async fn patch_tags_cover_letter_event_as_root() -> Result<()> {
1629 let (_, _, r53, r55, r56) = prep_run_create_proposal().await?;
1630 for relay in [&r53, &r55, &r56] {
1631 let patch_events: Vec<&nostr::Event> =
1632 relay.events.iter().filter(|e| is_patch(e)).collect();
1633
1634 let cover_letter_event = relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
1635
1636 for patch in patch_events {
1637 assert_eq!(
1638 patch
1639 .tags
1640 .iter()
1641 .find(|t| {
1642 t.as_vec()[0].eq("e")
1643 && t.as_vec().len().eq(&4)
1644 && t.as_vec()[3].eq("root")
1645 })
1646 .unwrap()
1647 .as_vec()[1],
1648 cover_letter_event.id.to_string()
1649 );
1650 }
1651 }
1652 Ok(())
1653 }
1654}
1655
1656mod in_reply_to_mentions_issue {
1657 use nostr::ToBech32;
1658
1659 use super::*;
1660 pub fn get_pretend_issue_event() -> nostr::Event {
1661 serde_json::from_str(r#"{"created_at":1709286372,"content":"please provide feedback\nthis is an example ngit issue to demonstrate gitworkshop.dev.\n\nplease provide feedback with in reply to this issue or by creating a new issue.","tags":[["r","26689f97810fc656c7134c76e2a37d33b2e40ce7"],["a","30617:a008def15796fba9a0d6fab04e8fd57089285d9fd505da5a83fe8aad57a3564d:ngit","wss://relay.damus.io","root"],["p","a008def15796fba9a0d6fab04e8fd57089285d9fd505da5a83fe8aad57a3564d"]],"kind":1621,"pubkey":"a008def15796fba9a0d6fab04e8fd57089285d9fd505da5a83fe8aad57a3564d","id":"e944765d625ae7323d080da0df069c726a0e5490a17b452f854d85e18f781588","sig":"a1af9e89a35f1f7ef93e3de33986bd86cb7c4d7d9abb233c0c6405f32b5788171e47f84551afe8515b3107d12f03472721ea784b8791ff3f25e66a3169a54c20"}"#).unwrap()
1662 }
1663
1664 fn cli_tester_create_proposal(git_repo: &GitTestRepo) -> CliTester {
1665 let issue_bech32 = get_pretend_issue_event().id.to_bech32().unwrap();
1666 let args = vec![
1667 "--nsec",
1668 TEST_KEY_1_NSEC,
1669 "--password",
1670 TEST_PASSWORD,
1671 "--disable-cli-spinners",
1672 "send",
1673 "HEAD~2",
1674 "--in-reply-to",
1675 &issue_bech32,
1676 // "note1a9z8vhtzttnny0ggpksd7p5uwf4qu4ys59a52tu9fkz7rrmczkyqc46ngg",
1677 "--title",
1678 "exampletitle",
1679 "--description",
1680 "exampledescription",
1681 ];
1682 CliTester::new_from_dir(&git_repo.dir, args)
1683 }
1684
1685 async fn prep_run_create_proposal() -> Result<(
1686 Relay<'static>,
1687 Relay<'static>,
1688 Relay<'static>,
1689 Relay<'static>,
1690 Relay<'static>,
1691 )> {
1692 let git_repo = prep_git_repo()?;
1693 // fallback (51,52) user write (53, 55) repo (55, 56)
1694 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
1695 Relay::new(
1696 8051,
1697 None,
1698 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1699 relay.respond_events(
1700 client_id,
1701 &subscription_id,
1702 &vec![
1703 generate_test_key_1_metadata_event("fred"),
1704 generate_test_key_1_relay_list_event(),
1705 get_pretend_issue_event(),
1706 ],
1707 )?;
1708 Ok(())
1709 }),
1710 ),
1711 Relay::new(8052, None, None),
1712 Relay::new(8053, None, None),
1713 Relay::new(
1714 8055,
1715 None,
1716 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1717 relay.respond_events(
1718 client_id,
1719 &subscription_id,
1720 &vec![generate_repo_ref_event(), get_pretend_issue_event()],
1721 )?;
1722 Ok(())
1723 }),
1724 ),
1725 Relay::new(8056, None, None),
1726 );
1727
1728 // // check relay had the right number of events
1729 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1730 let mut p = cli_tester_create_proposal(&git_repo);
1731 p.expect_end_eventually()?;
1732 for p in [51, 52, 53, 55, 56] {
1733 relay::shutdown_relay(8000 + p)?;
1734 }
1735 Ok(())
1736 });
1737
1738 // launch relay
1739 let _ = join!(
1740 r51.listen_until_close(),
1741 r52.listen_until_close(),
1742 r53.listen_until_close(),
1743 r55.listen_until_close(),
1744 r56.listen_until_close(),
1745 );
1746 cli_tester_handle.join().unwrap()?;
1747 Ok((r51, r52, r53, r55, r56))
1748 }
1749
1750 #[tokio::test]
1751 #[serial]
1752 async fn issue_event_mentioned_in_tagged_cover_letter() -> Result<()> {
1753 let (_, _, r53, r55, r56) = prep_run_create_proposal().await?;
1754 for relay in [&r53, &r55, &r56] {
1755 let cover_letter_event: &nostr::Event =
1756 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
1757 assert!(cover_letter_event.tags().iter().any(|t| {
1758 t.as_vec()[0].eq("e")
1759 && t.as_vec()[1].eq(&get_pretend_issue_event().id.to_hex())
1760 && t.as_vec()[3].eq(&"mention")
1761 }));
1762 }
1763 Ok(())
1764 }
1765
1766 #[tokio::test]
1767 #[serial]
1768 async fn isnt_tagged_as_revision() -> Result<()> {
1769 let (_, _, r53, r55, r56) = prep_run_create_proposal().await?;
1770 for relay in [&r53, &r55, &r56] {
1771 let cover_letter_event: &nostr::Event =
1772 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
1773 assert!(
1774 !cover_letter_event
1775 .tags()
1776 .iter()
1777 .any(|t| { t.as_vec()[0].eq("t") && t.as_vec()[1].eq(&"revision-root") })
1778 );
1779 }
1780 Ok(())
1781 }
1782}
1783mod in_reply_to_mentions_npub_and_nprofile_which_get_mentioned_in_proposal_root {
1784
1785 use super::*;
1786
1787 fn cli_tester_create_proposal(git_repo: &GitTestRepo) -> CliTester {
1788 let args = vec![
1789 "--nsec",
1790 TEST_KEY_1_NSEC,
1791 "--password",
1792 TEST_PASSWORD,
1793 "--disable-cli-spinners",
1794 "send",
1795 "HEAD~2",
1796 "--in-reply-to",
1797 // nsec1q3c5xnsm5m4wgsrhwnz04p0d5mevkryyggqgdpa9jwulpq9gldhswgtxvq
1798 "npub1knxeegzqg0xqflsryvg7l7x7nmpe7kd7pl7zazug0a7t99tdsphszuyapx",
1799 // nsec1nx5ulvcndhcuu8k6q8fenw50l6y75sec7pj8vr0r68l6a44w3lqspvj02k
1800 "nprofile1qqsvru3yqrec6dxjn06f8cjh79jcu9wyaxu4y6v47yzpsx7vjm4xcuc33z2n3",
1801 "--title",
1802 "exampletitle",
1803 "--description",
1804 "exampledescription",
1805 ];
1806 CliTester::new_from_dir(&git_repo.dir, args)
1807 }
1808
1809 async fn prep_run_create_proposal() -> Result<(
1810 Relay<'static>,
1811 Relay<'static>,
1812 Relay<'static>,
1813 Relay<'static>,
1814 Relay<'static>,
1815 )> {
1816 let git_repo = prep_git_repo()?;
1817 // fallback (51,52) user write (53, 55) repo (55, 56)
1818 let (mut r51, mut r52, mut r53, mut r55, mut r56) = (
1819 Relay::new(
1820 8051,
1821 None,
1822 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1823 relay.respond_events(
1824 client_id,
1825 &subscription_id,
1826 &vec![
1827 generate_test_key_1_metadata_event("fred"),
1828 generate_test_key_1_relay_list_event(),
1829 ],
1830 )?;
1831 Ok(())
1832 }),
1833 ),
1834 Relay::new(8052, None, None),
1835 Relay::new(8053, None, None),
1836 Relay::new(
1837 8055,
1838 None,
1839 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
1840 relay.respond_events(
1841 client_id,
1842 &subscription_id,
1843 &vec![generate_repo_ref_event()],
1844 )?;
1845 Ok(())
1846 }),
1847 ),
1848 Relay::new(8056, None, None),
1849 );
1850
1851 // // check relay had the right number of events
1852 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1853 let mut p = cli_tester_create_proposal(&git_repo);
1854 p.expect_end_eventually()?;
1855 for p in [51, 52, 53, 55, 56] {
1856 relay::shutdown_relay(8000 + p)?;
1857 }
1858 Ok(())
1859 });
1860
1861 // launch relay
1862 let _ = join!(
1863 r51.listen_until_close(),
1864 r52.listen_until_close(),
1865 r53.listen_until_close(),
1866 r55.listen_until_close(),
1867 r56.listen_until_close(),
1868 );
1869 cli_tester_handle.join().unwrap()?;
1870 Ok((r51, r52, r53, r55, r56))
1871 }
1872
1873 #[tokio::test]
1874 #[serial]
1875 async fn npub_and_nprofile_mentioned_in_tagged_cover_letter() -> Result<()> {
1876 let (_, _, r53, r55, r56) = prep_run_create_proposal().await?;
1877 for relay in [&r53, &r55, &r56] {
1878 let cover_letter_event: &nostr::Event =
1879 relay.events.iter().find(|e| is_cover_letter(e)).unwrap();
1880 assert!(cover_letter_event.tags().iter().any(|t| {
1881 t.as_vec()[0].eq("p")
1882 && t.as_vec()[1].eq(&nostr::Keys::parse(
1883 "nsec1q3c5xnsm5m4wgsrhwnz04p0d5mevkryyggqgdpa9jwulpq9gldhswgtxvq",
1884 )
1885 .unwrap()
1886 .public_key()
1887 .to_hex())
1888 }));
1889 assert!(cover_letter_event.tags().iter().any(|t| {
1890 t.as_vec()[0].eq("p")
1891 && t.as_vec()[1].eq(&nostr::Keys::parse(
1892 "nsec1nx5ulvcndhcuu8k6q8fenw50l6y75sec7pj8vr0r68l6a44w3lqspvj02k",
1893 )
1894 .unwrap()
1895 .public_key()
1896 .to_hex())
1897 }));
1898 }
1899 Ok(())
1900 }
1901}