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.rs1168
1 files changed, 1168 insertions, 0 deletions
diff --git a/tests/ngit/login.rs b/tests/ngit/login.rs
new file mode 100644
index 0000000..3bcfbf9
--- /dev/null
+++ b/tests/ngit/login.rs
@@ -0,0 +1,1168 @@
1use anyhow::Result;
2use git::GitTestRepo;
3use serial_test::serial;
4use test_utils::*;
5
6static EXPECTED_NSEC_PROMPT: &str = "login with nostr address / nsec";
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}
32mod with_relays {
33 use anyhow::Ok;
34 use futures::join;
35 use test_utils::relay::{shutdown_relay, ListenerReqFunc, Relay};
36
37 use super::*;
38
39 mod when_user_relay_list_aligns_with_fallback_relays {
40 // this simplifies testing
41 use super::*;
42
43 mod when_first_time_login {
44 use super::*;
45
46 // falls_back_to_fallback_relays - this is implict in the tests
47
48 mod dislays_logged_in_with_correct_name {
49
50 use super::*;
51
52 async fn run_test_displays_correct_name(
53 relay_listener1: Option<ListenerReqFunc<'_>>,
54 relay_listener2: Option<ListenerReqFunc<'_>>,
55 ) -> Result<()> {
56 let (mut r51, mut r52) = (
57 Relay::new(8051, None, relay_listener1),
58 Relay::new(8052, None, relay_listener2),
59 );
60
61 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
62 let test_repo = GitTestRepo::default();
63 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login"]);
64
65 p.expect_input(EXPECTED_NSEC_PROMPT)?
66 .succeeds_with(TEST_KEY_1_NSEC)?;
67
68 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
69 .succeeds_with(Some(true))?;
70
71 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
72 .succeeds_with(Some(false))?;
73
74 p.expect("saved login details to local git config\r\n")?;
75
76 p.expect("searching for profile...\r\n")?;
77
78 p.expect_end_with("logged in as fred\r\n")?;
79 for p in [51, 52] {
80 shutdown_relay(8000 + p)?;
81 }
82 Ok(())
83 });
84
85 // launch relay
86 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
87
88 cli_tester_handle.join().unwrap()?;
89 Ok(())
90 }
91
92 async fn run_test_displays_fallback_to_npub(
93 relay_listener1: Option<ListenerReqFunc<'_>>,
94 relay_listener2: Option<ListenerReqFunc<'_>>,
95 ) -> Result<()> {
96 let (mut r51, mut r52) = (
97 Relay::new(8051, None, relay_listener1),
98 Relay::new(8052, None, relay_listener2),
99 );
100
101 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
102 let test_repo = GitTestRepo::default();
103 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login"]);
104
105 p.expect_input(EXPECTED_NSEC_PROMPT)?
106 .succeeds_with(TEST_KEY_1_NSEC)?;
107
108 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
109 .succeeds_with(Some(true))?;
110
111 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
112 .succeeds_with(Some(false))?;
113
114 p.expect("saved login details to local git config\r\n")?;
115
116 p.expect("searching for profile...\r\n")?;
117
118 p.expect("cannot extract account name from account metadata...\r\n")?;
119
120 p.expect_end_with(
121 format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str(),
122 )?;
123 for p in [51, 52] {
124 shutdown_relay(8000 + p)?;
125 }
126 Ok(())
127 });
128
129 // launch relay
130 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
131
132 cli_tester_handle.join().unwrap()?;
133 Ok(())
134 }
135
136 #[tokio::test]
137 #[serial]
138 async fn when_latest_metadata_and_relay_list_on_all_relays() -> Result<()> {
139 run_test_displays_correct_name(
140 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
141 relay.respond_events(
142 client_id,
143 &subscription_id,
144 &vec![
145 generate_test_key_1_metadata_event("fred"),
146 generate_test_key_1_relay_list_event_same_as_fallback(),
147 ],
148 )?;
149 Ok(())
150 }),
151 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
152 relay.respond_events(
153 client_id,
154 &subscription_id,
155 &vec![
156 generate_test_key_1_metadata_event("fred"),
157 generate_test_key_1_relay_list_event_same_as_fallback(),
158 ],
159 )?;
160 Ok(())
161 }),
162 )
163 .await
164 }
165
166 mod poorly_quality_metadata_event {
167 use super::*;
168
169 #[tokio::test]
170 #[serial]
171 async fn when_metadata_contains_only_display_name() -> Result<()> {
172 run_test_displays_correct_name(
173 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
174 relay.respond_events(
175 client_id,
176 &subscription_id,
177 &vec![
178 nostr::event::EventBuilder::metadata(
179 &nostr::Metadata::new().display_name("fred"),
180 )
181 .to_event(&TEST_KEY_1_KEYS)
182 .unwrap(),
183 generate_test_key_1_relay_list_event_same_as_fallback(),
184 ],
185 )?;
186 Ok(())
187 }),
188 None,
189 )
190 .await
191 }
192
193 #[tokio::test]
194 #[serial]
195 async fn when_metadata_contains_only_displayname() -> Result<()> {
196 println!(
197 "displayName: {}",
198 nostr::Metadata::new()
199 .custom_field("displayName", "fred")
200 .custom
201 .get("displayName")
202 .unwrap()
203 );
204 println!(
205 "name: {}",
206 nostr::Metadata::new().name("fred").name.unwrap()
207 );
208
209 run_test_displays_correct_name(
210 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
211 relay.respond_events(
212 client_id,
213 &subscription_id,
214 &vec![
215 nostr::event::EventBuilder::metadata(
216 &nostr::Metadata::new()
217 .custom_field("displayName", "fred"),
218 )
219 .to_event(&TEST_KEY_1_KEYS)
220 .unwrap(),
221 generate_test_key_1_relay_list_event_same_as_fallback(),
222 ],
223 )?;
224 Ok(())
225 }),
226 None,
227 )
228 .await
229 }
230
231 #[tokio::test]
232 #[serial]
233 async fn displays_npub_when_metadata_contains_no_name_displayname_or_display_name()
234 -> Result<()> {
235 run_test_displays_fallback_to_npub(
236 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
237 relay.respond_events(
238 client_id,
239 &subscription_id,
240 &vec![
241 nostr::event::EventBuilder::metadata(
242 &nostr::Metadata::new().about("other info in metadata"),
243 )
244 .to_event(&TEST_KEY_1_KEYS)
245 .unwrap(),
246 generate_test_key_1_relay_list_event_same_as_fallback(),
247 ],
248 )?;
249 Ok(())
250 }),
251 None,
252 )
253 .await
254 }
255 }
256
257 #[tokio::test]
258 #[serial]
259 async fn when_latest_metadata_and_relay_list_on_some_relays_but_others_have_none()
260 -> Result<()> {
261 run_test_displays_correct_name(
262 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
263 relay.respond_events(
264 client_id,
265 &subscription_id,
266 &vec![
267 generate_test_key_1_metadata_event("fred"),
268 generate_test_key_1_relay_list_event_same_as_fallback(),
269 ],
270 )?;
271 Ok(())
272 }),
273 None,
274 )
275 .await
276 }
277
278 #[tokio::test]
279 #[serial]
280 async fn when_latest_metadata_only_on_relay_and_relay_list_on_another() -> Result<()>
281 {
282 run_test_displays_correct_name(
283 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
284 relay.respond_events(
285 client_id,
286 &subscription_id,
287 &vec![generate_test_key_1_metadata_event("fred")],
288 )?;
289 Ok(())
290 }),
291 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
292 relay.respond_events(
293 client_id,
294 &subscription_id,
295 &vec![generate_test_key_1_relay_list_event_same_as_fallback()],
296 )?;
297 Ok(())
298 }),
299 )
300 .await
301 }
302
303 #[tokio::test]
304 #[serial]
305 async fn when_some_relays_return_old_metadata_event() -> Result<()> {
306 run_test_displays_correct_name(
307 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
308 relay.respond_events(
309 client_id,
310 &subscription_id,
311 &vec![
312 generate_test_key_1_metadata_event("fred"),
313 generate_test_key_1_relay_list_event_same_as_fallback(),
314 ],
315 )?;
316 Ok(())
317 }),
318 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
319 relay.respond_events(
320 client_id,
321 &subscription_id,
322 &vec![generate_test_key_1_metadata_event_old("fred old")],
323 )?;
324 Ok(())
325 }),
326 )
327 .await
328 }
329
330 #[tokio::test]
331 #[serial]
332 async fn when_some_relays_return_other_users_metadata() -> Result<()> {
333 run_test_displays_correct_name(
334 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
335 relay.respond_events(
336 client_id,
337 &subscription_id,
338 &vec![generate_test_key_2_metadata_event("carole")],
339 )?;
340 Ok(())
341 }),
342 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
343 relay.respond_events(
344 client_id,
345 &subscription_id,
346 &vec![
347 generate_test_key_1_metadata_event_old("fred"),
348 generate_test_key_1_relay_list_event_same_as_fallback(),
349 ],
350 )?;
351 Ok(())
352 }),
353 )
354 .await
355 }
356
357 #[tokio::test]
358 #[serial]
359 async fn when_some_relays_return_other_event_kinds() -> Result<()> {
360 run_test_displays_correct_name(
361 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
362 let event = generate_test_key_1_kind_event(nostr::Kind::TextNote);
363 relay.respond_events(
364 client_id,
365 &subscription_id,
366 &vec![make_event_old_or_change_user(event, &TEST_KEY_1_KEYS, 0)],
367 )?;
368 Ok(())
369 }),
370 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
371 relay.respond_events(
372 client_id,
373 &subscription_id,
374 &vec![
375 generate_test_key_1_metadata_event_old("fred"),
376 generate_test_key_1_relay_list_event_same_as_fallback(),
377 ],
378 )?;
379 Ok(())
380 }),
381 )
382 .await
383 }
384
385 mod when_specifying_command_line_nsec_only {
386 use super::*;
387
388 #[tokio::test]
389 #[serial]
390 async fn displays_correct_name() -> Result<()> {
391 run_test_when_specifying_command_line_nsec_only_displays_correct_name(
392 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
393 relay.respond_events(
394 client_id,
395 &subscription_id,
396 &vec![
397 generate_test_key_1_metadata_event("fred"),
398 generate_test_key_1_relay_list_event_same_as_fallback(),
399 ],
400 )?;
401 Ok(())
402 }),
403 None,
404 )
405 .await
406 }
407 async fn run_test_when_specifying_command_line_nsec_only_displays_correct_name(
408 relay_listener1: Option<ListenerReqFunc<'_>>,
409 relay_listener2: Option<ListenerReqFunc<'_>>,
410 ) -> Result<()> {
411 let (mut r51, mut r52) = (
412 Relay::new(8051, None, relay_listener1),
413 Relay::new(8052, None, relay_listener2),
414 );
415
416 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
417 let test_repo = GitTestRepo::default();
418 let mut p = CliTester::new_from_dir(
419 &test_repo.dir,
420 ["login", "--nsec", TEST_KEY_1_NSEC],
421 );
422
423 p.expect("saved login details to local git config\r\n")?;
424
425 p.expect("searching for profile...\r\n")?;
426
427 p.expect_end_with("logged in as fred\r\n")?;
428 for p in [51, 52] {
429 shutdown_relay(8000 + p)?;
430 }
431 Ok(())
432 });
433
434 // launch relay
435 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
436
437 cli_tester_handle.join().unwrap()?;
438 Ok(())
439 }
440 }
441 mod when_specifying_command_line_password_only {
442 use super::*;
443
444 #[tokio::test]
445 #[serial]
446 async fn displays_correct_name() -> Result<()> {
447 run_test_when_specifying_command_line_password_only_displays_correct_name(
448 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
449 relay.respond_events(
450 client_id,
451 &subscription_id,
452 &vec![
453 generate_test_key_1_metadata_event("fred"),
454 generate_test_key_1_relay_list_event_same_as_fallback(),
455 ],
456 )?;
457 Ok(())
458 }),
459 None,
460 )
461 .await
462 }
463 async fn run_test_when_specifying_command_line_password_only_displays_correct_name(
464 relay_listener1: Option<ListenerReqFunc<'_>>,
465 relay_listener2: Option<ListenerReqFunc<'_>>,
466 ) -> Result<()> {
467 let (mut r51, mut r52) = (
468 Relay::new(8051, None, relay_listener1),
469 Relay::new(8052, None, relay_listener2),
470 );
471
472 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
473 let test_repo = GitTestRepo::default();
474 CliTester::new_from_dir(
475 &test_repo.dir,
476 [
477 "login",
478 "--offline",
479 "--nsec",
480 TEST_KEY_1_NSEC,
481 "--password",
482 TEST_PASSWORD,
483 ],
484 )
485 .expect_end_eventually()?;
486
487 let mut p = CliTester::new_from_dir(
488 &test_repo.dir,
489 ["login", "--password", TEST_PASSWORD],
490 );
491
492 p.expect("searching for profile...\r\n")?;
493
494 p.expect_end_with("logged in as fred\r\n")?;
495 for p in [51, 52] {
496 shutdown_relay(8000 + p)?;
497 }
498 Ok(())
499 });
500
501 // launch relay
502 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
503
504 cli_tester_handle.join().unwrap()?;
505 Ok(())
506 }
507 }
508
509 mod when_specifying_command_line_nsec_and_password {
510 use super::*;
511
512 #[tokio::test]
513 #[serial]
514 async fn displays_correct_name() -> Result<()> {
515 run_test_when_specifying_command_line_nsec_and_password_displays_correct_name(
516 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
517 relay.respond_events(
518 client_id,
519 &subscription_id,
520 &vec![
521 generate_test_key_1_metadata_event("fred"),
522 generate_test_key_1_relay_list_event_same_as_fallback(),
523 ],
524 )?;
525 Ok(())
526 }),
527 None,
528 ).await
529 }
530 async fn run_test_when_specifying_command_line_nsec_and_password_displays_correct_name(
531 relay_listener1: Option<ListenerReqFunc<'_>>,
532 relay_listener2: Option<ListenerReqFunc<'_>>,
533 ) -> Result<()> {
534 let (mut r51, mut r52) = (
535 Relay::new(8051, None, relay_listener1),
536 Relay::new(8052, None, relay_listener2),
537 );
538
539 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
540 let test_repo = GitTestRepo::default();
541 let mut p = CliTester::new_from_dir(
542 &test_repo.dir,
543 [
544 "login",
545 "--nsec",
546 TEST_KEY_1_NSEC,
547 "--password",
548 TEST_PASSWORD,
549 ],
550 );
551
552 p.expect("saved login details to local git config\r\n")?;
553
554 p.expect("searching for profile...\r\n")?;
555
556 p.expect_end_with("logged in as fred\r\n")?;
557 for p in [51, 52] {
558 shutdown_relay(8000 + p)?;
559 }
560 Ok(())
561 });
562
563 // launch relay
564 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
565
566 cli_tester_handle.join().unwrap()?;
567 Ok(())
568 }
569 }
570 }
571
572 mod when_no_metadata_found {
573 use super::*;
574
575 #[tokio::test]
576 #[serial]
577 async fn warm_user_and_displays_npub() -> Result<()> {
578 run_test_when_no_metadata_found_warns_user_and_uses_npub(None, None).await
579 }
580
581 async fn run_test_when_no_metadata_found_warns_user_and_uses_npub(
582 relay_listener1: Option<ListenerReqFunc<'_>>,
583 relay_listener2: Option<ListenerReqFunc<'_>>,
584 ) -> Result<()> {
585 let (mut r51, mut r52) = (
586 Relay::new(8051, None, relay_listener1),
587 Relay::new(8052, None, relay_listener2),
588 );
589
590 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
591 let test_repo = GitTestRepo::default();
592 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login"]);
593
594 p.expect_input(EXPECTED_NSEC_PROMPT)?
595 .succeeds_with(TEST_KEY_1_NSEC)?;
596
597 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
598 .succeeds_with(Some(true))?;
599
600 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
601 .succeeds_with(Some(false))?;
602
603 p.expect("saved login details to local git config\r\n")?;
604
605 p.expect("searching for profile...\r\n")?;
606
607 p.expect("cannot find profile...\r\n")?;
608
609 p.expect_end_with(format!("logged in as {TEST_KEY_1_NPUB}\r\n").as_str())?;
610 for p in [51, 52] {
611 shutdown_relay(8000 + p)?;
612 }
613 Ok(())
614 });
615
616 // launch relay
617 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
618
619 cli_tester_handle.join().unwrap()?;
620 Ok(())
621 }
622 }
623
624 mod when_metadata_but_no_relay_list_found {
625 use super::*;
626
627 #[tokio::test]
628 #[serial]
629 async fn warm_user_and_displays_name() -> Result<()> {
630 run_test_when_no_relay_list_found_warns_user_and_uses_npub(
631 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
632 relay.respond_events(
633 client_id,
634 &subscription_id,
635 &vec![generate_test_key_1_metadata_event("fred")],
636 )?;
637 Ok(())
638 }),
639 None,
640 )
641 .await
642 }
643
644 async fn run_test_when_no_relay_list_found_warns_user_and_uses_npub(
645 relay_listener1: Option<ListenerReqFunc<'_>>,
646 relay_listener2: Option<ListenerReqFunc<'_>>,
647 ) -> Result<()> {
648 let (mut r51, mut r52) = (
649 Relay::new(8051, None, relay_listener1),
650 Relay::new(8052, None, relay_listener2),
651 );
652
653 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
654 let test_repo = GitTestRepo::default();
655 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login"]);
656
657 p.expect_input(EXPECTED_NSEC_PROMPT)?
658 .succeeds_with(TEST_KEY_1_NSEC)?;
659
660 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
661 .succeeds_with(Some(true))?;
662
663 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
664 .succeeds_with(Some(false))?;
665
666 p.expect("saved login details to local git config\r\n")?;
667
668 p.expect("searching for profile...\r\n")?;
669
670 p.expect("cannot find your relay list. consider using another nostr client to create one to enhance your nostr experience.\r\n")?;
671
672 p.expect_end_with("logged in as fred\r\n")?;
673 for p in [51, 52] {
674 shutdown_relay(8000 + p)?;
675 }
676 Ok(())
677 });
678
679 // launch relay
680 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
681
682 cli_tester_handle.join().unwrap()?;
683 Ok(())
684 }
685 }
686 }
687
688 mod when_second_time_login_and_details_already_fetched {
689 use super::*;
690
691 mod uses_cache_and_stores_and_retrieves_ncryptsec_from_local_git_config {
692 use super::*;
693
694 #[tokio::test]
695 #[serial]
696 async fn dislays_logged_in_with_correct_name() -> Result<()> {
697 run_test_dislays_logged_in_with_correct_name(Some(
698 &|relay, client_id, subscription_id, _| -> Result<()> {
699 relay.respond_events(
700 client_id,
701 &subscription_id,
702 &vec![
703 generate_test_key_1_metadata_event("fred"),
704 generate_test_key_1_relay_list_event_same_as_fallback(),
705 ],
706 )?;
707 Ok(())
708 },
709 ))
710 .await
711 }
712 async fn run_test_dislays_logged_in_with_correct_name(
713 relay_listener: Option<ListenerReqFunc<'_>>,
714 ) -> Result<()> {
715 let (mut r51, mut r52) = (
716 Relay::new(8051, None, relay_listener),
717 Relay::new(8052, None, None),
718 );
719
720 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
721 let test_repo = GitTestRepo::default();
722 let mut p = CliTester::new_from_dir(
723 &test_repo.dir,
724 [
725 "login",
726 "--nsec",
727 TEST_KEY_1_NSEC,
728 "--password",
729 TEST_PASSWORD,
730 ],
731 );
732
733 p.expect("saved login details to local git config\r\n")?;
734
735 p.expect_end_eventually_with("logged in as fred\r\n")?;
736
737 for p in [51, 52] {
738 shutdown_relay(8000 + p)?;
739 }
740
741 let mut p = CliTester::new_from_dir(
742 &test_repo.dir,
743 ["login", "--password", TEST_PASSWORD],
744 );
745
746 p.expect_end_eventually_with("logged in as fred\r\n")?;
747
748 Ok(())
749 });
750
751 // launch relay
752 let _ = join!(r51.listen_until_close(), r52.listen_until_close(),);
753
754 cli_tester_handle.join().unwrap()?;
755
756 Ok(())
757 }
758 }
759 }
760 }
761 mod when_user_relay_list_contains_write_relays_not_in_fallback_list {
762 use super::*;
763 mod when_latest_metadata_not_on_fallback_relays_only_on_relays_in_user_list {
764 use super::*;
765 async fn run_test_displays_correct_name(
766 relay_listener1: Option<ListenerReqFunc<'_>>,
767 relay_listener2: Option<ListenerReqFunc<'_>>,
768 ) -> Result<()> {
769 let (mut r51, mut r52, mut r53, mut r55) = (
770 Relay::new(8051, None, relay_listener1),
771 Relay::new(8052, None, None),
772 Relay::new(8053, None, relay_listener2),
773 Relay::new(8055, None, None),
774 );
775
776 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
777 let test_repo = GitTestRepo::default();
778 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login"]);
779
780 p.expect_input(EXPECTED_NSEC_PROMPT)?
781 .succeeds_with(TEST_KEY_1_NSEC)?;
782
783 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
784 .succeeds_with(Some(true))?;
785
786 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
787 .succeeds_with(Some(false))?;
788
789 p.expect("saved login details to local git config\r\n")?;
790
791 p.expect("searching for profile...\r\n")?;
792
793 p.expect_end_with("logged in as fred\r\n")?;
794 for p in [51, 52, 53, 55] {
795 shutdown_relay(8000 + p)?;
796 }
797 Ok(())
798 });
799
800 // launch relay
801 let _ = join!(
802 r51.listen_until_close(),
803 r52.listen_until_close(),
804 r53.listen_until_close(),
805 r55.listen_until_close(),
806 );
807
808 cli_tester_handle.join().unwrap()?;
809 Ok(())
810 }
811
812 /// this also tests that additional relays are queried
813 #[tokio::test]
814 #[serial]
815 async fn displays_correct_name() -> Result<()> {
816 run_test_displays_correct_name(
817 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
818 relay.respond_events(
819 client_id,
820 &subscription_id,
821 &vec![
822 generate_test_key_1_metadata_event_old("Fred"),
823 generate_test_key_1_relay_list_event(),
824 ],
825 )?;
826 Ok(())
827 }),
828 Some(&|relay, client_id, subscription_id, _| -> Result<()> {
829 relay.respond_events(
830 client_id,
831 &subscription_id,
832 &vec![
833 generate_test_key_1_metadata_event("fred"),
834 generate_test_key_1_relay_list_event(),
835 ],
836 )?;
837 Ok(())
838 }),
839 )
840 .await
841 }
842 }
843 }
844}
845
846/// using the offline flag simplifies the test. relay interaction is tested
847/// seperately
848mod with_offline_flag {
849 use super::*;
850 mod when_first_time_login {
851 use super::*;
852
853 #[test]
854 fn prompts_for_nsec_and_password() -> Result<()> {
855 standard_first_time_login_encrypting_nsec()?;
856 Ok(())
857 }
858
859 #[test]
860 fn succeeds_with_text_logged_in_as_npub() -> Result<()> {
861 let test_repo = GitTestRepo::default();
862 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login", "--offline"]);
863
864 p.expect_input(EXPECTED_NSEC_PROMPT)?
865 .succeeds_with(TEST_KEY_1_NSEC)?;
866
867 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
868 .succeeds_with(Some(true))?;
869
870 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
871 .succeeds_with(Some(true))?;
872
873 p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)?
874 .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)?
875 .succeeds_with(TEST_PASSWORD)?;
876
877 p.expect("saved login details to local git config\r\n")?;
878
879 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
880 }
881
882 #[test]
883 fn succeeds_with_hex_secret_key_in_place_of_nsec() -> Result<()> {
884 let test_repo = GitTestRepo::default();
885 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login", "--offline"]);
886
887 p.expect_input(EXPECTED_NSEC_PROMPT)?
888 .succeeds_with(TEST_KEY_1_SK_HEX)?;
889
890 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
891 .succeeds_with(Some(true))?;
892
893 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
894 .succeeds_with(Some(true))?;
895
896 p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)?
897 .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)?
898 .succeeds_with(TEST_PASSWORD)?;
899
900 p.expect("saved login details to local git config\r\n")?;
901
902 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
903 }
904
905 mod when_invalid_nsec {
906 use super::*;
907
908 #[test]
909 fn prompts_for_nsec_until_valid() -> Result<()> {
910 let invalid_nsec_response =
911 "invalid. try again with nostr address / bunker uri / nsec";
912
913 let test_repo = GitTestRepo::default();
914 let mut p = CliTester::new_from_dir(&test_repo.dir, ["login", "--offline"]);
915
916 p.expect_input(EXPECTED_NSEC_PROMPT)?
917 // this behaviour is intentional. rejecting the response with dialoguer
918 // hides the original input from the user so they
919 // cannot see the mistake they made.
920 .succeeds_with(TEST_INVALID_NSEC)?;
921
922 p.expect_input(invalid_nsec_response)?
923 .succeeds_with(TEST_INVALID_NSEC)?;
924
925 p.expect_input(invalid_nsec_response)?
926 .succeeds_with(TEST_KEY_1_NSEC)?;
927
928 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
929 .succeeds_with(Some(true))?;
930
931 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
932 .succeeds_with(Some(true))?;
933
934 p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)?
935 .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)?
936 .succeeds_with(TEST_PASSWORD)?;
937
938 p.expect("saved login details to local git config\r\n")?;
939
940 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
941 }
942 }
943 }
944
945 mod when_called_with_nsec_parameter_only {
946 use super::*;
947
948 #[test]
949 fn valid_nsec_param_succeeds_without_prompts() -> Result<()> {
950 let test_repo = GitTestRepo::default();
951 let mut p = CliTester::new_from_dir(
952 &test_repo.dir,
953 ["login", "--offline", "--nsec", TEST_KEY_1_NSEC],
954 );
955
956 p.expect("saved login details to local git config\r\n")?;
957
958 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
959 }
960
961 #[test]
962 fn forgets_identity() -> Result<()> {
963 let test_repo = GitTestRepo::default();
964 let mut p = CliTester::new_from_dir(
965 &test_repo.dir,
966 ["login", "--offline", "--nsec", TEST_KEY_1_NSEC],
967 );
968
969 p.expect("saved login details to local git config\r\n")?;
970
971 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())?;
972
973 p = CliTester::new_from_dir(&test_repo.dir, ["login", "--offline"]);
974
975 p.expect_input(EXPECTED_NSEC_PROMPT)?
976 .succeeds_with(TEST_KEY_1_NSEC)?;
977
978 p.exit()
979 }
980
981 mod when_logging_in_as_different_nsec {
982 use super::*;
983
984 #[test]
985 fn valid_nsec_param_succeeds_without_prompts_and_logs_in() -> Result<()> {
986 standard_first_time_login_encrypting_nsec()?.exit()?;
987 let test_repo = GitTestRepo::default();
988 let mut p = CliTester::new_from_dir(
989 &test_repo.dir,
990 ["login", "--offline", "--nsec", TEST_KEY_2_NSEC],
991 );
992
993 p.expect("saved login details to local git config\r\n")?;
994
995 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NPUB).as_str())
996 }
997 }
998 #[test]
999 fn invalid_nsec_param_fails_without_prompts() -> Result<()> {
1000 let test_repo = GitTestRepo::default();
1001 let mut p = CliTester::new_from_dir(
1002 &test_repo.dir,
1003 ["login", "--offline", "--nsec", TEST_INVALID_NSEC],
1004 );
1005
1006 p.expect_end_with(
1007 "Error: invalid nsec parameter\r\n\r\nCaused by:\r\n Invalid secret key\r\n",
1008 )
1009 }
1010 }
1011
1012 mod when_called_with_nsec_and_password_parameter {
1013 use super::*;
1014
1015 #[test]
1016 fn valid_nsec_param_succeeds_without_prompts() -> Result<()> {
1017 let test_repo = GitTestRepo::default();
1018 let mut p = CliTester::new_from_dir(
1019 &test_repo.dir,
1020 [
1021 "login",
1022 "--offline",
1023 "--nsec",
1024 TEST_KEY_1_NSEC,
1025 "--password",
1026 TEST_PASSWORD,
1027 ],
1028 );
1029 p.expect("saved login details to local git config\r\n")?;
1030 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
1031 }
1032
1033 #[test]
1034 fn parameters_can_be_called_globally() -> Result<()> {
1035 let test_repo = GitTestRepo::default();
1036 let mut p = CliTester::new_from_dir(
1037 &test_repo.dir,
1038 [
1039 "--nsec",
1040 TEST_KEY_1_NSEC,
1041 "--password",
1042 TEST_PASSWORD,
1043 "login",
1044 "--offline",
1045 ],
1046 );
1047 p.expect("saved login details to local git config\r\n")?;
1048 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
1049 }
1050
1051 mod when_logging_in_as_different_nsec {
1052 use super::*;
1053
1054 #[test]
1055 fn valid_nsec_param_succeeds_without_prompts_and_logs_in() -> Result<()> {
1056 standard_first_time_login_encrypting_nsec()?.exit()?;
1057 let test_repo = GitTestRepo::default();
1058 let mut p = CliTester::new_from_dir(
1059 &test_repo.dir,
1060 [
1061 "login",
1062 "--offline",
1063 "--nsec",
1064 TEST_KEY_2_NSEC,
1065 "--password",
1066 TEST_PASSWORD,
1067 ],
1068 );
1069 p.expect("saved login details to local git config\r\n")?;
1070 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NPUB).as_str())
1071 }
1072 }
1073
1074 mod when_provided_with_new_password {
1075 use super::*;
1076
1077 #[test]
1078 fn password_changes() -> Result<()> {
1079 standard_first_time_login_encrypting_nsec()?.exit()?;
1080 let test_repo = GitTestRepo::default();
1081 let mut p = CliTester::new_from_dir(
1082 &test_repo.dir,
1083 [
1084 "login",
1085 "--offline",
1086 "--nsec",
1087 TEST_KEY_1_NSEC,
1088 "--password",
1089 TEST_INVALID_PASSWORD,
1090 ],
1091 );
1092 p.expect("saved login details to local git config\r\n")?;
1093 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())?;
1094
1095 CliTester::new_from_dir(
1096 &test_repo.dir,
1097 ["--password", TEST_INVALID_PASSWORD, "login", "--offline"],
1098 )
1099 .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
1100 }
1101 }
1102
1103 #[test]
1104 fn invalid_nsec_param_fails_without_prompts() -> Result<()> {
1105 let test_repo = GitTestRepo::default();
1106 let mut p = CliTester::new_from_dir(
1107 &test_repo.dir,
1108 [
1109 "login",
1110 "--offline",
1111 "--nsec",
1112 TEST_INVALID_NSEC,
1113 "--password",
1114 TEST_PASSWORD,
1115 ],
1116 );
1117 p.expect_end_with(
1118 "Error: invalid nsec parameter\r\n\r\nCaused by:\r\n Invalid secret key\r\n",
1119 )
1120 }
1121 }
1122
1123 mod when_weak_password {
1124 use super::*;
1125
1126 #[test]
1127 // combined into a single test as it is computationally expensive to run
1128 fn warns_it_might_take_a_few_seconds_then_succeeds_then_second_login_prompts_for_password_then_warns_again_then_succeeds()
1129 -> Result<()> {
1130 let test_repo = GitTestRepo::default();
1131 let mut p =
1132 CliTester::new_with_timeout_from_dir(15000, &test_repo.dir, ["login", "--offline"]);
1133 p.expect_input(EXPECTED_NSEC_PROMPT)?
1134 .succeeds_with(TEST_KEY_1_NSEC)?;
1135
1136 p.expect_confirm(EXPECTED_LOCAL_REPOSITORY_PROMPT, Some(false))?
1137 .succeeds_with(Some(true))?;
1138
1139 p.expect_confirm(EXPECTED_REQUIRE_PASSWORD_PROMPT, Some(false))?
1140 .succeeds_with(Some(true))?;
1141
1142 p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)?
1143 .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)?
1144 .succeeds_with(TEST_WEAK_PASSWORD)?;
1145
1146 p.expect("this may take a few seconds...\r\n")?;
1147
1148 p.expect("saved login details to local git config\r\n")?;
1149
1150 p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())
1151
1152 // commented out as 'login' command now assumes you want to
1153 // login as a new user
1154 // p = CliTester::new_with_timeout(10000, ["login",
1155 // "--offline"]);
1156
1157 // p.expect(format!("login as {}\r\n",
1158 // TEST_KEY_1_NPUB).as_str())?
1159 // .expect_password(EXPECTED_PASSWORD_PROMPT)?
1160 // .succeeds_with(TEST_WEAK_PASSWORD)?;
1161
1162 // p.expect("this may take a few seconds...\r\n")?;
1163
1164 // p.expect_end_with(format!("logged in as {}\r\n",
1165 // TEST_KEY_1_NPUB).as_str())
1166 }
1167 }
1168}