Remove logic for multiple channel parents (#3162)
This PR simplifies our state management for channels, and logic for inheriting channel permissions, by removing the ability for channels to have multiple parent channels.
This commit is contained in:
commit
90f65ec9fe
26 changed files with 879 additions and 2093 deletions
|
@ -193,9 +193,12 @@ CREATE TABLE "channels" (
|
|||
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"name" VARCHAR NOT NULL,
|
||||
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"visibility" VARCHAR NOT NULL
|
||||
"visibility" VARCHAR NOT NULL,
|
||||
"parent_path" TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX "index_channels_on_parent_path" ON "channels" ("parent_path");
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "channel_chat_participants" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"user_id" INTEGER NOT NULL REFERENCES users (id),
|
||||
|
@ -224,12 +227,6 @@ CREATE TABLE "channel_message_mentions" (
|
|||
PRIMARY KEY(message_id, start_offset)
|
||||
);
|
||||
|
||||
CREATE TABLE "channel_paths" (
|
||||
"id_path" TEXT NOT NULL PRIMARY KEY,
|
||||
"channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX "index_channel_paths_on_channel_id" ON "channel_paths" ("channel_id");
|
||||
|
||||
CREATE TABLE "channel_members" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE,
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
ALTER TABLE channels ADD COLUMN parent_path TEXT;
|
||||
|
||||
UPDATE channels
|
||||
SET parent_path = substr(
|
||||
channel_paths.id_path,
|
||||
2,
|
||||
length(channel_paths.id_path) - length('/' || channel_paths.channel_id::text || '/')
|
||||
)
|
||||
FROM channel_paths
|
||||
WHERE channel_paths.channel_id = channels.id;
|
||||
|
||||
CREATE INDEX "index_channels_on_parent_path" ON "channels" ("parent_path");
|
|
@ -13,7 +13,6 @@ use anyhow::anyhow;
|
|||
use collections::{BTreeMap, HashMap, HashSet};
|
||||
use dashmap::DashMap;
|
||||
use futures::StreamExt;
|
||||
use queries::channels::ChannelGraph;
|
||||
use rand::{prelude::StdRng, Rng, SeedableRng};
|
||||
use rpc::{
|
||||
proto::{self},
|
||||
|
@ -492,21 +491,33 @@ pub struct RemoveChannelMemberResult {
|
|||
pub notification_id: Option<NotificationId>,
|
||||
}
|
||||
|
||||
#[derive(FromQueryResult, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Channel {
|
||||
pub id: ChannelId,
|
||||
pub name: String,
|
||||
pub visibility: ChannelVisibility,
|
||||
pub role: ChannelRole,
|
||||
pub parent_path: Vec<ChannelId>,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
fn from_model(value: channel::Model, role: ChannelRole) -> Self {
|
||||
Channel {
|
||||
id: value.id,
|
||||
visibility: value.visibility,
|
||||
name: value.clone().name,
|
||||
role,
|
||||
parent_path: value.ancestors().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_proto(&self) -> proto::Channel {
|
||||
proto::Channel {
|
||||
id: self.id.to_proto(),
|
||||
name: self.name.clone(),
|
||||
visibility: self.visibility.into(),
|
||||
role: self.role.into(),
|
||||
parent_path: self.parent_path.iter().map(|c| c.to_proto()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -530,7 +541,7 @@ impl ChannelMember {
|
|||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ChannelsForUser {
|
||||
pub channels: ChannelGraph,
|
||||
pub channels: Vec<Channel>,
|
||||
pub channel_participants: HashMap<ChannelId, Vec<UserId>>,
|
||||
pub unseen_buffer_changes: Vec<proto::UnseenChannelBufferChange>,
|
||||
pub channel_messages: Vec<proto::UnseenChannelMessage>,
|
||||
|
|
|
@ -16,7 +16,8 @@ impl Database {
|
|||
connection: ConnectionId,
|
||||
) -> Result<proto::JoinChannelBufferResponse> {
|
||||
self.transaction(|tx| async move {
|
||||
self.check_user_is_channel_participant(channel_id, user_id, &tx)
|
||||
let channel = self.get_channel_internal(channel_id, &*tx).await?;
|
||||
self.check_user_is_channel_participant(&channel, user_id, &tx)
|
||||
.await?;
|
||||
|
||||
let buffer = channel::Model {
|
||||
|
@ -129,9 +130,11 @@ impl Database {
|
|||
self.transaction(|tx| async move {
|
||||
let mut results = Vec::new();
|
||||
for client_buffer in buffers {
|
||||
let channel_id = ChannelId::from_proto(client_buffer.channel_id);
|
||||
let channel = self
|
||||
.get_channel_internal(ChannelId::from_proto(client_buffer.channel_id), &*tx)
|
||||
.await?;
|
||||
if self
|
||||
.check_user_is_channel_participant(channel_id, user_id, &*tx)
|
||||
.check_user_is_channel_participant(&channel, user_id, &*tx)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
|
@ -139,9 +142,9 @@ impl Database {
|
|||
continue;
|
||||
}
|
||||
|
||||
let buffer = self.get_channel_buffer(channel_id, &*tx).await?;
|
||||
let buffer = self.get_channel_buffer(channel.id, &*tx).await?;
|
||||
let mut collaborators = channel_buffer_collaborator::Entity::find()
|
||||
.filter(channel_buffer_collaborator::Column::ChannelId.eq(channel_id))
|
||||
.filter(channel_buffer_collaborator::Column::ChannelId.eq(channel.id))
|
||||
.all(&*tx)
|
||||
.await?;
|
||||
|
||||
|
@ -439,7 +442,8 @@ impl Database {
|
|||
Vec<proto::VectorClockEntry>,
|
||||
)> {
|
||||
self.transaction(move |tx| async move {
|
||||
self.check_user_is_channel_member(channel_id, user, &*tx)
|
||||
let channel = self.get_channel_internal(channel_id, &*tx).await?;
|
||||
self.check_user_is_channel_member(&channel, user, &*tx)
|
||||
.await?;
|
||||
|
||||
let buffer = buffer::Entity::find()
|
||||
|
@ -482,7 +486,7 @@ impl Database {
|
|||
)
|
||||
.await?;
|
||||
|
||||
channel_members = self.get_channel_participants(channel_id, &*tx).await?;
|
||||
channel_members = self.get_channel_participants(&channel, &*tx).await?;
|
||||
let collaborators = self
|
||||
.get_channel_buffer_collaborators_internal(channel_id, &*tx)
|
||||
.await?;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,4 @@
|
|||
use super::*;
|
||||
use futures::Stream;
|
||||
use rpc::Notification;
|
||||
use sea_orm::TryInsertResult;
|
||||
use time::OffsetDateTime;
|
||||
|
@ -12,7 +11,8 @@ impl Database {
|
|||
user_id: UserId,
|
||||
) -> Result<()> {
|
||||
self.transaction(|tx| async move {
|
||||
self.check_user_is_channel_participant(channel_id, user_id, &*tx)
|
||||
let channel = self.get_channel_internal(channel_id, &*tx).await?;
|
||||
self.check_user_is_channel_participant(&channel, user_id, &*tx)
|
||||
.await?;
|
||||
channel_chat_participant::ActiveModel {
|
||||
id: ActiveValue::NotSet,
|
||||
|
@ -80,7 +80,8 @@ impl Database {
|
|||
before_message_id: Option<MessageId>,
|
||||
) -> Result<Vec<proto::ChannelMessage>> {
|
||||
self.transaction(|tx| async move {
|
||||
self.check_user_is_channel_participant(channel_id, user_id, &*tx)
|
||||
let channel = self.get_channel_internal(channel_id, &*tx).await?;
|
||||
self.check_user_is_channel_participant(&channel, user_id, &*tx)
|
||||
.await?;
|
||||
|
||||
let mut condition =
|
||||
|
@ -94,7 +95,7 @@ impl Database {
|
|||
.filter(condition)
|
||||
.order_by_desc(channel_message::Column::Id)
|
||||
.limit(count as u64)
|
||||
.stream(&*tx)
|
||||
.all(&*tx)
|
||||
.await?;
|
||||
|
||||
self.load_channel_messages(rows, &*tx).await
|
||||
|
@ -111,27 +112,23 @@ impl Database {
|
|||
let rows = channel_message::Entity::find()
|
||||
.filter(channel_message::Column::Id.is_in(message_ids.iter().copied()))
|
||||
.order_by_desc(channel_message::Column::Id)
|
||||
.stream(&*tx)
|
||||
.all(&*tx)
|
||||
.await?;
|
||||
|
||||
let mut channel_ids = HashSet::<ChannelId>::default();
|
||||
let messages = self
|
||||
.load_channel_messages(
|
||||
rows.map(|row| {
|
||||
row.map(|row| {
|
||||
channel_ids.insert(row.channel_id);
|
||||
row
|
||||
})
|
||||
}),
|
||||
&*tx,
|
||||
)
|
||||
.await?;
|
||||
let mut channels = HashMap::<ChannelId, channel::Model>::default();
|
||||
for row in &rows {
|
||||
channels.insert(
|
||||
row.channel_id,
|
||||
self.get_channel_internal(row.channel_id, &*tx).await?,
|
||||
);
|
||||
}
|
||||
|
||||
for channel_id in channel_ids {
|
||||
self.check_user_is_channel_member(channel_id, user_id, &*tx)
|
||||
for (_, channel) in channels {
|
||||
self.check_user_is_channel_participant(&channel, user_id, &*tx)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let messages = self.load_channel_messages(rows, &*tx).await?;
|
||||
Ok(messages)
|
||||
})
|
||||
.await
|
||||
|
@ -139,26 +136,26 @@ impl Database {
|
|||
|
||||
async fn load_channel_messages(
|
||||
&self,
|
||||
mut rows: impl Send + Unpin + Stream<Item = Result<channel_message::Model, sea_orm::DbErr>>,
|
||||
rows: Vec<channel_message::Model>,
|
||||
tx: &DatabaseTransaction,
|
||||
) -> Result<Vec<proto::ChannelMessage>> {
|
||||
let mut messages = Vec::new();
|
||||
while let Some(row) = rows.next().await {
|
||||
let row = row?;
|
||||
let nonce = row.nonce.as_u64_pair();
|
||||
messages.push(proto::ChannelMessage {
|
||||
id: row.id.to_proto(),
|
||||
sender_id: row.sender_id.to_proto(),
|
||||
body: row.body,
|
||||
timestamp: row.sent_at.assume_utc().unix_timestamp() as u64,
|
||||
mentions: vec![],
|
||||
nonce: Some(proto::Nonce {
|
||||
upper_half: nonce.0,
|
||||
lower_half: nonce.1,
|
||||
}),
|
||||
});
|
||||
}
|
||||
drop(rows);
|
||||
let mut messages = rows
|
||||
.into_iter()
|
||||
.map(|row| {
|
||||
let nonce = row.nonce.as_u64_pair();
|
||||
proto::ChannelMessage {
|
||||
id: row.id.to_proto(),
|
||||
sender_id: row.sender_id.to_proto(),
|
||||
body: row.body,
|
||||
timestamp: row.sent_at.assume_utc().unix_timestamp() as u64,
|
||||
mentions: vec![],
|
||||
nonce: Some(proto::Nonce {
|
||||
upper_half: nonce.0,
|
||||
lower_half: nonce.1,
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
messages.reverse();
|
||||
|
||||
let mut mentions = channel_message_mention::Entity::find()
|
||||
|
@ -203,7 +200,8 @@ impl Database {
|
|||
nonce: u128,
|
||||
) -> Result<CreatedChannelMessage> {
|
||||
self.transaction(|tx| async move {
|
||||
self.check_user_is_channel_participant(channel_id, user_id, &*tx)
|
||||
let channel = self.get_channel_internal(channel_id, &*tx).await?;
|
||||
self.check_user_is_channel_participant(&channel, user_id, &*tx)
|
||||
.await?;
|
||||
|
||||
let mut rows = channel_chat_participant::Entity::find()
|
||||
|
@ -310,7 +308,7 @@ impl Database {
|
|||
}
|
||||
}
|
||||
|
||||
let mut channel_members = self.get_channel_participants(channel_id, &*tx).await?;
|
||||
let mut channel_members = self.get_channel_participants(&channel, &*tx).await?;
|
||||
channel_members.retain(|member| !participant_user_ids.contains(member));
|
||||
|
||||
Ok(CreatedChannelMessage {
|
||||
|
@ -483,8 +481,9 @@ impl Database {
|
|||
.await?;
|
||||
|
||||
if result.rows_affected == 0 {
|
||||
let channel = self.get_channel_internal(channel_id, &*tx).await?;
|
||||
if self
|
||||
.check_user_is_channel_admin(channel_id, user_id, &*tx)
|
||||
.check_user_is_channel_admin(&channel, user_id, &*tx)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
|
|
|
@ -50,10 +50,10 @@ impl Database {
|
|||
.map(|participant| participant.user_id),
|
||||
);
|
||||
|
||||
let (channel_id, room) = self.get_channel_room(room_id, &tx).await?;
|
||||
let (channel, room) = self.get_channel_room(room_id, &tx).await?;
|
||||
let channel_members;
|
||||
if let Some(channel_id) = channel_id {
|
||||
channel_members = self.get_channel_participants(channel_id, &tx).await?;
|
||||
if let Some(channel) = &channel {
|
||||
channel_members = self.get_channel_participants(channel, &tx).await?;
|
||||
} else {
|
||||
channel_members = Vec::new();
|
||||
|
||||
|
@ -69,7 +69,7 @@ impl Database {
|
|||
|
||||
Ok(RefreshedRoom {
|
||||
room,
|
||||
channel_id,
|
||||
channel_id: channel.map(|channel| channel.id),
|
||||
channel_members,
|
||||
stale_participant_user_ids,
|
||||
canceled_calls_to_user_ids,
|
||||
|
@ -381,7 +381,6 @@ impl Database {
|
|||
|
||||
pub(crate) async fn join_channel_room_internal(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
room_id: RoomId,
|
||||
user_id: UserId,
|
||||
connection: ConnectionId,
|
||||
|
@ -420,11 +419,12 @@ impl Database {
|
|||
.exec(&*tx)
|
||||
.await?;
|
||||
|
||||
let room = self.get_room(room_id, &tx).await?;
|
||||
let channel_members = self.get_channel_participants(channel_id, &tx).await?;
|
||||
let (channel, room) = self.get_channel_room(room_id, &tx).await?;
|
||||
let channel = channel.ok_or_else(|| anyhow!("no channel for room"))?;
|
||||
let channel_members = self.get_channel_participants(&channel, &*tx).await?;
|
||||
Ok(JoinRoom {
|
||||
room,
|
||||
channel_id: Some(channel_id),
|
||||
channel_id: Some(channel.id),
|
||||
channel_members,
|
||||
})
|
||||
}
|
||||
|
@ -718,16 +718,16 @@ impl Database {
|
|||
});
|
||||
}
|
||||
|
||||
let (channel_id, room) = self.get_channel_room(room_id, &tx).await?;
|
||||
let channel_members = if let Some(channel_id) = channel_id {
|
||||
self.get_channel_participants(channel_id, &tx).await?
|
||||
let (channel, room) = self.get_channel_room(room_id, &tx).await?;
|
||||
let channel_members = if let Some(channel) = &channel {
|
||||
self.get_channel_participants(&channel, &tx).await?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
Ok(RejoinedRoom {
|
||||
room,
|
||||
channel_id,
|
||||
channel_id: channel.map(|channel| channel.id),
|
||||
channel_members,
|
||||
rejoined_projects,
|
||||
reshared_projects,
|
||||
|
@ -869,7 +869,7 @@ impl Database {
|
|||
.exec(&*tx)
|
||||
.await?;
|
||||
|
||||
let (channel_id, room) = self.get_channel_room(room_id, &tx).await?;
|
||||
let (channel, room) = self.get_channel_room(room_id, &tx).await?;
|
||||
let deleted = if room.participants.is_empty() {
|
||||
let result = room::Entity::delete_by_id(room_id).exec(&*tx).await?;
|
||||
result.rows_affected > 0
|
||||
|
@ -877,14 +877,14 @@ impl Database {
|
|||
false
|
||||
};
|
||||
|
||||
let channel_members = if let Some(channel_id) = channel_id {
|
||||
self.get_channel_participants(channel_id, &tx).await?
|
||||
let channel_members = if let Some(channel) = &channel {
|
||||
self.get_channel_participants(channel, &tx).await?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let left_room = LeftRoom {
|
||||
room,
|
||||
channel_id,
|
||||
channel_id: channel.map(|channel| channel.id),
|
||||
channel_members,
|
||||
left_projects,
|
||||
canceled_calls_to_user_ids,
|
||||
|
@ -1072,7 +1072,7 @@ impl Database {
|
|||
&self,
|
||||
room_id: RoomId,
|
||||
tx: &DatabaseTransaction,
|
||||
) -> Result<(Option<ChannelId>, proto::Room)> {
|
||||
) -> Result<(Option<channel::Model>, proto::Room)> {
|
||||
let db_room = room::Entity::find_by_id(room_id)
|
||||
.one(tx)
|
||||
.await?
|
||||
|
@ -1181,9 +1181,16 @@ impl Database {
|
|||
project_id: db_follower.project_id.to_proto(),
|
||||
});
|
||||
}
|
||||
drop(db_followers);
|
||||
|
||||
let channel = if let Some(channel_id) = db_room.channel_id {
|
||||
Some(self.get_channel_internal(channel_id, &*tx).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok((
|
||||
db_room.channel_id,
|
||||
channel,
|
||||
proto::Room {
|
||||
id: db_room.id.to_proto(),
|
||||
live_kit_room: db_room.live_kit_room,
|
||||
|
|
|
@ -8,7 +8,6 @@ pub mod channel_chat_participant;
|
|||
pub mod channel_member;
|
||||
pub mod channel_message;
|
||||
pub mod channel_message_mention;
|
||||
pub mod channel_path;
|
||||
pub mod contact;
|
||||
pub mod feature_flag;
|
||||
pub mod follower;
|
||||
|
|
|
@ -8,6 +8,28 @@ pub struct Model {
|
|||
pub id: ChannelId,
|
||||
pub name: String,
|
||||
pub visibility: ChannelVisibility,
|
||||
pub parent_path: String,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn parent_id(&self) -> Option<ChannelId> {
|
||||
self.ancestors().last()
|
||||
}
|
||||
|
||||
pub fn ancestors(&self) -> impl Iterator<Item = ChannelId> + '_ {
|
||||
self.parent_path
|
||||
.trim_end_matches('/')
|
||||
.split('/')
|
||||
.filter_map(|id| Some(ChannelId::from_proto(id.parse().ok()?)))
|
||||
}
|
||||
|
||||
pub fn ancestors_including_self(&self) -> impl Iterator<Item = ChannelId> + '_ {
|
||||
self.ancestors().chain(Some(self.id))
|
||||
}
|
||||
|
||||
pub fn path(&self) -> String {
|
||||
format!("{}{}/", self.parent_path, self.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
use crate::db::ChannelId;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "channel_paths")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id_path: String,
|
||||
pub channel_id: ChannelId,
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
|
@ -7,7 +7,6 @@ mod message_tests;
|
|||
use super::*;
|
||||
use gpui::executor::Background;
|
||||
use parking_lot::Mutex;
|
||||
use rpc::proto::ChannelEdge;
|
||||
use sea_orm::ConnectionTrait;
|
||||
use sqlx::migrate::MigrateDatabase;
|
||||
use std::sync::{
|
||||
|
@ -153,33 +152,17 @@ impl Drop for TestDb {
|
|||
}
|
||||
}
|
||||
|
||||
/// The second tuples are (channel_id, parent)
|
||||
fn graph(
|
||||
channels: &[(ChannelId, &'static str, ChannelRole)],
|
||||
edges: &[(ChannelId, ChannelId)],
|
||||
) -> ChannelGraph {
|
||||
let mut graph = ChannelGraph {
|
||||
channels: vec![],
|
||||
edges: vec![],
|
||||
};
|
||||
|
||||
for (id, name, role) in channels {
|
||||
graph.channels.push(Channel {
|
||||
fn channel_tree(channels: &[(ChannelId, &[ChannelId], &'static str, ChannelRole)]) -> Vec<Channel> {
|
||||
channels
|
||||
.iter()
|
||||
.map(|(id, parent_path, name, role)| Channel {
|
||||
id: *id,
|
||||
name: name.to_string(),
|
||||
visibility: ChannelVisibility::Members,
|
||||
role: *role,
|
||||
parent_path: parent_path.to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
for (channel, parent) in edges {
|
||||
graph.edges.push(ChannelEdge {
|
||||
channel_id: channel.to_proto(),
|
||||
parent_id: parent.to_proto(),
|
||||
})
|
||||
}
|
||||
|
||||
graph
|
||||
.collect()
|
||||
}
|
||||
|
||||
static GITHUB_USER_ID: AtomicI32 = AtomicI32::new(5);
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
db::{
|
||||
queries::channels::ChannelGraph,
|
||||
tests::{graph, new_test_connection, new_test_user, TEST_RELEASE_CHANNEL},
|
||||
ChannelId, ChannelRole, Database, NewUserParams, RoomId,
|
||||
tests::{channel_tree, new_test_connection, new_test_user, TEST_RELEASE_CHANNEL},
|
||||
Channel, ChannelId, ChannelRole, Database, NewUserParams, RoomId,
|
||||
},
|
||||
test_both_dbs,
|
||||
};
|
||||
use collections::{HashMap, HashSet};
|
||||
use rpc::{
|
||||
proto::{self},
|
||||
ConnectionId,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
test_both_dbs!(test_channels, test_channels_postgres, test_channels_sqlite);
|
||||
|
||||
|
@ -44,7 +41,10 @@ async fn test_channels(db: &Arc<Database>) {
|
|||
.unwrap();
|
||||
|
||||
let mut members = db
|
||||
.transaction(|tx| async move { Ok(db.get_channel_participants(replace_id, &*tx).await?) })
|
||||
.transaction(|tx| async move {
|
||||
let channel = db.get_channel_internal(replace_id, &*tx).await?;
|
||||
Ok(db.get_channel_participants(&channel, &*tx).await?)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
members.sort();
|
||||
|
@ -61,42 +61,41 @@ async fn test_channels(db: &Arc<Database>) {
|
|||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_eq!(
|
||||
result.channels,
|
||||
graph(
|
||||
&[
|
||||
(zed_id, "zed", ChannelRole::Admin),
|
||||
(crdb_id, "crdb", ChannelRole::Admin),
|
||||
(livestreaming_id, "livestreaming", ChannelRole::Admin),
|
||||
(replace_id, "replace", ChannelRole::Admin),
|
||||
(rust_id, "rust", ChannelRole::Admin),
|
||||
(cargo_id, "cargo", ChannelRole::Admin),
|
||||
(cargo_ra_id, "cargo-ra", ChannelRole::Admin)
|
||||
],
|
||||
&[
|
||||
(crdb_id, zed_id),
|
||||
(livestreaming_id, zed_id),
|
||||
(replace_id, zed_id),
|
||||
(cargo_id, rust_id),
|
||||
(cargo_ra_id, cargo_id),
|
||||
]
|
||||
)
|
||||
channel_tree(&[
|
||||
(zed_id, &[], "zed", ChannelRole::Admin),
|
||||
(crdb_id, &[zed_id], "crdb", ChannelRole::Admin),
|
||||
(
|
||||
livestreaming_id,
|
||||
&[zed_id],
|
||||
"livestreaming",
|
||||
ChannelRole::Admin
|
||||
),
|
||||
(replace_id, &[zed_id], "replace", ChannelRole::Admin),
|
||||
(rust_id, &[], "rust", ChannelRole::Admin),
|
||||
(cargo_id, &[rust_id], "cargo", ChannelRole::Admin),
|
||||
(
|
||||
cargo_ra_id,
|
||||
&[rust_id, cargo_id],
|
||||
"cargo-ra",
|
||||
ChannelRole::Admin
|
||||
)
|
||||
],)
|
||||
);
|
||||
|
||||
let result = db.get_channels_for_user(b_id).await.unwrap();
|
||||
assert_eq!(
|
||||
result.channels,
|
||||
graph(
|
||||
&[
|
||||
(zed_id, "zed", ChannelRole::Member),
|
||||
(crdb_id, "crdb", ChannelRole::Member),
|
||||
(livestreaming_id, "livestreaming", ChannelRole::Member),
|
||||
(replace_id, "replace", ChannelRole::Member)
|
||||
],
|
||||
&[
|
||||
(crdb_id, zed_id),
|
||||
(livestreaming_id, zed_id),
|
||||
(replace_id, zed_id)
|
||||
]
|
||||
)
|
||||
channel_tree(&[
|
||||
(zed_id, &[], "zed", ChannelRole::Member),
|
||||
(crdb_id, &[zed_id], "crdb", ChannelRole::Member),
|
||||
(
|
||||
livestreaming_id,
|
||||
&[zed_id],
|
||||
"livestreaming",
|
||||
ChannelRole::Member
|
||||
),
|
||||
(replace_id, &[zed_id], "replace", ChannelRole::Member)
|
||||
],)
|
||||
);
|
||||
|
||||
// Update member permissions
|
||||
|
@ -112,19 +111,17 @@ async fn test_channels(db: &Arc<Database>) {
|
|||
let result = db.get_channels_for_user(b_id).await.unwrap();
|
||||
assert_eq!(
|
||||
result.channels,
|
||||
graph(
|
||||
&[
|
||||
(zed_id, "zed", ChannelRole::Admin),
|
||||
(crdb_id, "crdb", ChannelRole::Admin),
|
||||
(livestreaming_id, "livestreaming", ChannelRole::Admin),
|
||||
(replace_id, "replace", ChannelRole::Admin)
|
||||
],
|
||||
&[
|
||||
(crdb_id, zed_id),
|
||||
(livestreaming_id, zed_id),
|
||||
(replace_id, zed_id)
|
||||
]
|
||||
)
|
||||
channel_tree(&[
|
||||
(zed_id, &[], "zed", ChannelRole::Admin),
|
||||
(crdb_id, &[zed_id], "crdb", ChannelRole::Admin),
|
||||
(
|
||||
livestreaming_id,
|
||||
&[zed_id],
|
||||
"livestreaming",
|
||||
ChannelRole::Admin
|
||||
),
|
||||
(replace_id, &[zed_id], "replace", ChannelRole::Admin)
|
||||
],)
|
||||
);
|
||||
|
||||
// Remove a single channel
|
||||
|
@ -327,14 +324,10 @@ async fn test_channel_renames(db: &Arc<Database>) {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let zed_archive_id = zed_id;
|
||||
|
||||
let channel = db.get_channel(zed_archive_id, user_1).await.unwrap();
|
||||
let channel = db.get_channel(zed_id, user_1).await.unwrap();
|
||||
assert_eq!(channel.name, "zed-archive");
|
||||
|
||||
let non_permissioned_rename = db
|
||||
.rename_channel(zed_archive_id, user_2, "hacked-lol")
|
||||
.await;
|
||||
let non_permissioned_rename = db.rename_channel(zed_id, user_2, "hacked-lol").await;
|
||||
assert!(non_permissioned_rename.is_err());
|
||||
|
||||
let bad_name_rename = db.rename_channel(zed_id, user_1, "#").await;
|
||||
|
@ -383,328 +376,16 @@ async fn test_db_channel_moving(db: &Arc<Database>) {
|
|||
// /- gpui2
|
||||
// zed -- crdb - livestreaming - livestreaming_dag
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_dag(
|
||||
assert_channel_tree(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(crdb_id, Some(zed_id)),
|
||||
(gpui2_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(crdb_id)),
|
||||
(livestreaming_dag_id, Some(livestreaming_id)),
|
||||
(zed_id, &[]),
|
||||
(crdb_id, &[zed_id]),
|
||||
(livestreaming_id, &[zed_id, crdb_id]),
|
||||
(livestreaming_dag_id, &[zed_id, crdb_id, livestreaming_id]),
|
||||
(gpui2_id, &[zed_id]),
|
||||
],
|
||||
);
|
||||
|
||||
// Attempt to make a cycle
|
||||
assert!(db
|
||||
.link_channel(a_id, zed_id, livestreaming_id)
|
||||
.await
|
||||
.is_err());
|
||||
|
||||
// // ========================================================================
|
||||
// // Make a link
|
||||
// db.link_channel(a_id, livestreaming_id, zed_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // DAG is now:
|
||||
// // /- gpui2
|
||||
// // zed -- crdb - livestreaming - livestreaming_dag
|
||||
// // \---------/
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // ========================================================================
|
||||
// // Create a new channel below a channel with multiple parents
|
||||
// let livestreaming_dag_sub_id = db
|
||||
// .create_channel("livestreaming_dag_sub", Some(livestreaming_dag_id), a_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // DAG is now:
|
||||
// // /- gpui2
|
||||
// // zed -- crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
|
||||
// // \---------/
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // ========================================================================
|
||||
// // Test a complex DAG by making another link
|
||||
// let returned_channels = db
|
||||
// .link_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // DAG is now:
|
||||
// // /- gpui2 /---------------------\
|
||||
// // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
|
||||
// // \--------/
|
||||
|
||||
// // make sure we're getting just the new link
|
||||
// // Not using the assert_dag helper because we want to make sure we're returning the full data
|
||||
// pretty_assertions::assert_eq!(
|
||||
// returned_channels,
|
||||
// graph(
|
||||
// &[(
|
||||
// livestreaming_dag_sub_id,
|
||||
// "livestreaming_dag_sub",
|
||||
// ChannelRole::Admin
|
||||
// )],
|
||||
// &[(livestreaming_dag_sub_id, livestreaming_id)]
|
||||
// )
|
||||
// );
|
||||
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // ========================================================================
|
||||
// // Test a complex DAG by making another link
|
||||
// let returned_channels = db
|
||||
// .link_channel(a_id, livestreaming_id, gpui2_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // DAG is now:
|
||||
// // /- gpui2 -\ /---------------------\
|
||||
// // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub_id
|
||||
// // \---------/
|
||||
|
||||
// // Make sure that we're correctly getting the full sub-dag
|
||||
// pretty_assertions::assert_eq!(
|
||||
// returned_channels,
|
||||
// graph(
|
||||
// &[
|
||||
// (livestreaming_id, "livestreaming", ChannelRole::Admin),
|
||||
// (
|
||||
// livestreaming_dag_id,
|
||||
// "livestreaming_dag",
|
||||
// ChannelRole::Admin
|
||||
// ),
|
||||
// (
|
||||
// livestreaming_dag_sub_id,
|
||||
// "livestreaming_dag_sub",
|
||||
// ChannelRole::Admin
|
||||
// ),
|
||||
// ],
|
||||
// &[
|
||||
// (livestreaming_id, gpui2_id),
|
||||
// (livestreaming_dag_id, livestreaming_id),
|
||||
// (livestreaming_dag_sub_id, livestreaming_id),
|
||||
// (livestreaming_dag_sub_id, livestreaming_dag_id),
|
||||
// ]
|
||||
// )
|
||||
// );
|
||||
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_id, Some(gpui2_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // ========================================================================
|
||||
// // Test unlinking in a complex DAG by removing the inner link
|
||||
// db.unlink_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // DAG is now:
|
||||
// // /- gpui2 -\
|
||||
// // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// // \---------/
|
||||
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(gpui2_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // ========================================================================
|
||||
// // Test unlinking in a complex DAG by removing the inner link
|
||||
// db.unlink_channel(a_id, livestreaming_id, gpui2_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // DAG is now:
|
||||
// // /- gpui2
|
||||
// // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// // \---------/
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // ========================================================================
|
||||
// // Test moving DAG nodes by moving livestreaming to be below gpui2
|
||||
// db.move_channel(livestreaming_id, Some(crdb_id), gpui2_id, a_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // DAG is now:
|
||||
// // /- gpui2 -- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// // zed - crdb /
|
||||
// // \---------/
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(gpui2_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // ========================================================================
|
||||
// // Deleting a channel should not delete children that still have other parents
|
||||
// db.delete_channel(gpui2_id, a_id).await.unwrap();
|
||||
|
||||
// // DAG is now:
|
||||
// // zed - crdb
|
||||
// // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // ========================================================================
|
||||
// // Unlinking a channel from it's parent should automatically promote it to a root channel
|
||||
// db.unlink_channel(a_id, crdb_id, zed_id).await.unwrap();
|
||||
|
||||
// // DAG is now:
|
||||
// // crdb
|
||||
// // zed
|
||||
// // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, None),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // ========================================================================
|
||||
// // You should be able to move a root channel into a non-root channel
|
||||
// db.link_channel(a_id, crdb_id, zed_id).await.unwrap();
|
||||
|
||||
// // DAG is now:
|
||||
// // zed - crdb
|
||||
// // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // ========================================================================
|
||||
// // Prep for DAG deletion test
|
||||
// db.link_channel(a_id, livestreaming_id, crdb_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // DAG is now:
|
||||
// // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// // \--------/
|
||||
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // Deleting the parent of a DAG should delete the whole DAG:
|
||||
// db.delete_channel(zed_id, a_id).await.unwrap();
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
|
||||
// assert!(result.channels.is_empty())
|
||||
}
|
||||
|
||||
test_both_dbs!(
|
||||
|
@ -743,37 +424,32 @@ async fn test_db_channel_moving_bugs(db: &Arc<Database>) {
|
|||
|
||||
// Move to same parent should be a no-op
|
||||
assert!(db
|
||||
.move_channel(projects_id, Some(zed_id), zed_id, user_id)
|
||||
.move_channel(projects_id, Some(zed_id), user_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.is_none());
|
||||
|
||||
let result = db.get_channels_for_user(user_id).await.unwrap();
|
||||
assert_dag(
|
||||
assert_channel_tree(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(projects_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(projects_id)),
|
||||
(zed_id, &[]),
|
||||
(projects_id, &[zed_id]),
|
||||
(livestreaming_id, &[zed_id, projects_id]),
|
||||
],
|
||||
);
|
||||
|
||||
// Stranding a channel should retain it's sub channels
|
||||
// Commented out as we don't fix permissions when this happens yet.
|
||||
//
|
||||
// db.unlink_channel(user_id, projects_id, zed_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// let result = db.get_channels_for_user(user_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (projects_id, None),
|
||||
// (livestreaming_id, Some(projects_id)),
|
||||
// ],
|
||||
// );
|
||||
// Move the project channel to the root
|
||||
db.move_channel(projects_id, None, user_id).await.unwrap();
|
||||
let result = db.get_channels_for_user(user_id).await.unwrap();
|
||||
assert_channel_tree(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, &[]),
|
||||
(projects_id, &[]),
|
||||
(livestreaming_id, &[projects_id]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
test_both_dbs!(
|
||||
|
@ -788,44 +464,52 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
|
|||
let guest = new_test_user(db, "guest@example.com").await;
|
||||
|
||||
let zed_channel = db.create_root_channel("zed", admin).await.unwrap();
|
||||
let active_channel = db
|
||||
let active_channel_id = db
|
||||
.create_sub_channel("active", zed_channel, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
let vim_channel = db
|
||||
.create_sub_channel("vim", active_channel, admin)
|
||||
let vim_channel_id = db
|
||||
.create_sub_channel("vim", active_channel_id, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
db.set_channel_visibility(vim_channel, crate::db::ChannelVisibility::Public, admin)
|
||||
db.set_channel_visibility(vim_channel_id, crate::db::ChannelVisibility::Public, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
db.invite_channel_member(active_channel, member, admin, ChannelRole::Member)
|
||||
db.invite_channel_member(active_channel_id, member, admin, ChannelRole::Member)
|
||||
.await
|
||||
.unwrap();
|
||||
db.invite_channel_member(vim_channel, guest, admin, ChannelRole::Guest)
|
||||
db.invite_channel_member(vim_channel_id, guest, admin, ChannelRole::Guest)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
db.respond_to_channel_invite(active_channel, member, true)
|
||||
db.respond_to_channel_invite(active_channel_id, member, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
db.transaction(|tx| async move {
|
||||
db.check_user_is_channel_participant(vim_channel, admin, &*tx)
|
||||
.await
|
||||
db.check_user_is_channel_participant(
|
||||
&db.get_channel_internal(vim_channel_id, &*tx).await?,
|
||||
admin,
|
||||
&*tx,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
db.transaction(|tx| async move {
|
||||
db.check_user_is_channel_participant(vim_channel, member, &*tx)
|
||||
.await
|
||||
db.check_user_is_channel_participant(
|
||||
&db.get_channel_internal(vim_channel_id, &*tx).await?,
|
||||
member,
|
||||
&*tx,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut members = db
|
||||
.get_channel_participant_details(vim_channel, admin)
|
||||
.get_channel_participant_details(vim_channel_id, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -852,38 +536,49 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
|
|||
]
|
||||
);
|
||||
|
||||
db.respond_to_channel_invite(vim_channel, guest, true)
|
||||
db.respond_to_channel_invite(vim_channel_id, guest, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
db.transaction(|tx| async move {
|
||||
db.check_user_is_channel_participant(vim_channel, guest, &*tx)
|
||||
.await
|
||||
db.check_user_is_channel_participant(
|
||||
&db.get_channel_internal(vim_channel_id, &*tx).await?,
|
||||
guest,
|
||||
&*tx,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let channels = db.get_channels_for_user(guest).await.unwrap().channels;
|
||||
assert_dag(channels, &[(vim_channel, None)]);
|
||||
assert_channel_tree(channels, &[(vim_channel_id, &[])]);
|
||||
let channels = db.get_channels_for_user(member).await.unwrap().channels;
|
||||
assert_dag(
|
||||
assert_channel_tree(
|
||||
channels,
|
||||
&[(active_channel, None), (vim_channel, Some(active_channel))],
|
||||
&[
|
||||
(active_channel_id, &[]),
|
||||
(vim_channel_id, &[active_channel_id]),
|
||||
],
|
||||
);
|
||||
|
||||
db.set_channel_member_role(vim_channel, admin, guest, ChannelRole::Banned)
|
||||
db.set_channel_member_role(vim_channel_id, admin, guest, ChannelRole::Banned)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(db
|
||||
.transaction(|tx| async move {
|
||||
db.check_user_is_channel_participant(vim_channel, guest, &*tx)
|
||||
.await
|
||||
db.check_user_is_channel_participant(
|
||||
&db.get_channel_internal(vim_channel_id, &*tx).await.unwrap(),
|
||||
guest,
|
||||
&*tx,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.is_err());
|
||||
|
||||
let mut members = db
|
||||
.get_channel_participant_details(vim_channel, admin)
|
||||
.get_channel_participant_details(vim_channel_id, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -910,7 +605,7 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
|
|||
]
|
||||
);
|
||||
|
||||
db.remove_channel_member(vim_channel, guest, admin)
|
||||
db.remove_channel_member(vim_channel_id, guest, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -924,7 +619,7 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
|
|||
|
||||
// currently people invited to parent channels are not shown here
|
||||
let mut members = db
|
||||
.get_channel_participant_details(vim_channel, admin)
|
||||
.get_channel_participant_details(vim_channel_id, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -951,28 +646,42 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
|
|||
.unwrap();
|
||||
|
||||
db.transaction(|tx| async move {
|
||||
db.check_user_is_channel_participant(zed_channel, guest, &*tx)
|
||||
.await
|
||||
db.check_user_is_channel_participant(
|
||||
&db.get_channel_internal(zed_channel, &*tx).await.unwrap(),
|
||||
guest,
|
||||
&*tx,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(db
|
||||
.transaction(|tx| async move {
|
||||
db.check_user_is_channel_participant(active_channel, guest, &*tx)
|
||||
.await
|
||||
db.check_user_is_channel_participant(
|
||||
&db.get_channel_internal(active_channel_id, &*tx)
|
||||
.await
|
||||
.unwrap(),
|
||||
guest,
|
||||
&*tx,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.is_err(),);
|
||||
|
||||
db.transaction(|tx| async move {
|
||||
db.check_user_is_channel_participant(vim_channel, guest, &*tx)
|
||||
.await
|
||||
db.check_user_is_channel_participant(
|
||||
&db.get_channel_internal(vim_channel_id, &*tx).await.unwrap(),
|
||||
guest,
|
||||
&*tx,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut members = db
|
||||
.get_channel_participant_details(vim_channel, admin)
|
||||
.get_channel_participant_details(vim_channel_id, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1000,9 +709,9 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
|
|||
);
|
||||
|
||||
let channels = db.get_channels_for_user(guest).await.unwrap().channels;
|
||||
assert_dag(
|
||||
assert_channel_tree(
|
||||
channels,
|
||||
&[(zed_channel, None), (vim_channel, Some(zed_channel))],
|
||||
&[(zed_channel, &[]), (vim_channel_id, &[zed_channel])],
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1047,15 +756,20 @@ async fn test_user_joins_correct_channel(db: &Arc<Database>) {
|
|||
let most_public = db
|
||||
.transaction(|tx| async move {
|
||||
Ok(db
|
||||
.public_path_to_channel(vim_channel, &tx)
|
||||
.public_ancestors_including_self(
|
||||
&db.get_channel_internal(vim_channel, &*tx).await.unwrap(),
|
||||
&tx,
|
||||
)
|
||||
.await?
|
||||
.first()
|
||||
.cloned())
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.id;
|
||||
|
||||
assert_eq!(most_public, Some(zed_channel))
|
||||
assert_eq!(most_public, zed_channel)
|
||||
}
|
||||
|
||||
test_both_dbs!(
|
||||
|
@ -1092,26 +806,14 @@ async fn test_guest_access(db: &Arc<Database>) {
|
|||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_dag(actual: ChannelGraph, expected: &[(ChannelId, Option<ChannelId>)]) {
|
||||
let mut actual_map: HashMap<ChannelId, HashSet<ChannelId>> = HashMap::default();
|
||||
for channel in actual.channels {
|
||||
actual_map.insert(channel.id, HashSet::default());
|
||||
}
|
||||
for edge in actual.edges {
|
||||
actual_map
|
||||
.get_mut(&ChannelId::from_proto(edge.channel_id))
|
||||
.unwrap()
|
||||
.insert(ChannelId::from_proto(edge.parent_id));
|
||||
}
|
||||
|
||||
let mut expected_map: HashMap<ChannelId, HashSet<ChannelId>> = HashMap::default();
|
||||
|
||||
for (child, parent) in expected {
|
||||
let entry = expected_map.entry(*child).or_default();
|
||||
if let Some(parent) = parent {
|
||||
entry.insert(*parent);
|
||||
}
|
||||
}
|
||||
|
||||
pretty_assertions::assert_eq!(actual_map, expected_map)
|
||||
fn assert_channel_tree(actual: Vec<Channel>, expected: &[(ChannelId, &[ChannelId])]) {
|
||||
let actual = actual
|
||||
.iter()
|
||||
.map(|channel| (channel.id, channel.parent_path.as_slice()))
|
||||
.collect::<Vec<_>>();
|
||||
pretty_assertions::assert_eq!(
|
||||
actual,
|
||||
expected.to_vec(),
|
||||
"wrong channel ids and parent paths"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -277,8 +277,6 @@ impl Server {
|
|||
.add_request_handler(get_channel_messages_by_id)
|
||||
.add_request_handler(get_notifications)
|
||||
.add_request_handler(mark_notification_as_read)
|
||||
.add_request_handler(link_channel)
|
||||
.add_request_handler(unlink_channel)
|
||||
.add_request_handler(move_channel)
|
||||
.add_request_handler(follow)
|
||||
.add_message_handler(unfollow)
|
||||
|
@ -2472,52 +2470,19 @@ async fn rename_channel(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Implement in terms of symlinks
|
||||
// Current behavior of this is more like 'Move root channel'
|
||||
async fn link_channel(
|
||||
request: proto::LinkChannel,
|
||||
response: Response<proto::LinkChannel>,
|
||||
session: Session,
|
||||
) -> Result<()> {
|
||||
let db = session.db().await;
|
||||
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||
let to = ChannelId::from_proto(request.to);
|
||||
|
||||
let result = db
|
||||
.move_channel(channel_id, None, to, session.user_id)
|
||||
.await?;
|
||||
drop(db);
|
||||
|
||||
notify_channel_moved(result, session).await?;
|
||||
|
||||
response.send(Ack {})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Implement in terms of symlinks
|
||||
async fn unlink_channel(
|
||||
_request: proto::UnlinkChannel,
|
||||
_response: Response<proto::UnlinkChannel>,
|
||||
_session: Session,
|
||||
) -> Result<()> {
|
||||
Err(anyhow!("unimplemented").into())
|
||||
}
|
||||
|
||||
async fn move_channel(
|
||||
request: proto::MoveChannel,
|
||||
response: Response<proto::MoveChannel>,
|
||||
session: Session,
|
||||
) -> Result<()> {
|
||||
let db = session.db().await;
|
||||
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||
let from_parent = ChannelId::from_proto(request.from);
|
||||
let to = ChannelId::from_proto(request.to);
|
||||
let to = request.to.map(ChannelId::from_proto);
|
||||
|
||||
let result = db
|
||||
.move_channel(channel_id, Some(from_parent), to, session.user_id)
|
||||
let result = session
|
||||
.db()
|
||||
.await
|
||||
.move_channel(channel_id, to, session.user_id)
|
||||
.await?;
|
||||
drop(db);
|
||||
|
||||
notify_channel_moved(result, session).await?;
|
||||
|
||||
|
@ -3244,18 +3209,12 @@ fn build_channels_update(
|
|||
) -> proto::UpdateChannels {
|
||||
let mut update = proto::UpdateChannels::default();
|
||||
|
||||
for channel in channels.channels.channels {
|
||||
update.channels.push(proto::Channel {
|
||||
id: channel.id.to_proto(),
|
||||
name: channel.name,
|
||||
visibility: channel.visibility.into(),
|
||||
role: channel.role.into(),
|
||||
});
|
||||
for channel in channels.channels {
|
||||
update.channels.push(channel.to_proto());
|
||||
}
|
||||
|
||||
update.unseen_channel_buffer_changes = channels.unseen_buffer_changes;
|
||||
update.unseen_channel_messages = channels.channel_messages;
|
||||
update.insert_edge = channels.channels.edges;
|
||||
|
||||
for (channel_id, participants) in channels.channel_participants {
|
||||
update
|
||||
|
@ -3267,12 +3226,7 @@ fn build_channels_update(
|
|||
}
|
||||
|
||||
for channel in channel_invites {
|
||||
update.channel_invitations.push(proto::Channel {
|
||||
id: channel.id.to_proto(),
|
||||
name: channel.name,
|
||||
visibility: channel.visibility.into(),
|
||||
role: channel.role.into(),
|
||||
});
|
||||
update.channel_invitations.push(channel.to_proto());
|
||||
}
|
||||
|
||||
update
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
tests::TestServer,
|
||||
};
|
||||
use call::ActiveCall;
|
||||
use channel::{Channel, ACKNOWLEDGE_DEBOUNCE_INTERVAL};
|
||||
use channel::ACKNOWLEDGE_DEBOUNCE_INTERVAL;
|
||||
use client::ParticipantIndex;
|
||||
use client::{Collaborator, UserId};
|
||||
use collab_ui::channel_view::ChannelView;
|
||||
|
@ -11,10 +11,7 @@ use collections::HashMap;
|
|||
use editor::{Anchor, Editor, ToOffset};
|
||||
use futures::future;
|
||||
use gpui::{executor::Deterministic, ModelHandle, TestAppContext, ViewContext};
|
||||
use rpc::{
|
||||
proto::{self, PeerId},
|
||||
RECEIVE_TIMEOUT,
|
||||
};
|
||||
use rpc::{proto::PeerId, RECEIVE_TIMEOUT};
|
||||
use serde_json::json;
|
||||
use std::{ops::Range, sync::Arc};
|
||||
|
||||
|
@ -411,10 +408,7 @@ async fn test_channel_buffer_disconnect(
|
|||
deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
|
||||
|
||||
channel_buffer_a.update(cx_a, |buffer, cx| {
|
||||
assert_eq!(
|
||||
buffer.channel(cx).unwrap().as_ref(),
|
||||
&channel(channel_id, "the-channel", proto::ChannelRole::Admin)
|
||||
);
|
||||
assert_eq!(buffer.channel(cx).unwrap().name, "the-channel");
|
||||
assert!(!buffer.is_connected());
|
||||
});
|
||||
|
||||
|
@ -441,17 +435,6 @@ async fn test_channel_buffer_disconnect(
|
|||
});
|
||||
}
|
||||
|
||||
fn channel(id: u64, name: &'static str, role: proto::ChannelRole) -> Channel {
|
||||
Channel {
|
||||
id,
|
||||
role,
|
||||
name: name.to_string(),
|
||||
visibility: proto::ChannelVisibility::Members,
|
||||
unseen_note_version: None,
|
||||
unseen_message_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_rejoin_channel_buffer(
|
||||
deterministic: Arc<Deterministic>,
|
||||
|
|
|
@ -61,10 +61,7 @@ async fn test_core_channels(
|
|||
);
|
||||
|
||||
client_b.channel_store().read_with(cx_b, |channels, _| {
|
||||
assert!(channels
|
||||
.channel_dag_entries()
|
||||
.collect::<Vec<_>>()
|
||||
.is_empty())
|
||||
assert!(channels.ordered_channels().collect::<Vec<_>>().is_empty())
|
||||
});
|
||||
|
||||
// Invite client B to channel A as client A.
|
||||
|
@ -1019,7 +1016,7 @@ async fn test_channel_link_notifications(
|
|||
client_a
|
||||
.channel_store()
|
||||
.update(cx_a, |channel_store, cx| {
|
||||
channel_store.link_channel(vim_channel, active_channel, cx)
|
||||
channel_store.move_channel(vim_channel, Some(active_channel), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -1054,7 +1051,7 @@ async fn test_channel_link_notifications(
|
|||
client_a
|
||||
.channel_store()
|
||||
.update(cx_a, |channel_store, cx| {
|
||||
channel_store.link_channel(helix_channel, vim_channel, cx)
|
||||
channel_store.move_channel(helix_channel, Some(vim_channel), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -1427,7 +1424,7 @@ async fn test_channel_moving(
|
|||
client_a
|
||||
.channel_store()
|
||||
.update(cx_a, |channel_store, cx| {
|
||||
channel_store.move_channel(channel_d_id, channel_c_id, channel_b_id, cx)
|
||||
channel_store.move_channel(channel_d_id, Some(channel_b_id), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -1445,189 +1442,6 @@ async fn test_channel_moving(
|
|||
(channel_d_id, 2),
|
||||
],
|
||||
);
|
||||
|
||||
// TODO: Restore this test once we have a way to make channel symlinks
|
||||
// client_a
|
||||
// .channel_store()
|
||||
// .update(cx_a, |channel_store, cx| {
|
||||
// channel_store.link_channel(channel_d_id, channel_c_id, cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // Current shape for A:
|
||||
// // /------\
|
||||
// // a - b -- c -- d
|
||||
// assert_channels_list_shape(
|
||||
// client_a.channel_store(),
|
||||
// cx_a,
|
||||
// &[
|
||||
// (channel_a_id, 0),
|
||||
// (channel_b_id, 1),
|
||||
// (channel_c_id, 2),
|
||||
// (channel_d_id, 3),
|
||||
// (channel_d_id, 2),
|
||||
// ],
|
||||
// );
|
||||
//
|
||||
// let b_channels = server
|
||||
// .make_channel_tree(
|
||||
// &[
|
||||
// ("channel-mu", None),
|
||||
// ("channel-gamma", Some("channel-mu")),
|
||||
// ("channel-epsilon", Some("channel-mu")),
|
||||
// ],
|
||||
// (&client_b, cx_b),
|
||||
// )
|
||||
// .await;
|
||||
// let channel_mu_id = b_channels[0];
|
||||
// let channel_ga_id = b_channels[1];
|
||||
// let channel_ep_id = b_channels[2];
|
||||
|
||||
// // Current shape for B:
|
||||
// // /- ep
|
||||
// // mu -- ga
|
||||
// assert_channels_list_shape(
|
||||
// client_b.channel_store(),
|
||||
// cx_b,
|
||||
// &[(channel_mu_id, 0), (channel_ep_id, 1), (channel_ga_id, 1)],
|
||||
// );
|
||||
|
||||
// client_a
|
||||
// .add_admin_to_channel((&client_b, cx_b), channel_b_id, cx_a)
|
||||
// .await;
|
||||
|
||||
// // Current shape for B:
|
||||
// // /- ep
|
||||
// // mu -- ga
|
||||
// // /---------\
|
||||
// // b -- c -- d
|
||||
// assert_channels_list_shape(
|
||||
// client_b.channel_store(),
|
||||
// cx_b,
|
||||
// &[
|
||||
// // New channels from a
|
||||
// (channel_b_id, 0),
|
||||
// (channel_c_id, 1),
|
||||
// (channel_d_id, 2),
|
||||
// (channel_d_id, 1),
|
||||
// // B's old channels
|
||||
// (channel_mu_id, 0),
|
||||
// (channel_ep_id, 1),
|
||||
// (channel_ga_id, 1),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// client_b
|
||||
// .add_admin_to_channel((&client_c, cx_c), channel_ep_id, cx_b)
|
||||
// .await;
|
||||
|
||||
// // Current shape for C:
|
||||
// // - ep
|
||||
// assert_channels_list_shape(client_c.channel_store(), cx_c, &[(channel_ep_id, 0)]);
|
||||
|
||||
// client_b
|
||||
// .channel_store()
|
||||
// .update(cx_b, |channel_store, cx| {
|
||||
// channel_store.link_channel(channel_b_id, channel_ep_id, cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // Current shape for B:
|
||||
// // /---------\
|
||||
// // /- ep -- b -- c -- d
|
||||
// // mu -- ga
|
||||
// assert_channels_list_shape(
|
||||
// client_b.channel_store(),
|
||||
// cx_b,
|
||||
// &[
|
||||
// (channel_mu_id, 0),
|
||||
// (channel_ep_id, 1),
|
||||
// (channel_b_id, 2),
|
||||
// (channel_c_id, 3),
|
||||
// (channel_d_id, 4),
|
||||
// (channel_d_id, 3),
|
||||
// (channel_ga_id, 1),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // Current shape for C:
|
||||
// // /---------\
|
||||
// // ep -- b -- c -- d
|
||||
// assert_channels_list_shape(
|
||||
// client_c.channel_store(),
|
||||
// cx_c,
|
||||
// &[
|
||||
// (channel_ep_id, 0),
|
||||
// (channel_b_id, 1),
|
||||
// (channel_c_id, 2),
|
||||
// (channel_d_id, 3),
|
||||
// (channel_d_id, 2),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// client_b
|
||||
// .channel_store()
|
||||
// .update(cx_b, |channel_store, cx| {
|
||||
// channel_store.link_channel(channel_ga_id, channel_b_id, cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // Current shape for B:
|
||||
// // /---------\
|
||||
// // /- ep -- b -- c -- d
|
||||
// // / \
|
||||
// // mu ---------- ga
|
||||
// assert_channels_list_shape(
|
||||
// client_b.channel_store(),
|
||||
// cx_b,
|
||||
// &[
|
||||
// (channel_mu_id, 0),
|
||||
// (channel_ep_id, 1),
|
||||
// (channel_b_id, 2),
|
||||
// (channel_c_id, 3),
|
||||
// (channel_d_id, 4),
|
||||
// (channel_d_id, 3),
|
||||
// (channel_ga_id, 3),
|
||||
// (channel_ga_id, 1),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // Current shape for A:
|
||||
// // /------\
|
||||
// // a - b -- c -- d
|
||||
// // \-- ga
|
||||
// assert_channels_list_shape(
|
||||
// client_a.channel_store(),
|
||||
// cx_a,
|
||||
// &[
|
||||
// (channel_a_id, 0),
|
||||
// (channel_b_id, 1),
|
||||
// (channel_c_id, 2),
|
||||
// (channel_d_id, 3),
|
||||
// (channel_d_id, 2),
|
||||
// (channel_ga_id, 2),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// // Current shape for C:
|
||||
// // /-------\
|
||||
// // ep -- b -- c -- d
|
||||
// // \-- ga
|
||||
// assert_channels_list_shape(
|
||||
// client_c.channel_store(),
|
||||
// cx_c,
|
||||
// &[
|
||||
// (channel_ep_id, 0),
|
||||
// (channel_b_id, 1),
|
||||
// (channel_c_id, 2),
|
||||
// (channel_d_id, 3),
|
||||
// (channel_d_id, 2),
|
||||
// (channel_ga_id, 2),
|
||||
// ],
|
||||
// );
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -1667,7 +1481,7 @@ fn assert_channels(
|
|||
) {
|
||||
let actual = channel_store.read_with(cx, |store, _| {
|
||||
store
|
||||
.channel_dag_entries()
|
||||
.ordered_channels()
|
||||
.map(|(depth, channel)| ExpectedChannel {
|
||||
depth,
|
||||
name: channel.name.clone(),
|
||||
|
@ -1689,7 +1503,7 @@ fn assert_channels_list_shape(
|
|||
|
||||
let actual = channel_store.read_with(cx, |store, _| {
|
||||
store
|
||||
.channel_dag_entries()
|
||||
.ordered_channels()
|
||||
.map(|(depth, channel)| (channel.id, depth))
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
|
|
@ -83,7 +83,7 @@ impl RandomizedTest for RandomChannelBufferTest {
|
|||
match rng.gen_range(0..100_u32) {
|
||||
0..=29 => {
|
||||
let channel_name = client.channel_store().read_with(cx, |store, cx| {
|
||||
store.channel_dag_entries().find_map(|(_, channel)| {
|
||||
store.ordered_channels().find_map(|(_, channel)| {
|
||||
if store.has_open_channel_buffer(channel.id, cx) {
|
||||
None
|
||||
} else {
|
||||
|
@ -131,7 +131,7 @@ impl RandomizedTest for RandomChannelBufferTest {
|
|||
ChannelBufferOperation::JoinChannelNotes { channel_name } => {
|
||||
let buffer = client.channel_store().update(cx, |store, cx| {
|
||||
let channel_id = store
|
||||
.channel_dag_entries()
|
||||
.ordered_channels()
|
||||
.find(|(_, c)| c.name == channel_name)
|
||||
.unwrap()
|
||||
.1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue