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