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
455test_both_dbs!(
456 test_user_is_channel_participant,
457 test_user_is_channel_participant_postgres,
458 test_user_is_channel_participant_sqlite
459);
460
461async fn test_user_is_channel_participant(db: &Arc<Database>) {
462 let admin = new_test_user(db, "admin@example.com").await;
463 let member = new_test_user(db, "member@example.com").await;
464 let guest = new_test_user(db, "guest@example.com").await;
465
466 let zed_channel = db.create_root_channel("zed", admin).await.unwrap();
467 let active_channel_id = db
468 .create_sub_channel("active", zed_channel, admin)
469 .await
470 .unwrap();
471 let vim_channel_id = db
472 .create_sub_channel("vim", active_channel_id, admin)
473 .await
474 .unwrap();
475
476 db.set_channel_visibility(vim_channel_id, crate::db::ChannelVisibility::Public, admin)
477 .await
478 .unwrap();
479 db.invite_channel_member(active_channel_id, member, admin, ChannelRole::Member)
480 .await
481 .unwrap();
482 db.invite_channel_member(vim_channel_id, guest, admin, ChannelRole::Guest)
483 .await
484 .unwrap();
485
486 db.respond_to_channel_invite(active_channel_id, member, true)
487 .await
488 .unwrap();
489
490 db.transaction(|tx| async move {
491 db.check_user_is_channel_participant(
492 &db.get_channel_internal(vim_channel_id, &*tx).await?,
493 admin,
494 &*tx,
495 )
496 .await
497 })
498 .await
499 .unwrap();
500 db.transaction(|tx| async move {
501 db.check_user_is_channel_participant(
502 &db.get_channel_internal(vim_channel_id, &*tx).await?,
503 member,
504 &*tx,
505 )
506 .await
507 })
508 .await
509 .unwrap();
510
511 let mut members = db
512 .get_channel_participant_details(vim_channel_id, admin)
513 .await
514 .unwrap();
515
516 members.sort_by_key(|member| member.user_id);
517
518 assert_eq!(
519 members,
520 &[
521 proto::ChannelMember {
522 user_id: admin.to_proto(),
523 kind: proto::channel_member::Kind::AncestorMember.into(),
524 role: proto::ChannelRole::Admin.into(),
525 },
526 proto::ChannelMember {
527 user_id: member.to_proto(),
528 kind: proto::channel_member::Kind::AncestorMember.into(),
529 role: proto::ChannelRole::Member.into(),
530 },
531 proto::ChannelMember {
532 user_id: guest.to_proto(),
533 kind: proto::channel_member::Kind::Invitee.into(),
534 role: proto::ChannelRole::Guest.into(),
535 },
536 ]
537 );
538
539 db.respond_to_channel_invite(vim_channel_id, guest, true)
540 .await
541 .unwrap();
542
543 db.transaction(|tx| async move {
544 db.check_user_is_channel_participant(
545 &db.get_channel_internal(vim_channel_id, &*tx).await?,
546 guest,
547 &*tx,
548 )
549 .await
550 })
551 .await
552 .unwrap();
553
554 let channels = db.get_channels_for_user(guest).await.unwrap().channels;
555 assert_channel_tree(channels, &[(vim_channel_id, &[])]);
556 let channels = db.get_channels_for_user(member).await.unwrap().channels;
557 assert_channel_tree(
558 channels,
559 &[
560 (active_channel_id, &[]),
561 (vim_channel_id, &[active_channel_id]),
562 ],
563 );
564
565 db.set_channel_member_role(vim_channel_id, admin, guest, ChannelRole::Banned)
566 .await
567 .unwrap();
568 assert!(db
569 .transaction(|tx| async move {
570 db.check_user_is_channel_participant(
571 &db.get_channel_internal(vim_channel_id, &*tx).await.unwrap(),
572 guest,
573 &*tx,
574 )
575 .await
576 })
577 .await
578 .is_err());
579
580 let mut members = db
581 .get_channel_participant_details(vim_channel_id, admin)
582 .await
583 .unwrap();
584
585 members.sort_by_key(|member| member.user_id);
586
587 assert_eq!(
588 members,
589 &[
590 proto::ChannelMember {
591 user_id: admin.to_proto(),
592 kind: proto::channel_member::Kind::AncestorMember.into(),
593 role: proto::ChannelRole::Admin.into(),
594 },
595 proto::ChannelMember {
596 user_id: member.to_proto(),
597 kind: proto::channel_member::Kind::AncestorMember.into(),
598 role: proto::ChannelRole::Member.into(),
599 },
600 proto::ChannelMember {
601 user_id: guest.to_proto(),
602 kind: proto::channel_member::Kind::Member.into(),
603 role: proto::ChannelRole::Banned.into(),
604 },
605 ]
606 );
607
608 db.remove_channel_member(vim_channel_id, guest, admin)
609 .await
610 .unwrap();
611
612 db.set_channel_visibility(zed_channel, crate::db::ChannelVisibility::Public, admin)
613 .await
614 .unwrap();
615
616 db.invite_channel_member(zed_channel, guest, admin, ChannelRole::Guest)
617 .await
618 .unwrap();
619
620 // currently people invited to parent channels are not shown here
621 let mut members = db
622 .get_channel_participant_details(vim_channel_id, admin)
623 .await
624 .unwrap();
625
626 members.sort_by_key(|member| member.user_id);
627
628 assert_eq!(
629 members,
630 &[
631 proto::ChannelMember {
632 user_id: admin.to_proto(),
633 kind: proto::channel_member::Kind::AncestorMember.into(),
634 role: proto::ChannelRole::Admin.into(),
635 },
636 proto::ChannelMember {
637 user_id: member.to_proto(),
638 kind: proto::channel_member::Kind::AncestorMember.into(),
639 role: proto::ChannelRole::Member.into(),
640 },
641 ]
642 );
643
644 db.respond_to_channel_invite(zed_channel, guest, true)
645 .await
646 .unwrap();
647
648 db.transaction(|tx| async move {
649 db.check_user_is_channel_participant(
650 &db.get_channel_internal(zed_channel, &*tx).await.unwrap(),
651 guest,
652 &*tx,
653 )
654 .await
655 })
656 .await
657 .unwrap();
658 assert!(db
659 .transaction(|tx| async move {
660 db.check_user_is_channel_participant(
661 &db.get_channel_internal(active_channel_id, &*tx)
662 .await
663 .unwrap(),
664 guest,
665 &*tx,
666 )
667 .await
668 })
669 .await
670 .is_err(),);
671
672 db.transaction(|tx| async move {
673 db.check_user_is_channel_participant(
674 &db.get_channel_internal(vim_channel_id, &*tx).await.unwrap(),
675 guest,
676 &*tx,
677 )
678 .await
679 })
680 .await
681 .unwrap();
682
683 let mut members = db
684 .get_channel_participant_details(vim_channel_id, admin)
685 .await
686 .unwrap();
687
688 members.sort_by_key(|member| member.user_id);
689
690 assert_eq!(
691 members,
692 &[
693 proto::ChannelMember {
694 user_id: admin.to_proto(),
695 kind: proto::channel_member::Kind::AncestorMember.into(),
696 role: proto::ChannelRole::Admin.into(),
697 },
698 proto::ChannelMember {
699 user_id: member.to_proto(),
700 kind: proto::channel_member::Kind::AncestorMember.into(),
701 role: proto::ChannelRole::Member.into(),
702 },
703 proto::ChannelMember {
704 user_id: guest.to_proto(),
705 kind: proto::channel_member::Kind::AncestorMember.into(),
706 role: proto::ChannelRole::Guest.into(),
707 },
708 ]
709 );
710
711 let channels = db.get_channels_for_user(guest).await.unwrap().channels;
712 assert_channel_tree(
713 channels,
714 &[(zed_channel, &[]), (vim_channel_id, &[zed_channel])],
715 )
716}
717
718test_both_dbs!(
719 test_user_joins_correct_channel,
720 test_user_joins_correct_channel_postgres,
721 test_user_joins_correct_channel_sqlite
722);
723
724async fn test_user_joins_correct_channel(db: &Arc<Database>) {
725 let admin = new_test_user(db, "admin@example.com").await;
726
727 let zed_channel = db.create_root_channel("zed", admin).await.unwrap();
728
729 let active_channel = db
730 .create_sub_channel("active", zed_channel, admin)
731 .await
732 .unwrap();
733
734 let vim_channel = db
735 .create_sub_channel("vim", active_channel, admin)
736 .await
737 .unwrap();
738
739 let vim2_channel = db
740 .create_sub_channel("vim2", vim_channel, admin)
741 .await
742 .unwrap();
743
744 db.set_channel_visibility(zed_channel, crate::db::ChannelVisibility::Public, admin)
745 .await
746 .unwrap();
747
748 db.set_channel_visibility(vim_channel, crate::db::ChannelVisibility::Public, admin)
749 .await
750 .unwrap();
751
752 db.set_channel_visibility(vim2_channel, crate::db::ChannelVisibility::Public, admin)
753 .await
754 .unwrap();
755
756 let most_public = db
757 .transaction(|tx| async move {
758 Ok(db
759 .public_ancestors_including_self(
760 &db.get_channel_internal(vim_channel, &*tx).await.unwrap(),
761 &tx,
762 )
763 .await?
764 .first()
765 .cloned())
766 })
767 .await
768 .unwrap()
769 .unwrap()
770 .id;
771
772 assert_eq!(most_public, zed_channel)
773}
774
775test_both_dbs!(
776 test_guest_access,
777 test_guest_access_postgres,
778 test_guest_access_sqlite
779);
780
781async fn test_guest_access(db: &Arc<Database>) {
782 let server = db.create_server("test").await.unwrap();
783
784 let admin = new_test_user(db, "admin@example.com").await;
785 let guest = new_test_user(db, "guest@example.com").await;
786 let guest_connection = new_test_connection(server);
787
788 let zed_channel = db.create_root_channel("zed", admin).await.unwrap();
789 db.set_channel_visibility(zed_channel, crate::db::ChannelVisibility::Public, admin)
790 .await
791 .unwrap();
792
793 assert!(db
794 .join_channel_chat(zed_channel, guest_connection, guest)
795 .await
796 .is_err());
797
798 db.join_channel(zed_channel, guest, guest_connection, TEST_RELEASE_CHANNEL)
799 .await
800 .unwrap();
801
802 assert!(db
803 .join_channel_chat(zed_channel, guest_connection, guest)
804 .await
805 .is_ok())
806}
807
808#[track_caller]
809fn assert_channel_tree(actual: Vec<Channel>, expected: &[(ChannelId, &[ChannelId])]) {
810 let actual = actual
811 .iter()
812 .map(|channel| (channel.id, channel.parent_path.as_slice()))
813 .collect::<Vec<_>>();
814 pretty_assertions::assert_eq!(
815 actual,
816 expected.to_vec(),
817 "wrong channel ids and parent paths"
818 );
819}