upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests/ngit_login.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/ngit_login.rs')
-rw-r--r--tests/ngit_login.rs1186
1 files changed, 1186 insertions, 0 deletions
diff --git a/tests/ngit_login.rs b/tests/ngit_login.rs
new file mode 100644
index 0000000..477b25b
--- /dev/null
+++ b/tests/ngit_login.rs
@@ -0,0 +1,1186 @@
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}