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