channel_tests.rs

  1use crate::{
  2    db::{
  3        tests::{channel_tree, new_test_connection, new_test_user, TEST_RELEASE_CHANNEL},
  4        Channel, ChannelId, ChannelRole, Database, NewUserParams, RoomId,
  5    },
  6    test_both_dbs,
  7};
  8use rpc::{
  9    proto::{self},
 10    ConnectionId,
 11};
 12use std::sync::Arc;
 13
 14test_both_dbs!(test_channels, test_channels_postgres, test_channels_sqlite);
 15
 16async fn test_channels(db: &Arc<Database>) {
 17    let a_id = new_test_user(db, "user1@example.com").await;
 18    let b_id = new_test_user(db, "user2@example.com").await;
 19
 20    let zed_id = db.create_root_channel("zed", a_id).await.unwrap();
 21
 22    // Make sure that people cannot read channels they haven't been invited to
 23    assert!(db.get_channel(zed_id, b_id).await.is_err());
 24
 25    db.invite_channel_member(zed_id, b_id, a_id, ChannelRole::Member)
 26        .await
 27        .unwrap();
 28
 29    db.respond_to_channel_invite(zed_id, b_id, true)
 30        .await
 31        .unwrap();
 32
 33    let crdb_id = db.create_sub_channel("crdb", zed_id, a_id).await.unwrap();
 34    let livestreaming_id = db
 35        .create_sub_channel("livestreaming", zed_id, a_id)
 36        .await
 37        .unwrap();
 38    let replace_id = db
 39        .create_sub_channel("replace", zed_id, a_id)
 40        .await
 41        .unwrap();
 42
 43    let mut members = db
 44        .transaction(|tx| async move {
 45            let channel = db.get_channel_internal(replace_id, &*tx).await?;
 46            Ok(db.get_channel_participants(&channel, &*tx).await?)
 47        })
 48        .await
 49        .unwrap();
 50    members.sort();
 51    assert_eq!(members, &[a_id, b_id]);
 52
 53    let rust_id = db.create_root_channel("rust", a_id).await.unwrap();
 54    let cargo_id = db.create_sub_channel("cargo", rust_id, a_id).await.unwrap();
 55
 56    let cargo_ra_id = db
 57        .create_sub_channel("cargo-ra", cargo_id, a_id)
 58        .await
 59        .unwrap();
 60
 61    let result = db.get_channels_for_user(a_id).await.unwrap();
 62    assert_eq!(
 63        result.channels,
 64        channel_tree(&[
 65            (zed_id, &[], "zed", ChannelRole::Admin),
 66            (crdb_id, &[zed_id], "crdb", ChannelRole::Admin),
 67            (
 68                livestreaming_id,
 69                &[zed_id],
 70                "livestreaming",
 71                ChannelRole::Admin
 72            ),
 73            (replace_id, &[zed_id], "replace", ChannelRole::Admin),
 74            (rust_id, &[], "rust", ChannelRole::Admin),
 75            (cargo_id, &[rust_id], "cargo", ChannelRole::Admin),
 76            (
 77                cargo_ra_id,
 78                &[rust_id, cargo_id],
 79                "cargo-ra",
 80                ChannelRole::Admin
 81            )
 82        ],)
 83    );
 84
 85    let result = db.get_channels_for_user(b_id).await.unwrap();
 86    assert_eq!(
 87        result.channels,
 88        channel_tree(&[
 89            (zed_id, &[], "zed", ChannelRole::Member),
 90            (crdb_id, &[zed_id], "crdb", ChannelRole::Member),
 91            (
 92                livestreaming_id,
 93                &[zed_id],
 94                "livestreaming",
 95                ChannelRole::Member
 96            ),
 97            (replace_id, &[zed_id], "replace", ChannelRole::Member)
 98        ],)
 99    );
100
101    // Update member permissions
102    let set_subchannel_admin = db
103        .set_channel_member_role(crdb_id, a_id, b_id, ChannelRole::Admin)
104        .await;
105    assert!(set_subchannel_admin.is_err());
106    let set_channel_admin = db
107        .set_channel_member_role(zed_id, a_id, b_id, ChannelRole::Admin)
108        .await;
109    assert!(set_channel_admin.is_ok());
110
111    let result = db.get_channels_for_user(b_id).await.unwrap();
112    assert_eq!(
113        result.channels,
114        channel_tree(&[
115            (zed_id, &[], "zed", ChannelRole::Admin),
116            (crdb_id, &[zed_id], "crdb", ChannelRole::Admin),
117            (
118                livestreaming_id,
119                &[zed_id],
120                "livestreaming",
121                ChannelRole::Admin
122            ),
123            (replace_id, &[zed_id], "replace", ChannelRole::Admin)
124        ],)
125    );
126
127    // Remove a single channel
128    db.delete_channel(crdb_id, a_id).await.unwrap();
129    assert!(db.get_channel(crdb_id, a_id).await.is_err());
130
131    // Remove a channel tree
132    let (mut channel_ids, user_ids) = db.delete_channel(rust_id, a_id).await.unwrap();
133    channel_ids.sort();
134    assert_eq!(channel_ids, &[rust_id, cargo_id, cargo_ra_id]);
135    assert_eq!(user_ids, &[a_id]);
136
137    assert!(db.get_channel(rust_id, a_id).await.is_err());
138    assert!(db.get_channel(cargo_id, a_id).await.is_err());
139    assert!(db.get_channel(cargo_ra_id, a_id).await.is_err());
140}
141
142test_both_dbs!(
143    test_joining_channels,
144    test_joining_channels_postgres,
145    test_joining_channels_sqlite
146);
147
148async fn test_joining_channels(db: &Arc<Database>) {
149    let owner_id = db.create_server("test").await.unwrap().0 as u32;
150
151    let user_1 = new_test_user(db, "user1@example.com").await;
152    let user_2 = new_test_user(db, "user2@example.com").await;
153
154    let channel_1 = db.create_root_channel("channel_1", user_1).await.unwrap();
155
156    // can join a room with membership to its channel
157    let (joined_room, _, _) = db
158        .join_channel(
159            channel_1,
160            user_1,
161            ConnectionId { owner_id, id: 1 },
162            TEST_RELEASE_CHANNEL,
163        )
164        .await
165        .unwrap();
166    assert_eq!(joined_room.room.participants.len(), 1);
167
168    let room_id = RoomId::from_proto(joined_room.room.id);
169    drop(joined_room);
170    // cannot join a room without membership to its channel
171    assert!(db
172        .join_room(
173            room_id,
174            user_2,
175            ConnectionId { owner_id, id: 1 },
176            TEST_RELEASE_CHANNEL
177        )
178        .await
179        .is_err());
180}
181
182test_both_dbs!(
183    test_channel_invites,
184    test_channel_invites_postgres,
185    test_channel_invites_sqlite
186);
187
188async fn test_channel_invites(db: &Arc<Database>) {
189    db.create_server("test").await.unwrap();
190
191    let user_1 = new_test_user(db, "user1@example.com").await;
192    let user_2 = new_test_user(db, "user2@example.com").await;
193    let user_3 = new_test_user(db, "user3@example.com").await;
194
195    let channel_1_1 = db.create_root_channel("channel_1", user_1).await.unwrap();
196
197    let channel_1_2 = db.create_root_channel("channel_2", user_1).await.unwrap();
198
199    db.invite_channel_member(channel_1_1, user_2, user_1, ChannelRole::Member)
200        .await
201        .unwrap();
202    db.invite_channel_member(channel_1_2, user_2, user_1, ChannelRole::Member)
203        .await
204        .unwrap();
205    db.invite_channel_member(channel_1_1, user_3, user_1, ChannelRole::Admin)
206        .await
207        .unwrap();
208
209    let user_2_invites = db
210        .get_channel_invites_for_user(user_2) // -> [channel_1_1, channel_1_2]
211        .await
212        .unwrap()
213        .into_iter()
214        .map(|channel| channel.id)
215        .collect::<Vec<_>>();
216
217    assert_eq!(user_2_invites, &[channel_1_1, channel_1_2]);
218
219    let user_3_invites = db
220        .get_channel_invites_for_user(user_3) // -> [channel_1_1]
221        .await
222        .unwrap()
223        .into_iter()
224        .map(|channel| channel.id)
225        .collect::<Vec<_>>();
226
227    assert_eq!(user_3_invites, &[channel_1_1]);
228
229    let mut members = db
230        .get_channel_participant_details(channel_1_1, user_1)
231        .await
232        .unwrap();
233
234    members.sort_by_key(|member| member.user_id);
235    assert_eq!(
236        members,
237        &[
238            proto::ChannelMember {
239                user_id: user_1.to_proto(),
240                kind: proto::channel_member::Kind::Member.into(),
241                role: proto::ChannelRole::Admin.into(),
242            },
243            proto::ChannelMember {
244                user_id: user_2.to_proto(),
245                kind: proto::channel_member::Kind::Invitee.into(),
246                role: proto::ChannelRole::Member.into(),
247            },
248            proto::ChannelMember {
249                user_id: user_3.to_proto(),
250                kind: proto::channel_member::Kind::Invitee.into(),
251                role: proto::ChannelRole::Admin.into(),
252            },
253        ]
254    );
255
256    db.respond_to_channel_invite(channel_1_1, user_2, true)
257        .await
258        .unwrap();
259
260    let channel_1_3 = db
261        .create_sub_channel("channel_3", channel_1_1, user_1)
262        .await
263        .unwrap();
264
265    let members = db
266        .get_channel_participant_details(channel_1_3, user_1)
267        .await
268        .unwrap();
269    assert_eq!(
270        members,
271        &[
272            proto::ChannelMember {
273                user_id: user_1.to_proto(),
274                kind: proto::channel_member::Kind::AncestorMember.into(),
275                role: proto::ChannelRole::Admin.into(),
276            },
277            proto::ChannelMember {
278                user_id: user_2.to_proto(),
279                kind: proto::channel_member::Kind::AncestorMember.into(),
280                role: proto::ChannelRole::Member.into(),
281            },
282        ]
283    );
284}
285
286test_both_dbs!(
287    test_channel_renames,
288    test_channel_renames_postgres,
289    test_channel_renames_sqlite
290);
291
292async fn test_channel_renames(db: &Arc<Database>) {
293    db.create_server("test").await.unwrap();
294
295    let user_1 = db
296        .create_user(
297            "user1@example.com",
298            false,
299            NewUserParams {
300                github_login: "user1".into(),
301                github_user_id: 5,
302            },
303        )
304        .await
305        .unwrap()
306        .user_id;
307
308    let user_2 = db
309        .create_user(
310            "user2@example.com",
311            false,
312            NewUserParams {
313                github_login: "user2".into(),
314                github_user_id: 6,
315            },
316        )
317        .await
318        .unwrap()
319        .user_id;
320
321    let zed_id = db.create_root_channel("zed", user_1).await.unwrap();
322
323    db.rename_channel(zed_id, user_1, "#zed-archive")
324        .await
325        .unwrap();
326
327    let channel = db.get_channel(zed_id, user_1).await.unwrap();
328    assert_eq!(channel.name, "zed-archive");
329
330    let non_permissioned_rename = db.rename_channel(zed_id, user_2, "hacked-lol").await;
331    assert!(non_permissioned_rename.is_err());
332
333    let bad_name_rename = db.rename_channel(zed_id, user_1, "#").await;
334    assert!(bad_name_rename.is_err())
335}
336
337test_both_dbs!(
338    test_db_channel_moving,
339    test_channels_moving_postgres,
340    test_channels_moving_sqlite
341);
342
343async fn test_db_channel_moving(db: &Arc<Database>) {
344    let a_id = db
345        .create_user(
346            "user1@example.com",
347            false,
348            NewUserParams {
349                github_login: "user1".into(),
350                github_user_id: 5,
351            },
352        )
353        .await
354        .unwrap()
355        .user_id;
356
357    let zed_id = db.create_root_channel("zed", a_id).await.unwrap();
358
359    let crdb_id = db.create_sub_channel("crdb", zed_id, a_id).await.unwrap();
360
361    let gpui2_id = db.create_sub_channel("gpui2", zed_id, a_id).await.unwrap();
362
363    let livestreaming_id = db
364        .create_sub_channel("livestreaming", crdb_id, a_id)
365        .await
366        .unwrap();
367
368    let livestreaming_dag_id = db
369        .create_sub_channel("livestreaming_dag", livestreaming_id, a_id)
370        .await
371        .unwrap();
372
373    // ========================================================================
374    // sanity check
375    // Initial DAG:
376    //     /- gpui2
377    // zed -- crdb - livestreaming - livestreaming_dag
378    let result = db.get_channels_for_user(a_id).await.unwrap();
379    assert_channel_tree(
380        result.channels,
381        &[
382            (zed_id, &[]),
383            (crdb_id, &[zed_id]),
384            (livestreaming_id, &[zed_id, crdb_id]),
385            (livestreaming_dag_id, &[zed_id, crdb_id, livestreaming_id]),
386            (gpui2_id, &[zed_id]),
387        ],
388    );
389}
390
391test_both_dbs!(
392    test_db_channel_moving_bugs,
393    test_db_channel_moving_bugs_postgres,
394    test_db_channel_moving_bugs_sqlite
395);
396
397async fn test_db_channel_moving_bugs(db: &Arc<Database>) {
398    let user_id = db
399        .create_user(
400            "user1@example.com",
401            false,
402            NewUserParams {
403                github_login: "user1".into(),
404                github_user_id: 5,
405            },
406        )
407        .await
408        .unwrap()
409        .user_id;
410
411    let zed_id = db.create_root_channel("zed", user_id).await.unwrap();
412
413    let projects_id = db
414        .create_sub_channel("projects", zed_id, user_id)
415        .await
416        .unwrap();
417
418    let livestreaming_id = db
419        .create_sub_channel("livestreaming", projects_id, user_id)
420        .await
421        .unwrap();
422
423    // Dag is: zed - projects - livestreaming
424
425    // Move to same parent should be a no-op
426    assert!(db
427        .move_channel(projects_id, Some(zed_id), user_id)
428        .await
429        .unwrap()
430        .is_none());
431
432    let result = db.get_channels_for_user(user_id).await.unwrap();
433    assert_channel_tree(
434        result.channels,
435        &[
436            (zed_id, &[]),
437            (projects_id, &[zed_id]),
438            (livestreaming_id, &[zed_id, projects_id]),
439        ],
440    );
441
442    // Move the project channel to the root
443    db.move_channel(projects_id, None, user_id).await.unwrap();
444    let result = db.get_channels_for_user(user_id).await.unwrap();
445    assert_channel_tree(
446        result.channels,
447        &[
448            (zed_id, &[]),
449            (projects_id, &[]),
450            (livestreaming_id, &[projects_id]),
451        ],
452    );
453
454    // Can't move a channel into its ancestor
455    db.move_channel(projects_id, Some(livestreaming_id), user_id)
456        .await
457        .unwrap_err();
458    let result = db.get_channels_for_user(user_id).await.unwrap();
459    assert_channel_tree(
460        result.channels,
461        &[
462            (zed_id, &[]),
463            (projects_id, &[]),
464            (livestreaming_id, &[projects_id]),
465        ],
466    );
467}
468
469test_both_dbs!(
470    test_user_is_channel_participant,
471    test_user_is_channel_participant_postgres,
472    test_user_is_channel_participant_sqlite
473);
474
475async fn test_user_is_channel_participant(db: &Arc<Database>) {
476    let admin = new_test_user(db, "admin@example.com").await;
477    let member = new_test_user(db, "member@example.com").await;
478    let guest = new_test_user(db, "guest@example.com").await;
479
480    let zed_channel = db.create_root_channel("zed", admin).await.unwrap();
481    let active_channel_id = db
482        .create_sub_channel("active", zed_channel, admin)
483        .await
484        .unwrap();
485    let vim_channel_id = db
486        .create_sub_channel("vim", active_channel_id, admin)
487        .await
488        .unwrap();
489
490    db.set_channel_visibility(vim_channel_id, crate::db::ChannelVisibility::Public, admin)
491        .await
492        .unwrap();
493    db.invite_channel_member(active_channel_id, member, admin, ChannelRole::Member)
494        .await
495        .unwrap();
496    db.invite_channel_member(vim_channel_id, guest, admin, ChannelRole::Guest)
497        .await
498        .unwrap();
499
500    db.respond_to_channel_invite(active_channel_id, member, true)
501        .await
502        .unwrap();
503
504    db.transaction(|tx| async move {
505        db.check_user_is_channel_participant(
506            &db.get_channel_internal(vim_channel_id, &*tx).await?,
507            admin,
508            &*tx,
509        )
510        .await
511    })
512    .await
513    .unwrap();
514    db.transaction(|tx| async move {
515        db.check_user_is_channel_participant(
516            &db.get_channel_internal(vim_channel_id, &*tx).await?,
517            member,
518            &*tx,
519        )
520        .await
521    })
522    .await
523    .unwrap();
524
525    let mut members = db
526        .get_channel_participant_details(vim_channel_id, admin)
527        .await
528        .unwrap();
529
530    members.sort_by_key(|member| member.user_id);
531
532    assert_eq!(
533        members,
534        &[
535            proto::ChannelMember {
536                user_id: admin.to_proto(),
537                kind: proto::channel_member::Kind::AncestorMember.into(),
538                role: proto::ChannelRole::Admin.into(),
539            },
540            proto::ChannelMember {
541                user_id: member.to_proto(),
542                kind: proto::channel_member::Kind::AncestorMember.into(),
543                role: proto::ChannelRole::Member.into(),
544            },
545            proto::ChannelMember {
546                user_id: guest.to_proto(),
547                kind: proto::channel_member::Kind::Invitee.into(),
548                role: proto::ChannelRole::Guest.into(),
549            },
550        ]
551    );
552
553    db.respond_to_channel_invite(vim_channel_id, guest, true)
554        .await
555        .unwrap();
556
557    db.transaction(|tx| async move {
558        db.check_user_is_channel_participant(
559            &db.get_channel_internal(vim_channel_id, &*tx).await?,
560            guest,
561            &*tx,
562        )
563        .await
564    })
565    .await
566    .unwrap();
567
568    let channels = db.get_channels_for_user(guest).await.unwrap().channels;
569    assert_channel_tree(channels, &[(vim_channel_id, &[])]);
570    let channels = db.get_channels_for_user(member).await.unwrap().channels;
571    assert_channel_tree(
572        channels,
573        &[
574            (active_channel_id, &[]),
575            (vim_channel_id, &[active_channel_id]),
576        ],
577    );
578
579    db.set_channel_member_role(vim_channel_id, admin, guest, ChannelRole::Banned)
580        .await
581        .unwrap();
582    assert!(db
583        .transaction(|tx| async move {
584            db.check_user_is_channel_participant(
585                &db.get_channel_internal(vim_channel_id, &*tx).await.unwrap(),
586                guest,
587                &*tx,
588            )
589            .await
590        })
591        .await
592        .is_err());
593
594    let mut members = db
595        .get_channel_participant_details(vim_channel_id, admin)
596        .await
597        .unwrap();
598
599    members.sort_by_key(|member| member.user_id);
600
601    assert_eq!(
602        members,
603        &[
604            proto::ChannelMember {
605                user_id: admin.to_proto(),
606                kind: proto::channel_member::Kind::AncestorMember.into(),
607                role: proto::ChannelRole::Admin.into(),
608            },
609            proto::ChannelMember {
610                user_id: member.to_proto(),
611                kind: proto::channel_member::Kind::AncestorMember.into(),
612                role: proto::ChannelRole::Member.into(),
613            },
614            proto::ChannelMember {
615                user_id: guest.to_proto(),
616                kind: proto::channel_member::Kind::Member.into(),
617                role: proto::ChannelRole::Banned.into(),
618            },
619        ]
620    );
621
622    db.remove_channel_member(vim_channel_id, guest, admin)
623        .await
624        .unwrap();
625
626    db.set_channel_visibility(zed_channel, crate::db::ChannelVisibility::Public, admin)
627        .await
628        .unwrap();
629
630    db.invite_channel_member(zed_channel, guest, admin, ChannelRole::Guest)
631        .await
632        .unwrap();
633
634    // currently people invited to parent channels are not shown here
635    let mut members = db
636        .get_channel_participant_details(vim_channel_id, admin)
637        .await
638        .unwrap();
639
640    members.sort_by_key(|member| member.user_id);
641
642    assert_eq!(
643        members,
644        &[
645            proto::ChannelMember {
646                user_id: admin.to_proto(),
647                kind: proto::channel_member::Kind::AncestorMember.into(),
648                role: proto::ChannelRole::Admin.into(),
649            },
650            proto::ChannelMember {
651                user_id: member.to_proto(),
652                kind: proto::channel_member::Kind::AncestorMember.into(),
653                role: proto::ChannelRole::Member.into(),
654            },
655        ]
656    );
657
658    db.respond_to_channel_invite(zed_channel, guest, true)
659        .await
660        .unwrap();
661
662    db.transaction(|tx| async move {
663        db.check_user_is_channel_participant(
664            &db.get_channel_internal(zed_channel, &*tx).await.unwrap(),
665            guest,
666            &*tx,
667        )
668        .await
669    })
670    .await
671    .unwrap();
672    assert!(db
673        .transaction(|tx| async move {
674            db.check_user_is_channel_participant(
675                &db.get_channel_internal(active_channel_id, &*tx)
676                    .await
677                    .unwrap(),
678                guest,
679                &*tx,
680            )
681            .await
682        })
683        .await
684        .is_err(),);
685
686    db.transaction(|tx| async move {
687        db.check_user_is_channel_participant(
688            &db.get_channel_internal(vim_channel_id, &*tx).await.unwrap(),
689            guest,
690            &*tx,
691        )
692        .await
693    })
694    .await
695    .unwrap();
696
697    let mut members = db
698        .get_channel_participant_details(vim_channel_id, admin)
699        .await
700        .unwrap();
701
702    members.sort_by_key(|member| member.user_id);
703
704    assert_eq!(
705        members,
706        &[
707            proto::ChannelMember {
708                user_id: admin.to_proto(),
709                kind: proto::channel_member::Kind::AncestorMember.into(),
710                role: proto::ChannelRole::Admin.into(),
711            },
712            proto::ChannelMember {
713                user_id: member.to_proto(),
714                kind: proto::channel_member::Kind::AncestorMember.into(),
715                role: proto::ChannelRole::Member.into(),
716            },
717            proto::ChannelMember {
718                user_id: guest.to_proto(),
719                kind: proto::channel_member::Kind::AncestorMember.into(),
720                role: proto::ChannelRole::Guest.into(),
721            },
722        ]
723    );
724
725    let channels = db.get_channels_for_user(guest).await.unwrap().channels;
726    assert_channel_tree(
727        channels,
728        &[(zed_channel, &[]), (vim_channel_id, &[zed_channel])],
729    )
730}
731
732test_both_dbs!(
733    test_user_joins_correct_channel,
734    test_user_joins_correct_channel_postgres,
735    test_user_joins_correct_channel_sqlite
736);
737
738async fn test_user_joins_correct_channel(db: &Arc<Database>) {
739    let admin = new_test_user(db, "admin@example.com").await;
740
741    let zed_channel = db.create_root_channel("zed", admin).await.unwrap();
742
743    let active_channel = db
744        .create_sub_channel("active", zed_channel, admin)
745        .await
746        .unwrap();
747
748    let vim_channel = db
749        .create_sub_channel("vim", active_channel, admin)
750        .await
751        .unwrap();
752
753    let vim2_channel = db
754        .create_sub_channel("vim2", vim_channel, admin)
755        .await
756        .unwrap();
757
758    db.set_channel_visibility(zed_channel, crate::db::ChannelVisibility::Public, admin)
759        .await
760        .unwrap();
761
762    db.set_channel_visibility(vim_channel, crate::db::ChannelVisibility::Public, admin)
763        .await
764        .unwrap();
765
766    db.set_channel_visibility(vim2_channel, crate::db::ChannelVisibility::Public, admin)
767        .await
768        .unwrap();
769
770    let most_public = db
771        .transaction(|tx| async move {
772            Ok(db
773                .public_ancestors_including_self(
774                    &db.get_channel_internal(vim_channel, &*tx).await.unwrap(),
775                    &tx,
776                )
777                .await?
778                .first()
779                .cloned())
780        })
781        .await
782        .unwrap()
783        .unwrap()
784        .id;
785
786    assert_eq!(most_public, zed_channel)
787}
788
789test_both_dbs!(
790    test_guest_access,
791    test_guest_access_postgres,
792    test_guest_access_sqlite
793);
794
795async fn test_guest_access(db: &Arc<Database>) {
796    let server = db.create_server("test").await.unwrap();
797
798    let admin = new_test_user(db, "admin@example.com").await;
799    let guest = new_test_user(db, "guest@example.com").await;
800    let guest_connection = new_test_connection(server);
801
802    let zed_channel = db.create_root_channel("zed", admin).await.unwrap();
803    db.set_channel_visibility(zed_channel, crate::db::ChannelVisibility::Public, admin)
804        .await
805        .unwrap();
806
807    assert!(db
808        .join_channel_chat(zed_channel, guest_connection, guest)
809        .await
810        .is_err());
811
812    db.join_channel(zed_channel, guest, guest_connection, TEST_RELEASE_CHANNEL)
813        .await
814        .unwrap();
815
816    assert!(db
817        .join_channel_chat(zed_channel, guest_connection, guest)
818        .await
819        .is_ok())
820}
821
822#[track_caller]
823fn assert_channel_tree(actual: Vec<Channel>, expected: &[(ChannelId, &[ChannelId])]) {
824    let actual = actual
825        .iter()
826        .map(|channel| (channel.id, channel.parent_path.as_slice()))
827        .collect::<Vec<_>>();
828    pretty_assertions::assert_eq!(
829        actual,
830        expected.to_vec(),
831        "wrong channel ids and parent paths"
832    );
833}