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