Skip to main content

max / goingson

4.3 KB · 106 lines History Blame Raw
1 //! Integration tests for SqliteUserRepository.
2
3 #[allow(dead_code)]
4 mod common;
5
6 use goingson_core::UserRepository;
7 use goingson_db_sqlite::SqliteUserRepository;
8
9 #[tokio::test]
10 async fn test_create_hashes_password_and_lowercases_email() {
11 let pool = common::setup_test_db().await;
12 let repo = SqliteUserRepository::new(pool);
13
14 let user = repo
15 .create("Alice@Example.COM", "correct horse battery staple", "Alice")
16 .await
17 .expect("Failed to create user");
18
19 assert_eq!(user.email, "alice@example.com", "email must be stored lowercased");
20 assert_eq!(user.display_name, "Alice");
21 assert_ne!(user.password_hash, "correct horse battery staple", "password must not be stored in plaintext");
22 assert!(user.password_hash.starts_with("$argon2"), "stored hash should be a PHC Argon2 string, got: {}", user.password_hash);
23 assert!(user.last_login_at.is_none(), "a freshly created user has never logged in");
24 }
25
26 #[tokio::test]
27 async fn test_find_by_email_found_and_not_found() {
28 let pool = common::setup_test_db().await;
29 let repo = SqliteUserRepository::new(pool);
30
31 repo.create("bob@example.com", "hunter2hunter2", "Bob").await.expect("create");
32
33 let found = repo.find_by_email("bob@example.com").await.expect("find");
34 assert!(found.is_some());
35 assert_eq!(found.unwrap().display_name, "Bob");
36
37 let missing = repo.find_by_email("nobody@example.com").await.expect("find");
38 assert!(missing.is_none(), "unknown email must return None");
39 }
40
41 #[tokio::test]
42 async fn test_find_by_email_is_case_insensitive() {
43 let pool = common::setup_test_db().await;
44 let repo = SqliteUserRepository::new(pool);
45
46 let created = repo.create("carol@example.com", "passwordpassword", "Carol").await.expect("create");
47
48 let found = repo.find_by_email("CAROL@EXAMPLE.COM").await.expect("find").expect("should match despite case");
49 assert_eq!(found.id, created.id, "lookup must fold case to match the stored lowercase email");
50 }
51
52 #[tokio::test]
53 async fn test_authenticate_correct_password_succeeds() {
54 let pool = common::setup_test_db().await;
55 let repo = SqliteUserRepository::new(pool);
56
57 let created = repo.create("dave@example.com", "s3cr3t-passphrase", "Dave").await.expect("create");
58 assert!(created.last_login_at.is_none());
59
60 let authed = repo.authenticate("dave@example.com", "s3cr3t-passphrase").await.expect("authenticate");
61 assert!(authed.is_some(), "correct password must authenticate");
62 assert_eq!(authed.unwrap().id, created.id);
63
64 // authenticate() records a successful login timestamp.
65 let after = repo.find_by_email("dave@example.com").await.expect("find").unwrap();
66 assert!(after.last_login_at.is_some(), "successful authentication must stamp last_login_at");
67 }
68
69 #[tokio::test]
70 async fn test_authenticate_wrong_password_fails() {
71 let pool = common::setup_test_db().await;
72 let repo = SqliteUserRepository::new(pool);
73
74 repo.create("erin@example.com", "the-right-password", "Erin").await.expect("create");
75
76 let result = repo.authenticate("erin@example.com", "the-wrong-password").await.expect("authenticate");
77 assert!(result.is_none(), "wrong password must not authenticate");
78
79 // A failed login must not stamp last_login_at.
80 let after = repo.find_by_email("erin@example.com").await.expect("find").unwrap();
81 assert!(after.last_login_at.is_none(), "failed authentication must leave last_login_at untouched");
82 }
83
84 #[tokio::test]
85 async fn test_authenticate_unknown_email_returns_none() {
86 let pool = common::setup_test_db().await;
87 let repo = SqliteUserRepository::new(pool);
88
89 let result = repo.authenticate("ghost@example.com", "whatever").await.expect("authenticate");
90 assert!(result.is_none(), "authenticating an unknown email must return None, not error");
91 }
92
93 #[tokio::test]
94 async fn test_update_last_login_sets_timestamp() {
95 let pool = common::setup_test_db().await;
96 let repo = SqliteUserRepository::new(pool);
97
98 let user = repo.create("frank@example.com", "frankspassword", "Frank").await.expect("create");
99 assert!(user.last_login_at.is_none());
100
101 repo.update_last_login(user.id).await.expect("update_last_login");
102
103 let refreshed = repo.find_by_email("frank@example.com").await.expect("find").unwrap();
104 assert!(refreshed.last_login_at.is_some(), "update_last_login must persist a timestamp");
105 }
106