channel_tests.rs

  1use collections::{HashMap, HashSet};
  2use rpc::{
  3    proto::{self},
  4    ConnectionId,
  5};
  6
  7use crate::{
  8    db::{
  9        queries::channels::ChannelGraph,
 10        tests::{graph, TEST_RELEASE_CHANNEL},
 11        ChannelId, ChannelRole, Database, NewUserParams, UserId,
 12    },
 13    test_both_dbs,
 14};
 15use std::sync::{
 16    atomic::{AtomicI32, Ordering},
 17    Arc,
 18};
 19
 20test_both_dbs!(test_channels, test_channels_postgres, test_channels_sqlite);
 21
 22async fn test_channels(db: &Arc<Database>) {
 23    let a_id = db
 24        .create_user(
 25            "user1@example.com",
 26            false,
 27            NewUserParams {
 28                github_login: "user1".into(),
 29                github_user_id: 5,
 30                invite_count: 0,
 31            },
 32        )
 33        .await
 34        .unwrap()
 35        .user_id;
 36
 37    let b_id = db
 38        .create_user(
 39            "user2@example.com",
 40            false,
 41            NewUserParams {
 42                github_login: "user2".into(),
 43                github_user_id: 6,
 44                invite_count: 0,
 45            },
 46        )
 47        .await
 48        .unwrap()
 49        .user_id;
 50
 51    let zed_id = db.create_root_channel("zed", a_id).await.unwrap();
 52
 53    // Make sure that people cannot read channels they haven't been invited to
 54    assert!(db.get_channel(zed_id, b_id).await.unwrap().is_none());
 55
 56    db.invite_channel_member(zed_id, b_id, a_id, ChannelRole::Member)
 57        .await
 58        .unwrap();
 59
 60    db.respond_to_channel_invite(zed_id, b_id, true)
 61        .await
 62        .unwrap();
 63
 64    let crdb_id = db.create_channel("crdb", Some(zed_id), a_id).await.unwrap();
 65    let livestreaming_id = db
 66        .create_channel("livestreaming", Some(zed_id), a_id)
 67        .await
 68        .unwrap();
 69    let replace_id = db
 70        .create_channel("replace", Some(zed_id), a_id)
 71        .await
 72        .unwrap();
 73
 74    let mut members = db.get_channel_members(replace_id).await.unwrap();
 75    members.sort();
 76    assert_eq!(members, &[a_id, b_id]);
 77
 78    let rust_id = db.create_root_channel("rust", a_id).await.unwrap();
 79    let cargo_id = db
 80        .create_channel("cargo", Some(rust_id), a_id)
 81        .await
 82        .unwrap();
 83
 84    let cargo_ra_id = db
 85        .create_channel("cargo-ra", Some(cargo_id), a_id)
 86        .await
 87        .unwrap();
 88
 89    let result = db.get_channels_for_user(a_id).await.unwrap();
 90    assert_eq!(
 91        result.channels,
 92        graph(
 93            &[
 94                (zed_id, "zed"),
 95                (crdb_id, "crdb"),
 96                (livestreaming_id, "livestreaming"),
 97                (replace_id, "replace"),
 98                (rust_id, "rust"),
 99                (cargo_id, "cargo"),
100                (cargo_ra_id, "cargo-ra")
101            ],
102            &[
103                (crdb_id, zed_id),
104                (livestreaming_id, zed_id),
105                (replace_id, zed_id),
106                (cargo_id, rust_id),
107                (cargo_ra_id, cargo_id),
108            ]
109        )
110    );
111
112    let result = db.get_channels_for_user(b_id).await.unwrap();
113    assert_eq!(
114        result.channels,
115        graph(
116            &[
117                (zed_id, "zed"),
118                (crdb_id, "crdb"),
119                (livestreaming_id, "livestreaming"),
120                (replace_id, "replace")
121            ],
122            &[
123                (crdb_id, zed_id),
124                (livestreaming_id, zed_id),
125                (replace_id, zed_id)
126            ]
127        )
128    );
129
130    // Update member permissions
131    let set_subchannel_admin = db
132        .set_channel_member_role(crdb_id, a_id, b_id, ChannelRole::Admin)
133        .await;
134    assert!(set_subchannel_admin.is_err());
135    let set_channel_admin = db
136        .set_channel_member_role(zed_id, a_id, b_id, ChannelRole::Admin)
137        .await;
138    assert!(set_channel_admin.is_ok());
139
140    let result = db.get_channels_for_user(b_id).await.unwrap();
141    assert_eq!(
142        result.channels,
143        graph(
144            &[
145                (zed_id, "zed"),
146                (crdb_id, "crdb"),
147                (livestreaming_id, "livestreaming"),
148                (replace_id, "replace")
149            ],
150            &[
151                (crdb_id, zed_id),
152                (livestreaming_id, zed_id),
153                (replace_id, zed_id)
154            ]
155        )
156    );
157
158    // Remove a single channel
159    db.delete_channel(crdb_id, a_id).await.unwrap();
160    assert!(db.get_channel(crdb_id, a_id).await.unwrap().is_none());
161
162    // Remove a channel tree
163    let (mut channel_ids, user_ids) = db.delete_channel(rust_id, a_id).await.unwrap();
164    channel_ids.sort();
165    assert_eq!(channel_ids, &[rust_id, cargo_id, cargo_ra_id]);
166    assert_eq!(user_ids, &[a_id]);
167
168    assert!(db.get_channel(rust_id, a_id).await.unwrap().is_none());
169    assert!(db.get_channel(cargo_id, a_id).await.unwrap().is_none());
170    assert!(db.get_channel(cargo_ra_id, a_id).await.unwrap().is_none());
171}
172
173test_both_dbs!(
174    test_joining_channels,
175    test_joining_channels_postgres,
176    test_joining_channels_sqlite
177);
178
179async fn test_joining_channels(db: &Arc<Database>) {
180    let owner_id = db.create_server("test").await.unwrap().0 as u32;
181
182    let user_1 = db
183        .create_user(
184            "user1@example.com",
185            false,
186            NewUserParams {
187                github_login: "user1".into(),
188                github_user_id: 5,
189                invite_count: 0,
190            },
191        )
192        .await
193        .unwrap()
194        .user_id;
195    let user_2 = db
196        .create_user(
197            "user2@example.com",
198            false,
199            NewUserParams {
200                github_login: "user2".into(),
201                github_user_id: 6,
202                invite_count: 0,
203            },
204        )
205        .await
206        .unwrap()
207        .user_id;
208
209    let channel_1 = db.create_root_channel("channel_1", user_1).await.unwrap();
210    let room_1 = db
211        .get_or_create_channel_room(channel_1, "1", TEST_RELEASE_CHANNEL)
212        .await
213        .unwrap();
214
215    // can join a room with membership to its channel
216    let joined_room = db
217        .join_room(
218            room_1,
219            user_1,
220            ConnectionId { owner_id, id: 1 },
221            TEST_RELEASE_CHANNEL,
222        )
223        .await
224        .unwrap();
225    assert_eq!(joined_room.room.participants.len(), 1);
226
227    drop(joined_room);
228    // cannot join a room without membership to its channel
229    assert!(db
230        .join_room(
231            room_1,
232            user_2,
233            ConnectionId { owner_id, id: 1 },
234            TEST_RELEASE_CHANNEL
235        )
236        .await
237        .is_err());
238}
239
240test_both_dbs!(
241    test_channel_invites,
242    test_channel_invites_postgres,
243    test_channel_invites_sqlite
244);
245
246async fn test_channel_invites(db: &Arc<Database>) {
247    db.create_server("test").await.unwrap();
248
249    let user_1 = db
250        .create_user(
251            "user1@example.com",
252            false,
253            NewUserParams {
254                github_login: "user1".into(),
255                github_user_id: 5,
256                invite_count: 0,
257            },
258        )
259        .await
260        .unwrap()
261        .user_id;
262    let user_2 = db
263        .create_user(
264            "user2@example.com",
265            false,
266            NewUserParams {
267                github_login: "user2".into(),
268                github_user_id: 6,
269                invite_count: 0,
270            },
271        )
272        .await
273        .unwrap()
274        .user_id;
275
276    let user_3 = db
277        .create_user(
278            "user3@example.com",
279            false,
280            NewUserParams {
281                github_login: "user3".into(),
282                github_user_id: 7,
283                invite_count: 0,
284            },
285        )
286        .await
287        .unwrap()
288        .user_id;
289
290    let channel_1_1 = db.create_root_channel("channel_1", user_1).await.unwrap();
291
292    let channel_1_2 = db.create_root_channel("channel_2", user_1).await.unwrap();
293
294    db.invite_channel_member(channel_1_1, user_2, user_1, ChannelRole::Member)
295        .await
296        .unwrap();
297    db.invite_channel_member(channel_1_2, user_2, user_1, ChannelRole::Member)
298        .await
299        .unwrap();
300    db.invite_channel_member(channel_1_1, user_3, user_1, ChannelRole::Admin)
301        .await
302        .unwrap();
303
304    let user_2_invites = db
305        .get_channel_invites_for_user(user_2) // -> [channel_1_1, channel_1_2]
306        .await
307        .unwrap()
308        .into_iter()
309        .map(|channel| channel.id)
310        .collect::<Vec<_>>();
311
312    assert_eq!(user_2_invites, &[channel_1_1, channel_1_2]);
313
314    let user_3_invites = db
315        .get_channel_invites_for_user(user_3) // -> [channel_1_1]
316        .await
317        .unwrap()
318        .into_iter()
319        .map(|channel| channel.id)
320        .collect::<Vec<_>>();
321
322    assert_eq!(user_3_invites, &[channel_1_1]);
323
324    let members = db
325        .get_channel_member_details(channel_1_1, user_1)
326        .await
327        .unwrap();
328    assert_eq!(
329        members,
330        &[
331            proto::ChannelMember {
332                user_id: user_1.to_proto(),
333                kind: proto::channel_member::Kind::Member.into(),
334                role: proto::ChannelRole::Admin.into(),
335            },
336            proto::ChannelMember {
337                user_id: user_2.to_proto(),
338                kind: proto::channel_member::Kind::Invitee.into(),
339                role: proto::ChannelRole::Member.into(),
340            },
341            proto::ChannelMember {
342                user_id: user_3.to_proto(),
343                kind: proto::channel_member::Kind::Invitee.into(),
344                role: proto::ChannelRole::Admin.into(),
345            },
346        ]
347    );
348
349    db.respond_to_channel_invite(channel_1_1, user_2, true)
350        .await
351        .unwrap();
352
353    let channel_1_3 = db
354        .create_channel("channel_3", Some(channel_1_1), user_1)
355        .await
356        .unwrap();
357
358    let members = db
359        .get_channel_member_details(channel_1_3, user_1)
360        .await
361        .unwrap();
362    assert_eq!(
363        members,
364        &[
365            proto::ChannelMember {
366                user_id: user_1.to_proto(),
367                kind: proto::channel_member::Kind::Member.into(),
368                role: proto::ChannelRole::Admin.into(),
369            },
370            proto::ChannelMember {
371                user_id: user_2.to_proto(),
372                kind: proto::channel_member::Kind::AncestorMember.into(),
373                role: proto::ChannelRole::Member.into(),
374            },
375        ]
376    );
377}
378
379test_both_dbs!(
380    test_channel_renames,
381    test_channel_renames_postgres,
382    test_channel_renames_sqlite
383);
384
385async fn test_channel_renames(db: &Arc<Database>) {
386    db.create_server("test").await.unwrap();
387
388    let user_1 = db
389        .create_user(
390            "user1@example.com",
391            false,
392            NewUserParams {
393                github_login: "user1".into(),
394                github_user_id: 5,
395                invite_count: 0,
396            },
397        )
398        .await
399        .unwrap()
400        .user_id;
401
402    let user_2 = db
403        .create_user(
404            "user2@example.com",
405            false,
406            NewUserParams {
407                github_login: "user2".into(),
408                github_user_id: 6,
409                invite_count: 0,
410            },
411        )
412        .await
413        .unwrap()
414        .user_id;
415
416    let zed_id = db.create_root_channel("zed", user_1).await.unwrap();
417
418    db.rename_channel(zed_id, user_1, "#zed-archive")
419        .await
420        .unwrap();
421
422    let zed_archive_id = zed_id;
423
424    let (channel, _) = db
425        .get_channel(zed_archive_id, user_1)
426        .await
427        .unwrap()
428        .unwrap();
429    assert_eq!(channel.name, "zed-archive");
430
431    let non_permissioned_rename = db
432        .rename_channel(zed_archive_id, user_2, "hacked-lol")
433        .await;
434    assert!(non_permissioned_rename.is_err());
435
436    let bad_name_rename = db.rename_channel(zed_id, user_1, "#").await;
437    assert!(bad_name_rename.is_err())
438}
439
440test_both_dbs!(
441    test_db_channel_moving,
442    test_channels_moving_postgres,
443    test_channels_moving_sqlite
444);
445
446async fn test_db_channel_moving(db: &Arc<Database>) {
447    let a_id = db
448        .create_user(
449            "user1@example.com",
450            false,
451            NewUserParams {
452                github_login: "user1".into(),
453                github_user_id: 5,
454                invite_count: 0,
455            },
456        )
457        .await
458        .unwrap()
459        .user_id;
460
461    let zed_id = db.create_root_channel("zed", a_id).await.unwrap();
462
463    let crdb_id = db.create_channel("crdb", Some(zed_id), a_id).await.unwrap();
464
465    let gpui2_id = db
466        .create_channel("gpui2", Some(zed_id), a_id)
467        .await
468        .unwrap();
469
470    let livestreaming_id = db
471        .create_channel("livestreaming", Some(crdb_id), a_id)
472        .await
473        .unwrap();
474
475    let livestreaming_dag_id = db
476        .create_channel("livestreaming_dag", Some(livestreaming_id), a_id)
477        .await
478        .unwrap();
479
480    // ========================================================================
481    // sanity check
482    // Initial DAG:
483    //     /- gpui2
484    // zed -- crdb - livestreaming - livestreaming_dag
485    let result = db.get_channels_for_user(a_id).await.unwrap();
486    assert_dag(
487        result.channels,
488        &[
489            (zed_id, None),
490            (crdb_id, Some(zed_id)),
491            (gpui2_id, Some(zed_id)),
492            (livestreaming_id, Some(crdb_id)),
493            (livestreaming_dag_id, Some(livestreaming_id)),
494        ],
495    );
496
497    // Attempt to make a cycle
498    assert!(db
499        .link_channel(a_id, zed_id, livestreaming_id)
500        .await
501        .is_err());
502
503    // ========================================================================
504    // Make a link
505    db.link_channel(a_id, livestreaming_id, zed_id)
506        .await
507        .unwrap();
508
509    // DAG is now:
510    //     /- gpui2
511    // zed -- crdb - livestreaming - livestreaming_dag
512    //    \---------/
513    let result = db.get_channels_for_user(a_id).await.unwrap();
514    assert_dag(
515        result.channels,
516        &[
517            (zed_id, None),
518            (crdb_id, Some(zed_id)),
519            (gpui2_id, Some(zed_id)),
520            (livestreaming_id, Some(zed_id)),
521            (livestreaming_id, Some(crdb_id)),
522            (livestreaming_dag_id, Some(livestreaming_id)),
523        ],
524    );
525
526    // ========================================================================
527    // Create a new channel below a channel with multiple parents
528    let livestreaming_dag_sub_id = db
529        .create_channel("livestreaming_dag_sub", Some(livestreaming_dag_id), a_id)
530        .await
531        .unwrap();
532
533    // DAG is now:
534    //     /- gpui2
535    // zed -- crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
536    //    \---------/
537    let result = db.get_channels_for_user(a_id).await.unwrap();
538    assert_dag(
539        result.channels,
540        &[
541            (zed_id, None),
542            (crdb_id, Some(zed_id)),
543            (gpui2_id, Some(zed_id)),
544            (livestreaming_id, Some(zed_id)),
545            (livestreaming_id, Some(crdb_id)),
546            (livestreaming_dag_id, Some(livestreaming_id)),
547            (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
548        ],
549    );
550
551    // ========================================================================
552    // Test a complex DAG by making another link
553    let returned_channels = db
554        .link_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
555        .await
556        .unwrap();
557
558    // DAG is now:
559    //    /- gpui2                /---------------------\
560    // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
561    //    \--------/
562
563    // make sure we're getting just the new link
564    // Not using the assert_dag helper because we want to make sure we're returning the full data
565    pretty_assertions::assert_eq!(
566        returned_channels,
567        graph(
568            &[(livestreaming_dag_sub_id, "livestreaming_dag_sub")],
569            &[(livestreaming_dag_sub_id, livestreaming_id)]
570        )
571    );
572
573    let result = db.get_channels_for_user(a_id).await.unwrap();
574    assert_dag(
575        result.channels,
576        &[
577            (zed_id, None),
578            (crdb_id, Some(zed_id)),
579            (gpui2_id, Some(zed_id)),
580            (livestreaming_id, Some(zed_id)),
581            (livestreaming_id, Some(crdb_id)),
582            (livestreaming_dag_id, Some(livestreaming_id)),
583            (livestreaming_dag_sub_id, Some(livestreaming_id)),
584            (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
585        ],
586    );
587
588    // ========================================================================
589    // Test a complex DAG by making another link
590    let returned_channels = db
591        .link_channel(a_id, livestreaming_id, gpui2_id)
592        .await
593        .unwrap();
594
595    // DAG is now:
596    //    /- gpui2 -\             /---------------------\
597    // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub_id
598    //    \---------/
599
600    // Make sure that we're correctly getting the full sub-dag
601    pretty_assertions::assert_eq!(
602        returned_channels,
603        graph(
604            &[
605                (livestreaming_id, "livestreaming"),
606                (livestreaming_dag_id, "livestreaming_dag"),
607                (livestreaming_dag_sub_id, "livestreaming_dag_sub"),
608            ],
609            &[
610                (livestreaming_id, gpui2_id),
611                (livestreaming_dag_id, livestreaming_id),
612                (livestreaming_dag_sub_id, livestreaming_id),
613                (livestreaming_dag_sub_id, livestreaming_dag_id),
614            ]
615        )
616    );
617
618    let result = db.get_channels_for_user(a_id).await.unwrap();
619    assert_dag(
620        result.channels,
621        &[
622            (zed_id, None),
623            (crdb_id, Some(zed_id)),
624            (gpui2_id, Some(zed_id)),
625            (livestreaming_id, Some(zed_id)),
626            (livestreaming_id, Some(crdb_id)),
627            (livestreaming_id, Some(gpui2_id)),
628            (livestreaming_dag_id, Some(livestreaming_id)),
629            (livestreaming_dag_sub_id, Some(livestreaming_id)),
630            (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
631        ],
632    );
633
634    // ========================================================================
635    // Test unlinking in a complex DAG by removing the inner link
636    db.unlink_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
637        .await
638        .unwrap();
639
640    // DAG is now:
641    //    /- gpui2 -\
642    // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
643    //    \---------/
644
645    let result = db.get_channels_for_user(a_id).await.unwrap();
646    assert_dag(
647        result.channels,
648        &[
649            (zed_id, None),
650            (crdb_id, Some(zed_id)),
651            (gpui2_id, Some(zed_id)),
652            (livestreaming_id, Some(gpui2_id)),
653            (livestreaming_id, Some(zed_id)),
654            (livestreaming_id, Some(crdb_id)),
655            (livestreaming_dag_id, Some(livestreaming_id)),
656            (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
657        ],
658    );
659
660    // ========================================================================
661    // Test unlinking in a complex DAG by removing the inner link
662    db.unlink_channel(a_id, livestreaming_id, gpui2_id)
663        .await
664        .unwrap();
665
666    // DAG is now:
667    //    /- gpui2
668    // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
669    //    \---------/
670    let result = db.get_channels_for_user(a_id).await.unwrap();
671    assert_dag(
672        result.channels,
673        &[
674            (zed_id, None),
675            (crdb_id, Some(zed_id)),
676            (gpui2_id, Some(zed_id)),
677            (livestreaming_id, Some(zed_id)),
678            (livestreaming_id, Some(crdb_id)),
679            (livestreaming_dag_id, Some(livestreaming_id)),
680            (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
681        ],
682    );
683
684    // ========================================================================
685    // Test moving DAG nodes by moving livestreaming to be below gpui2
686    db.move_channel(a_id, livestreaming_id, crdb_id, gpui2_id)
687        .await
688        .unwrap();
689
690    // DAG is now:
691    //    /- gpui2 -- livestreaming - livestreaming_dag - livestreaming_dag_sub
692    // zed - crdb    /
693    //    \---------/
694    let result = db.get_channels_for_user(a_id).await.unwrap();
695    assert_dag(
696        result.channels,
697        &[
698            (zed_id, None),
699            (crdb_id, Some(zed_id)),
700            (gpui2_id, Some(zed_id)),
701            (livestreaming_id, Some(zed_id)),
702            (livestreaming_id, Some(gpui2_id)),
703            (livestreaming_dag_id, Some(livestreaming_id)),
704            (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
705        ],
706    );
707
708    // ========================================================================
709    // Deleting a channel should not delete children that still have other parents
710    db.delete_channel(gpui2_id, a_id).await.unwrap();
711
712    // DAG is now:
713    // zed - crdb
714    //    \- livestreaming - livestreaming_dag - livestreaming_dag_sub
715    let result = db.get_channels_for_user(a_id).await.unwrap();
716    assert_dag(
717        result.channels,
718        &[
719            (zed_id, None),
720            (crdb_id, Some(zed_id)),
721            (livestreaming_id, Some(zed_id)),
722            (livestreaming_dag_id, Some(livestreaming_id)),
723            (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
724        ],
725    );
726
727    // ========================================================================
728    // Unlinking a channel from it's parent should automatically promote it to a root channel
729    db.unlink_channel(a_id, crdb_id, zed_id).await.unwrap();
730
731    // DAG is now:
732    // crdb
733    // zed
734    //    \- livestreaming - livestreaming_dag - livestreaming_dag_sub
735
736    let result = db.get_channels_for_user(a_id).await.unwrap();
737    assert_dag(
738        result.channels,
739        &[
740            (zed_id, None),
741            (crdb_id, None),
742            (livestreaming_id, Some(zed_id)),
743            (livestreaming_dag_id, Some(livestreaming_id)),
744            (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
745        ],
746    );
747
748    // ========================================================================
749    // You should be able to move a root channel into a non-root channel
750    db.link_channel(a_id, crdb_id, zed_id).await.unwrap();
751
752    // DAG is now:
753    // zed - crdb
754    //    \- livestreaming - livestreaming_dag - livestreaming_dag_sub
755
756    let result = db.get_channels_for_user(a_id).await.unwrap();
757    assert_dag(
758        result.channels,
759        &[
760            (zed_id, None),
761            (crdb_id, Some(zed_id)),
762            (livestreaming_id, Some(zed_id)),
763            (livestreaming_dag_id, Some(livestreaming_id)),
764            (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
765        ],
766    );
767
768    // ========================================================================
769    // Prep for DAG deletion test
770    db.link_channel(a_id, livestreaming_id, crdb_id)
771        .await
772        .unwrap();
773
774    // DAG is now:
775    // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub
776    //    \--------/
777
778    let result = db.get_channels_for_user(a_id).await.unwrap();
779    assert_dag(
780        result.channels,
781        &[
782            (zed_id, None),
783            (crdb_id, Some(zed_id)),
784            (livestreaming_id, Some(zed_id)),
785            (livestreaming_id, Some(crdb_id)),
786            (livestreaming_dag_id, Some(livestreaming_id)),
787            (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
788        ],
789    );
790
791    // Deleting the parent of a DAG should delete the whole DAG:
792    db.delete_channel(zed_id, a_id).await.unwrap();
793    let result = db.get_channels_for_user(a_id).await.unwrap();
794
795    assert!(result.channels.is_empty())
796}
797
798test_both_dbs!(
799    test_db_channel_moving_bugs,
800    test_db_channel_moving_bugs_postgres,
801    test_db_channel_moving_bugs_sqlite
802);
803
804async fn test_db_channel_moving_bugs(db: &Arc<Database>) {
805    let user_id = db
806        .create_user(
807            "user1@example.com",
808            false,
809            NewUserParams {
810                github_login: "user1".into(),
811                github_user_id: 5,
812                invite_count: 0,
813            },
814        )
815        .await
816        .unwrap()
817        .user_id;
818
819    let zed_id = db.create_root_channel("zed", user_id).await.unwrap();
820
821    let projects_id = db
822        .create_channel("projects", Some(zed_id), user_id)
823        .await
824        .unwrap();
825
826    let livestreaming_id = db
827        .create_channel("livestreaming", Some(projects_id), user_id)
828        .await
829        .unwrap();
830
831    // Dag is: zed - projects - livestreaming
832
833    // Move to same parent should be a no-op
834    assert!(db
835        .move_channel(user_id, projects_id, zed_id, zed_id)
836        .await
837        .unwrap()
838        .is_empty());
839
840    // Stranding a channel should retain it's sub channels
841    db.unlink_channel(user_id, projects_id, zed_id)
842        .await
843        .unwrap();
844
845    let result = db.get_channels_for_user(user_id).await.unwrap();
846    assert_dag(
847        result.channels,
848        &[
849            (zed_id, None),
850            (projects_id, None),
851            (livestreaming_id, Some(projects_id)),
852        ],
853    );
854}
855
856test_both_dbs!(
857    test_user_is_channel_participant,
858    test_user_is_channel_participant_postgres,
859    test_user_is_channel_participant_sqlite
860);
861
862async fn test_user_is_channel_participant(db: &Arc<Database>) {
863    let admin_id = new_test_user(db, "admin@example.com").await;
864    let member_id = new_test_user(db, "member@example.com").await;
865    let guest_id = new_test_user(db, "guest@example.com").await;
866
867    let zed_id = db.create_root_channel("zed", admin_id).await.unwrap();
868    let intermediate_id = db
869        .create_channel("active", Some(zed_id), admin_id)
870        .await
871        .unwrap();
872    let public_id = db
873        .create_channel("active", Some(intermediate_id), admin_id)
874        .await
875        .unwrap();
876
877    db.set_channel_visibility(public_id, crate::db::ChannelVisibility::Public, admin_id)
878        .await
879        .unwrap();
880    db.invite_channel_member(intermediate_id, member_id, admin_id, ChannelRole::Member)
881        .await
882        .unwrap();
883    db.invite_channel_member(public_id, guest_id, admin_id, ChannelRole::Guest)
884        .await
885        .unwrap();
886
887    db.transaction(|tx| async move {
888        db.check_user_is_channel_participant(public_id, admin_id, &*tx)
889            .await
890    })
891    .await
892    .unwrap();
893    db.transaction(|tx| async move {
894        db.check_user_is_channel_participant(public_id, member_id, &*tx)
895            .await
896    })
897    .await
898    .unwrap();
899    db.transaction(|tx| async move {
900        db.check_user_is_channel_participant(public_id, guest_id, &*tx)
901            .await
902    })
903    .await
904    .unwrap();
905
906    db.set_channel_member_role(public_id, admin_id, guest_id, ChannelRole::Banned)
907        .await
908        .unwrap();
909    assert!(db
910        .transaction(|tx| async move {
911            db.check_user_is_channel_participant(public_id, guest_id, &*tx)
912                .await
913        })
914        .await
915        .is_err());
916
917    db.remove_channel_member(public_id, guest_id, admin_id)
918        .await
919        .unwrap();
920
921    db.set_channel_visibility(zed_id, crate::db::ChannelVisibility::Public, admin_id)
922        .await
923        .unwrap();
924
925    db.invite_channel_member(zed_id, guest_id, admin_id, ChannelRole::Guest)
926        .await
927        .unwrap();
928
929    db.transaction(|tx| async move {
930        db.check_user_is_channel_participant(zed_id, guest_id, &*tx)
931            .await
932    })
933    .await
934    .unwrap();
935    assert!(db
936        .transaction(|tx| async move {
937            db.check_user_is_channel_participant(intermediate_id, guest_id, &*tx)
938                .await
939        })
940        .await
941        .is_err(),);
942
943    db.transaction(|tx| async move {
944        db.check_user_is_channel_participant(public_id, guest_id, &*tx)
945            .await
946    })
947    .await
948    .unwrap();
949}
950
951#[track_caller]
952fn assert_dag(actual: ChannelGraph, expected: &[(ChannelId, Option<ChannelId>)]) {
953    let mut actual_map: HashMap<ChannelId, HashSet<ChannelId>> = HashMap::default();
954    for channel in actual.channels {
955        actual_map.insert(channel.id, HashSet::default());
956    }
957    for edge in actual.edges {
958        actual_map
959            .get_mut(&ChannelId::from_proto(edge.channel_id))
960            .unwrap()
961            .insert(ChannelId::from_proto(edge.parent_id));
962    }
963
964    let mut expected_map: HashMap<ChannelId, HashSet<ChannelId>> = HashMap::default();
965
966    for (child, parent) in expected {
967        let entry = expected_map.entry(*child).or_default();
968        if let Some(parent) = parent {
969            entry.insert(*parent);
970        }
971    }
972
973    pretty_assertions::assert_eq!(actual_map, expected_map)
974}
975
976static GITHUB_USER_ID: AtomicI32 = AtomicI32::new(5);
977
978async fn new_test_user(db: &Arc<Database>, email: &str) -> UserId {
979    let gid = GITHUB_USER_ID.fetch_add(1, Ordering::SeqCst);
980
981    db.create_user(
982        email,
983        false,
984        NewUserParams {
985            github_login: email[0..email.find("@").unwrap()].to_string(),
986            github_user_id: GITHUB_USER_ID.fetch_add(1, Ordering::SeqCst),
987            invite_count: 0,
988        },
989    )
990    .await
991    .unwrap()
992    .user_id
993}