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