1use super::*;
2use crate::test_both_dbs;
3use chrono::Utc;
4use pretty_assertions::{assert_eq, assert_ne};
5use std::sync::Arc;
6
7test_both_dbs!(
8 test_get_users,
9 test_get_users_by_ids_postgres,
10 test_get_users_by_ids_sqlite
11);
12
13async fn test_get_users(db: &Arc<Database>) {
14 let mut user_ids = Vec::new();
15 let mut user_metric_ids = Vec::new();
16 for i in 1..=4 {
17 let user = db
18 .create_user(
19 &format!("user{i}@example.com"),
20 false,
21 NewUserParams {
22 github_login: format!("user{i}"),
23 github_user_id: i,
24 },
25 )
26 .await
27 .unwrap();
28 user_ids.push(user.user_id);
29 user_metric_ids.push(user.metrics_id);
30 }
31
32 assert_eq!(
33 db.get_users_by_ids(user_ids.clone())
34 .await
35 .unwrap()
36 .into_iter()
37 .map(|user| (
38 user.id,
39 user.github_login,
40 user.github_user_id,
41 user.email_address
42 ))
43 .collect::<Vec<_>>(),
44 vec![
45 (
46 user_ids[0],
47 "user1".to_string(),
48 1,
49 Some("user1@example.com".to_string()),
50 ),
51 (
52 user_ids[1],
53 "user2".to_string(),
54 2,
55 Some("user2@example.com".to_string()),
56 ),
57 (
58 user_ids[2],
59 "user3".to_string(),
60 3,
61 Some("user3@example.com".to_string()),
62 ),
63 (
64 user_ids[3],
65 "user4".to_string(),
66 4,
67 Some("user4@example.com".to_string()),
68 )
69 ]
70 );
71}
72
73test_both_dbs!(
74 test_get_or_create_user_by_github_account,
75 test_get_or_create_user_by_github_account_postgres,
76 test_get_or_create_user_by_github_account_sqlite
77);
78
79async fn test_get_or_create_user_by_github_account(db: &Arc<Database>) {
80 db.create_user(
81 "user1@example.com",
82 false,
83 NewUserParams {
84 github_login: "login1".into(),
85 github_user_id: 101,
86 },
87 )
88 .await
89 .unwrap();
90 let user_id2 = db
91 .create_user(
92 "user2@example.com",
93 false,
94 NewUserParams {
95 github_login: "login2".into(),
96 github_user_id: 102,
97 },
98 )
99 .await
100 .unwrap()
101 .user_id;
102
103 let user = db
104 .get_or_create_user_by_github_account("the-new-login2", 102, None, Utc::now(), None)
105 .await
106 .unwrap();
107 assert_eq!(user.id, user_id2);
108 assert_eq!(&user.github_login, "the-new-login2");
109 assert_eq!(user.github_user_id, 102);
110
111 let user = db
112 .get_or_create_user_by_github_account(
113 "login3",
114 103,
115 Some("user3@example.com"),
116 Utc::now(),
117 None,
118 )
119 .await
120 .unwrap();
121 assert_eq!(&user.github_login, "login3");
122 assert_eq!(user.github_user_id, 103);
123 assert_eq!(user.email_address, Some("user3@example.com".into()));
124}
125
126test_both_dbs!(
127 test_create_access_tokens,
128 test_create_access_tokens_postgres,
129 test_create_access_tokens_sqlite
130);
131
132async fn test_create_access_tokens(db: &Arc<Database>) {
133 let user_1 = db
134 .create_user(
135 "u1@example.com",
136 false,
137 NewUserParams {
138 github_login: "u1".into(),
139 github_user_id: 1,
140 },
141 )
142 .await
143 .unwrap()
144 .user_id;
145 let user_2 = db
146 .create_user(
147 "u2@example.com",
148 false,
149 NewUserParams {
150 github_login: "u2".into(),
151 github_user_id: 2,
152 },
153 )
154 .await
155 .unwrap()
156 .user_id;
157
158 let token_1 = db.create_access_token(user_1, None, "h1", 2).await.unwrap();
159 let token_2 = db.create_access_token(user_1, None, "h2", 2).await.unwrap();
160 assert_eq!(
161 db.get_access_token(token_1).await.unwrap(),
162 access_token::Model {
163 id: token_1,
164 user_id: user_1,
165 impersonated_user_id: None,
166 hash: "h1".into(),
167 }
168 );
169 assert_eq!(
170 db.get_access_token(token_2).await.unwrap(),
171 access_token::Model {
172 id: token_2,
173 user_id: user_1,
174 impersonated_user_id: None,
175 hash: "h2".into()
176 }
177 );
178
179 let token_3 = db.create_access_token(user_1, None, "h3", 2).await.unwrap();
180 assert_eq!(
181 db.get_access_token(token_3).await.unwrap(),
182 access_token::Model {
183 id: token_3,
184 user_id: user_1,
185 impersonated_user_id: None,
186 hash: "h3".into()
187 }
188 );
189 assert_eq!(
190 db.get_access_token(token_2).await.unwrap(),
191 access_token::Model {
192 id: token_2,
193 user_id: user_1,
194 impersonated_user_id: None,
195 hash: "h2".into()
196 }
197 );
198 assert!(db.get_access_token(token_1).await.is_err());
199
200 let token_4 = db.create_access_token(user_1, None, "h4", 2).await.unwrap();
201 assert_eq!(
202 db.get_access_token(token_4).await.unwrap(),
203 access_token::Model {
204 id: token_4,
205 user_id: user_1,
206 impersonated_user_id: None,
207 hash: "h4".into()
208 }
209 );
210 assert_eq!(
211 db.get_access_token(token_3).await.unwrap(),
212 access_token::Model {
213 id: token_3,
214 user_id: user_1,
215 impersonated_user_id: None,
216 hash: "h3".into()
217 }
218 );
219 assert!(db.get_access_token(token_2).await.is_err());
220 assert!(db.get_access_token(token_1).await.is_err());
221
222 // An access token for user 2 impersonating user 1 does not
223 // count against user 1's access token limit (of 2).
224 let token_5 = db
225 .create_access_token(user_2, Some(user_1), "h5", 2)
226 .await
227 .unwrap();
228 assert_eq!(
229 db.get_access_token(token_5).await.unwrap(),
230 access_token::Model {
231 id: token_5,
232 user_id: user_2,
233 impersonated_user_id: Some(user_1),
234 hash: "h5".into()
235 }
236 );
237 assert_eq!(
238 db.get_access_token(token_3).await.unwrap(),
239 access_token::Model {
240 id: token_3,
241 user_id: user_1,
242 impersonated_user_id: None,
243 hash: "h3".into()
244 }
245 );
246
247 // Only a limited number (2) of access tokens are stored for user 2
248 // impersonating other users.
249 let token_6 = db
250 .create_access_token(user_2, Some(user_1), "h6", 2)
251 .await
252 .unwrap();
253 let token_7 = db
254 .create_access_token(user_2, Some(user_1), "h7", 2)
255 .await
256 .unwrap();
257 assert_eq!(
258 db.get_access_token(token_6).await.unwrap(),
259 access_token::Model {
260 id: token_6,
261 user_id: user_2,
262 impersonated_user_id: Some(user_1),
263 hash: "h6".into()
264 }
265 );
266 assert_eq!(
267 db.get_access_token(token_7).await.unwrap(),
268 access_token::Model {
269 id: token_7,
270 user_id: user_2,
271 impersonated_user_id: Some(user_1),
272 hash: "h7".into()
273 }
274 );
275 assert!(db.get_access_token(token_5).await.is_err());
276 assert_eq!(
277 db.get_access_token(token_3).await.unwrap(),
278 access_token::Model {
279 id: token_3,
280 user_id: user_1,
281 impersonated_user_id: None,
282 hash: "h3".into()
283 }
284 );
285}
286
287test_both_dbs!(
288 test_add_contacts,
289 test_add_contacts_postgres,
290 test_add_contacts_sqlite
291);
292
293async fn test_add_contacts(db: &Arc<Database>) {
294 let mut user_ids = Vec::new();
295 for i in 0..3 {
296 user_ids.push(
297 db.create_user(
298 &format!("user{i}@example.com"),
299 false,
300 NewUserParams {
301 github_login: format!("user{i}"),
302 github_user_id: i,
303 },
304 )
305 .await
306 .unwrap()
307 .user_id,
308 );
309 }
310
311 let user_1 = user_ids[0];
312 let user_2 = user_ids[1];
313 let user_3 = user_ids[2];
314
315 // User starts with no contacts
316 assert_eq!(db.get_contacts(user_1).await.unwrap(), &[]);
317
318 // User requests a contact. Both users see the pending request.
319 db.send_contact_request(user_1, user_2).await.unwrap();
320 assert!(!db.has_contact(user_1, user_2).await.unwrap());
321 assert!(!db.has_contact(user_2, user_1).await.unwrap());
322 assert_eq!(
323 db.get_contacts(user_1).await.unwrap(),
324 &[Contact::Outgoing { user_id: user_2 }],
325 );
326 assert_eq!(
327 db.get_contacts(user_2).await.unwrap(),
328 &[Contact::Incoming { user_id: user_1 }]
329 );
330
331 // User 2 dismisses the contact request notification without accepting or rejecting.
332 // We shouldn't notify them again.
333 db.dismiss_contact_notification(user_1, user_2)
334 .await
335 .unwrap_err();
336 db.dismiss_contact_notification(user_2, user_1)
337 .await
338 .unwrap();
339 assert_eq!(
340 db.get_contacts(user_2).await.unwrap(),
341 &[Contact::Incoming { user_id: user_1 }]
342 );
343
344 // User can't accept their own contact request
345 db.respond_to_contact_request(user_1, user_2, true)
346 .await
347 .unwrap_err();
348
349 // User accepts a contact request. Both users see the contact.
350 db.respond_to_contact_request(user_2, user_1, true)
351 .await
352 .unwrap();
353 assert_eq!(
354 db.get_contacts(user_1).await.unwrap(),
355 &[Contact::Accepted {
356 user_id: user_2,
357 busy: false,
358 }],
359 );
360 assert!(db.has_contact(user_1, user_2).await.unwrap());
361 assert!(db.has_contact(user_2, user_1).await.unwrap());
362 assert_eq!(
363 db.get_contacts(user_2).await.unwrap(),
364 &[Contact::Accepted {
365 user_id: user_1,
366 busy: false,
367 }]
368 );
369
370 // Users cannot re-request existing contacts.
371 db.send_contact_request(user_1, user_2).await.unwrap_err();
372 db.send_contact_request(user_2, user_1).await.unwrap_err();
373
374 // Users can't dismiss notifications of them accepting other users' requests.
375 db.dismiss_contact_notification(user_2, user_1)
376 .await
377 .unwrap_err();
378 assert_eq!(
379 db.get_contacts(user_1).await.unwrap(),
380 &[Contact::Accepted {
381 user_id: user_2,
382 busy: false,
383 }]
384 );
385
386 // Users can dismiss notifications of other users accepting their requests.
387 db.dismiss_contact_notification(user_1, user_2)
388 .await
389 .unwrap();
390 assert_eq!(
391 db.get_contacts(user_1).await.unwrap(),
392 &[Contact::Accepted {
393 user_id: user_2,
394 busy: false,
395 }]
396 );
397
398 // Users send each other concurrent contact requests and
399 // see that they are immediately accepted.
400 db.send_contact_request(user_1, user_3).await.unwrap();
401 db.send_contact_request(user_3, user_1).await.unwrap();
402 assert_eq!(
403 db.get_contacts(user_1).await.unwrap(),
404 &[
405 Contact::Accepted {
406 user_id: user_2,
407 busy: false,
408 },
409 Contact::Accepted {
410 user_id: user_3,
411 busy: false,
412 }
413 ]
414 );
415 assert_eq!(
416 db.get_contacts(user_3).await.unwrap(),
417 &[Contact::Accepted {
418 user_id: user_1,
419 busy: false,
420 }],
421 );
422
423 // User declines a contact request. Both users see that it is gone.
424 db.send_contact_request(user_2, user_3).await.unwrap();
425 db.respond_to_contact_request(user_3, user_2, false)
426 .await
427 .unwrap();
428 assert!(!db.has_contact(user_2, user_3).await.unwrap());
429 assert!(!db.has_contact(user_3, user_2).await.unwrap());
430 assert_eq!(
431 db.get_contacts(user_2).await.unwrap(),
432 &[Contact::Accepted {
433 user_id: user_1,
434 busy: false,
435 }]
436 );
437 assert_eq!(
438 db.get_contacts(user_3).await.unwrap(),
439 &[Contact::Accepted {
440 user_id: user_1,
441 busy: false,
442 }],
443 );
444}
445
446test_both_dbs!(
447 test_metrics_id,
448 test_metrics_id_postgres,
449 test_metrics_id_sqlite
450);
451
452async fn test_metrics_id(db: &Arc<Database>) {
453 let NewUserResult {
454 user_id: user1,
455 metrics_id: metrics_id1,
456 ..
457 } = db
458 .create_user(
459 "person1@example.com",
460 false,
461 NewUserParams {
462 github_login: "person1".into(),
463 github_user_id: 101,
464 },
465 )
466 .await
467 .unwrap();
468 let NewUserResult {
469 user_id: user2,
470 metrics_id: metrics_id2,
471 ..
472 } = db
473 .create_user(
474 "person2@example.com",
475 false,
476 NewUserParams {
477 github_login: "person2".into(),
478 github_user_id: 102,
479 },
480 )
481 .await
482 .unwrap();
483
484 assert_eq!(db.get_user_metrics_id(user1).await.unwrap(), metrics_id1);
485 assert_eq!(db.get_user_metrics_id(user2).await.unwrap(), metrics_id2);
486 assert_eq!(metrics_id1.len(), 36);
487 assert_eq!(metrics_id2.len(), 36);
488 assert_ne!(metrics_id1, metrics_id2);
489}
490
491test_both_dbs!(
492 test_project_count,
493 test_project_count_postgres,
494 test_project_count_sqlite
495);
496
497async fn test_project_count(db: &Arc<Database>) {
498 let owner_id = db.create_server("test").await.unwrap().0 as u32;
499
500 let user1 = db
501 .create_user(
502 "admin@example.com",
503 true,
504 NewUserParams {
505 github_login: "admin".into(),
506 github_user_id: 0,
507 },
508 )
509 .await
510 .unwrap();
511 let user2 = db
512 .create_user(
513 "user@example.com",
514 false,
515 NewUserParams {
516 github_login: "user".into(),
517 github_user_id: 1,
518 },
519 )
520 .await
521 .unwrap();
522
523 let room_id = RoomId::from_proto(
524 db.create_room(user1.user_id, ConnectionId { owner_id, id: 0 }, "")
525 .await
526 .unwrap()
527 .id,
528 );
529 db.call(
530 room_id,
531 user1.user_id,
532 ConnectionId { owner_id, id: 0 },
533 user2.user_id,
534 None,
535 )
536 .await
537 .unwrap();
538 db.join_room(room_id, user2.user_id, ConnectionId { owner_id, id: 1 })
539 .await
540 .unwrap();
541 assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0);
542
543 db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false)
544 .await
545 .unwrap();
546 assert_eq!(db.project_count_excluding_admins().await.unwrap(), 1);
547
548 db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false)
549 .await
550 .unwrap();
551 assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2);
552
553 // Projects shared by admins aren't counted.
554 db.share_project(room_id, ConnectionId { owner_id, id: 0 }, &[], false)
555 .await
556 .unwrap();
557 assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2);
558
559 db.leave_room(ConnectionId { owner_id, id: 1 })
560 .await
561 .unwrap();
562 assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0);
563}
564
565#[test]
566fn test_fuzzy_like_string() {
567 assert_eq!(Database::fuzzy_like_string("abcd"), "%a%b%c%d%");
568 assert_eq!(Database::fuzzy_like_string("x y"), "%x%y%");
569 assert_eq!(Database::fuzzy_like_string(" z "), "%z%");
570}
571
572#[cfg(target_os = "macos")]
573#[gpui::test]
574async fn test_fuzzy_search_users(cx: &mut gpui::TestAppContext) {
575 let test_db = tests::TestDb::postgres(cx.executor());
576 let db = test_db.db();
577 for (i, github_login) in [
578 "California",
579 "colorado",
580 "oregon",
581 "washington",
582 "florida",
583 "delaware",
584 "rhode-island",
585 ]
586 .into_iter()
587 .enumerate()
588 {
589 db.create_user(
590 &format!("{github_login}@example.com"),
591 false,
592 NewUserParams {
593 github_login: github_login.into(),
594 github_user_id: i as i32,
595 },
596 )
597 .await
598 .unwrap();
599 }
600
601 assert_eq!(
602 fuzzy_search_user_names(db, "clr").await,
603 &["colorado", "California"]
604 );
605 assert_eq!(
606 fuzzy_search_user_names(db, "ro").await,
607 &["rhode-island", "colorado", "oregon"],
608 );
609
610 async fn fuzzy_search_user_names(db: &Database, query: &str) -> Vec<String> {
611 db.fuzzy_search_users(query, 10)
612 .await
613 .unwrap()
614 .into_iter()
615 .map(|user| user.github_login)
616 .collect::<Vec<_>>()
617 }
618}