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