WIP: Send the channel name and the channel edges seperately, so we're not repeating them constantly
This commit is currently broken and includes debug data for a failed attempt at rewriting the insert_edge logic
This commit is contained in:
parent
363867c65b
commit
5f9c56c8b0
13 changed files with 437 additions and 760 deletions
|
@ -1,3 +1,4 @@
|
|||
use rpc::proto::ChannelEdge;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use super::*;
|
||||
|
@ -326,7 +327,6 @@ impl Database {
|
|||
.map(|channel| Channel {
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
parent_id: None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -335,12 +335,12 @@ impl Database {
|
|||
.await
|
||||
}
|
||||
|
||||
async fn get_channels_internal(
|
||||
async fn get_channel_graph(
|
||||
&self,
|
||||
parents_by_child_id: ChannelDescendants,
|
||||
trim_dangling_parents: bool,
|
||||
tx: &DatabaseTransaction,
|
||||
) -> Result<Vec<Channel>> {
|
||||
) -> Result<ChannelGraph> {
|
||||
let mut channels = Vec::with_capacity(parents_by_child_id.len());
|
||||
{
|
||||
let mut rows = channel::Entity::find()
|
||||
|
@ -349,49 +349,36 @@ impl Database {
|
|||
.await?;
|
||||
while let Some(row) = rows.next().await {
|
||||
let row = row?;
|
||||
// As these rows are pulled from the map's keys, this unwrap is safe.
|
||||
let parents = parents_by_child_id.get(&row.id).unwrap();
|
||||
if parents.len() > 0 {
|
||||
let mut added_channel = false;
|
||||
for parent in parents.iter() {
|
||||
// Trim out any dangling parent pointers.
|
||||
// That the user doesn't have access to
|
||||
if trim_dangling_parents {
|
||||
if parents_by_child_id.contains_key(parent) {
|
||||
added_channel = true;
|
||||
channels.push(Channel {
|
||||
id: row.id,
|
||||
name: row.name.clone(),
|
||||
parent_id: Some(*parent),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
added_channel = true;
|
||||
channels.push(Channel {
|
||||
id: row.id,
|
||||
name: row.name.clone(),
|
||||
parent_id: Some(*parent),
|
||||
});
|
||||
}
|
||||
}
|
||||
if !added_channel {
|
||||
channels.push(Channel {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
parent_id: None,
|
||||
channels.push(Channel {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let mut edges = Vec::with_capacity(parents_by_child_id.len());
|
||||
for (channel, parents) in parents_by_child_id.iter() {
|
||||
for parent in parents.into_iter() {
|
||||
if trim_dangling_parents {
|
||||
if parents_by_child_id.contains_key(parent) {
|
||||
edges.push(ChannelEdge {
|
||||
channel_id: channel.to_proto(),
|
||||
parent_id: parent.to_proto(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
channels.push(Channel {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
parent_id: None,
|
||||
edges.push(ChannelEdge {
|
||||
channel_id: channel.to_proto(),
|
||||
parent_id: parent.to_proto(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(channels)
|
||||
Ok(ChannelGraph {
|
||||
channels,
|
||||
edges,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_channels_for_user(&self, user_id: UserId) -> Result<ChannelsForUser> {
|
||||
|
@ -407,51 +394,74 @@ impl Database {
|
|||
.all(&*tx)
|
||||
.await?;
|
||||
|
||||
let parents_by_child_id = self
|
||||
.get_channel_descendants(channel_memberships.iter().map(|m| m.channel_id), &*tx)
|
||||
.await?;
|
||||
|
||||
let channels_with_admin_privileges = channel_memberships
|
||||
.iter()
|
||||
.filter_map(|membership| membership.admin.then_some(membership.channel_id))
|
||||
.collect();
|
||||
|
||||
let channels = self
|
||||
.get_channels_internal(parents_by_child_id, true, &tx)
|
||||
.await?;
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||
enum QueryUserIdsAndChannelIds {
|
||||
ChannelId,
|
||||
UserId,
|
||||
}
|
||||
|
||||
let mut channel_participants: HashMap<ChannelId, Vec<UserId>> = HashMap::default();
|
||||
{
|
||||
let mut rows = room_participant::Entity::find()
|
||||
.inner_join(room::Entity)
|
||||
.filter(room::Column::ChannelId.is_in(channels.iter().map(|c| c.id)))
|
||||
.select_only()
|
||||
.column(room::Column::ChannelId)
|
||||
.column(room_participant::Column::UserId)
|
||||
.into_values::<_, QueryUserIdsAndChannelIds>()
|
||||
.stream(&*tx)
|
||||
.await?;
|
||||
while let Some(row) = rows.next().await {
|
||||
let row: (ChannelId, UserId) = row?;
|
||||
channel_participants.entry(row.0).or_default().push(row.1)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ChannelsForUser {
|
||||
channels,
|
||||
channel_participants,
|
||||
channels_with_admin_privileges,
|
||||
})
|
||||
self.get_user_channels(channel_memberships, user_id, &tx).await
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_channel_for_user(&self, channel_id: ChannelId, user_id: UserId) -> Result<ChannelsForUser> {
|
||||
self.transaction(|tx| async move {
|
||||
let tx = tx;
|
||||
|
||||
let channel_membership = channel_member::Entity::find()
|
||||
.filter(
|
||||
channel_member::Column::UserId
|
||||
.eq(user_id)
|
||||
.and(channel_member::Column::ChannelId.eq(channel_id))
|
||||
.and(channel_member::Column::Accepted.eq(true)),
|
||||
)
|
||||
.all(&*tx)
|
||||
.await?;
|
||||
|
||||
self.get_user_channels(channel_membership, user_id, &tx).await
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_user_channels(&self, channel_memberships: Vec<channel_member::Model>, user_id: UserId, tx: &DatabaseTransaction) -> Result<ChannelsForUser> {
|
||||
let parents_by_child_id = self
|
||||
.get_channel_descendants(channel_memberships.iter().map(|m| m.channel_id), &*tx)
|
||||
.await?;
|
||||
|
||||
let channels_with_admin_privileges = channel_memberships
|
||||
.iter()
|
||||
.filter_map(|membership| membership.admin.then_some(membership.channel_id))
|
||||
.collect();
|
||||
|
||||
let graph = self
|
||||
.get_channel_graph(parents_by_child_id, true, &tx)
|
||||
.await?;
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||
enum QueryUserIdsAndChannelIds {
|
||||
ChannelId,
|
||||
UserId,
|
||||
}
|
||||
|
||||
let mut channel_participants: HashMap<ChannelId, Vec<UserId>> = HashMap::default();
|
||||
{
|
||||
let mut rows = room_participant::Entity::find()
|
||||
.inner_join(room::Entity)
|
||||
.filter(room::Column::ChannelId.is_in(graph.channels.iter().map(|c| c.id)))
|
||||
.select_only()
|
||||
.column(room::Column::ChannelId)
|
||||
.column(room_participant::Column::UserId)
|
||||
.into_values::<_, QueryUserIdsAndChannelIds>()
|
||||
.stream(&*tx)
|
||||
.await?;
|
||||
while let Some(row) = rows.next().await {
|
||||
let row: (ChannelId, UserId) = row?;
|
||||
channel_participants.entry(row.0).or_default().push(row.1)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ChannelsForUser {
|
||||
channels: graph,
|
||||
channel_participants,
|
||||
channels_with_admin_privileges,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_channel_members(&self, id: ChannelId) -> Result<Vec<UserId>> {
|
||||
self.transaction(|tx| async move { self.get_channel_members_internal(id, &*tx).await })
|
||||
.await
|
||||
|
@ -759,7 +769,6 @@ impl Database {
|
|||
Channel {
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
parent_id: None,
|
||||
},
|
||||
is_accepted,
|
||||
)))
|
||||
|
@ -792,7 +801,7 @@ impl Database {
|
|||
user: UserId,
|
||||
channel: ChannelId,
|
||||
to: ChannelId,
|
||||
) -> Result<Vec<Channel>> {
|
||||
) -> Result<ChannelGraph> {
|
||||
self.transaction(|tx| async move {
|
||||
// Note that even with these maxed permissions, this linking operation
|
||||
// is still insecure because you can't remove someone's permissions to a
|
||||
|
@ -811,7 +820,7 @@ impl Database {
|
|||
channel: ChannelId,
|
||||
to: ChannelId,
|
||||
tx: &DatabaseTransaction,
|
||||
) -> Result<Vec<Channel>> {
|
||||
) -> Result<ChannelGraph> {
|
||||
self.check_user_is_channel_admin(to, user, &*tx).await?;
|
||||
|
||||
let to_ancestors = self.get_channel_ancestors(to, &*tx).await?;
|
||||
|
@ -881,7 +890,7 @@ impl Database {
|
|||
}
|
||||
|
||||
let channels = self
|
||||
.get_channels_internal(from_descendants, false, &*tx)
|
||||
.get_channel_graph(from_descendants, false, &*tx)
|
||||
.await?;
|
||||
|
||||
Ok(channels)
|
||||
|
@ -961,7 +970,7 @@ impl Database {
|
|||
channel: ChannelId,
|
||||
from: ChannelId,
|
||||
to: ChannelId,
|
||||
) -> Result<Vec<Channel>> {
|
||||
) -> Result<ChannelGraph> {
|
||||
self.transaction(|tx| async move {
|
||||
self.check_user_is_channel_admin(channel, user, &*tx)
|
||||
.await?;
|
||||
|
@ -982,6 +991,39 @@ enum QueryUserIds {
|
|||
UserId,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ChannelGraph {
|
||||
pub channels: Vec<Channel>,
|
||||
pub edges: Vec<ChannelEdge>,
|
||||
}
|
||||
|
||||
impl ChannelGraph {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.channels.is_empty() && self.edges.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl PartialEq for ChannelGraph {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// Order independent comparison for tests
|
||||
let channels_set = self.channels.iter().collect::<HashSet<_>>();
|
||||
let other_channels_set = other.channels.iter().collect::<HashSet<_>>();
|
||||
let edges_set = self.edges.iter().map(|edge| (edge.channel_id, edge.parent_id)).collect::<HashSet<_>>();
|
||||
let other_edges_set = other.edges.iter().map(|edge| (edge.channel_id, edge.parent_id)).collect::<HashSet<_>>();
|
||||
|
||||
channels_set == other_channels_set && edges_set == other_edges_set
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
impl PartialEq for ChannelGraph {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.channels == other.channels && self.edges == other.edges
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct SmallSet<T>(SmallVec<[T; 1]>);
|
||||
|
||||
impl<T> Deref for SmallSet<T> {
|
||||
|
@ -1008,7 +1050,7 @@ impl<T> SmallSet<T> {
|
|||
Err(ix) => {
|
||||
self.0.insert(ix, value);
|
||||
true
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ 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::Arc;
|
||||
|
@ -144,3 +145,27 @@ impl Drop for TestDb {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The second tuples are (channel_id, parent)
|
||||
fn graph(channels: &[(ChannelId, &'static str)], edges: &[(ChannelId, ChannelId)]) -> ChannelGraph {
|
||||
let mut graph = ChannelGraph {
|
||||
channels: vec![],
|
||||
edges: vec![],
|
||||
};
|
||||
|
||||
for (id, name) in channels {
|
||||
graph.channels.push(Channel {
|
||||
id: *id,
|
||||
name: name.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
for (channel, parent) in edges {
|
||||
graph.edges.push(ChannelEdge {
|
||||
channel_id: channel.to_proto(),
|
||||
parent_id: parent.to_proto(),
|
||||
})
|
||||
}
|
||||
|
||||
graph
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use collections::{HashMap, HashSet};
|
||||
use rpc::{proto, ConnectionId};
|
||||
use rpc::{
|
||||
proto::{self},
|
||||
ConnectionId,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
db::{Channel, ChannelId, Database, NewUserParams},
|
||||
db::{queries::channels::ChannelGraph, ChannelId, Database, NewUserParams, tests::graph},
|
||||
test_both_dbs,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
@ -82,70 +85,42 @@ async fn test_channels(db: &Arc<Database>) {
|
|||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_eq!(
|
||||
result.channels,
|
||||
vec![
|
||||
Channel {
|
||||
id: zed_id,
|
||||
name: "zed".to_string(),
|
||||
parent_id: None,
|
||||
},
|
||||
Channel {
|
||||
id: crdb_id,
|
||||
name: "crdb".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: livestreaming_id,
|
||||
name: "livestreaming".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: replace_id,
|
||||
name: "replace".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: rust_id,
|
||||
name: "rust".to_string(),
|
||||
parent_id: None,
|
||||
},
|
||||
Channel {
|
||||
id: cargo_id,
|
||||
name: "cargo".to_string(),
|
||||
parent_id: Some(rust_id),
|
||||
},
|
||||
Channel {
|
||||
id: cargo_ra_id,
|
||||
name: "cargo-ra".to_string(),
|
||||
parent_id: Some(cargo_id),
|
||||
}
|
||||
]
|
||||
graph(
|
||||
&[
|
||||
(zed_id, "zed"),
|
||||
(crdb_id, "crdb"),
|
||||
(livestreaming_id, "livestreaming"),
|
||||
(replace_id, "replace"),
|
||||
(rust_id, "rust"),
|
||||
(cargo_id, "cargo"),
|
||||
(cargo_ra_id, "cargo-ra")
|
||||
],
|
||||
&[
|
||||
(crdb_id, zed_id),
|
||||
(livestreaming_id, zed_id),
|
||||
(replace_id, zed_id),
|
||||
(cargo_id, rust_id),
|
||||
(cargo_ra_id, cargo_id),
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
let result = db.get_channels_for_user(b_id).await.unwrap();
|
||||
assert_eq!(
|
||||
result.channels,
|
||||
vec![
|
||||
Channel {
|
||||
id: zed_id,
|
||||
name: "zed".to_string(),
|
||||
parent_id: None,
|
||||
},
|
||||
Channel {
|
||||
id: crdb_id,
|
||||
name: "crdb".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: livestreaming_id,
|
||||
name: "livestreaming".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: replace_id,
|
||||
name: "replace".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
]
|
||||
graph(
|
||||
&[
|
||||
(zed_id, "zed"),
|
||||
(crdb_id, "crdb"),
|
||||
(livestreaming_id, "livestreaming"),
|
||||
(replace_id, "replace")
|
||||
],
|
||||
&[
|
||||
(crdb_id, zed_id),
|
||||
(livestreaming_id, zed_id),
|
||||
(replace_id, zed_id)
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
// Update member permissions
|
||||
|
@ -157,28 +132,19 @@ async fn test_channels(db: &Arc<Database>) {
|
|||
let result = db.get_channels_for_user(b_id).await.unwrap();
|
||||
assert_eq!(
|
||||
result.channels,
|
||||
vec![
|
||||
Channel {
|
||||
id: zed_id,
|
||||
name: "zed".to_string(),
|
||||
parent_id: None,
|
||||
},
|
||||
Channel {
|
||||
id: crdb_id,
|
||||
name: "crdb".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: livestreaming_id,
|
||||
name: "livestreaming".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: replace_id,
|
||||
name: "replace".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
]
|
||||
graph(
|
||||
&[
|
||||
(zed_id, "zed"),
|
||||
(crdb_id, "crdb"),
|
||||
(livestreaming_id, "livestreaming"),
|
||||
(replace_id, "replace")
|
||||
],
|
||||
&[
|
||||
(crdb_id, zed_id),
|
||||
(livestreaming_id, zed_id),
|
||||
(replace_id, zed_id)
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
// Remove a single channel
|
||||
|
@ -594,11 +560,10 @@ async fn test_db_channel_moving(db: &Arc<Database>) {
|
|||
// Not using the assert_dag helper because we want to make sure we're returning the full data
|
||||
pretty_assertions::assert_eq!(
|
||||
returned_channels,
|
||||
vec![Channel {
|
||||
id: livestreaming_dag_sub_id,
|
||||
name: "livestreaming_dag_sub".to_string(),
|
||||
parent_id: Some(livestreaming_id),
|
||||
}]
|
||||
graph(
|
||||
&[(livestreaming_dag_sub_id, "livestreaming_dag_sub")],
|
||||
&[(livestreaming_dag_sub_id, livestreaming_id)]
|
||||
)
|
||||
);
|
||||
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
|
@ -631,28 +596,19 @@ async fn test_db_channel_moving(db: &Arc<Database>) {
|
|||
// Make sure that we're correctly getting the full sub-dag
|
||||
pretty_assertions::assert_eq!(
|
||||
returned_channels,
|
||||
vec![
|
||||
Channel {
|
||||
id: livestreaming_id,
|
||||
name: "livestreaming".to_string(),
|
||||
parent_id: Some(gpui2_id),
|
||||
},
|
||||
Channel {
|
||||
id: livestreaming_dag_id,
|
||||
name: "livestreaming_dag".to_string(),
|
||||
parent_id: Some(livestreaming_id),
|
||||
},
|
||||
Channel {
|
||||
id: livestreaming_dag_sub_id,
|
||||
name: "livestreaming_dag_sub".to_string(),
|
||||
parent_id: Some(livestreaming_id),
|
||||
},
|
||||
Channel {
|
||||
id: livestreaming_dag_sub_id,
|
||||
name: "livestreaming_dag_sub".to_string(),
|
||||
parent_id: Some(livestreaming_dag_id),
|
||||
}
|
||||
]
|
||||
graph(
|
||||
&[
|
||||
(livestreaming_id, "livestreaming"),
|
||||
(livestreaming_dag_id, "livestreaming_dag"),
|
||||
(livestreaming_dag_sub_id, "livestreaming_dag_sub"),
|
||||
],
|
||||
&[
|
||||
(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();
|
||||
|
@ -836,26 +792,26 @@ async fn test_db_channel_moving(db: &Arc<Database>) {
|
|||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_dag(actual: Vec<Channel>, expected: &[(ChannelId, Option<ChannelId>)]) {
|
||||
/// This is used to allow tests to be ordering independent
|
||||
fn make_parents_map(association_table: impl IntoIterator<Item= (ChannelId, Option<ChannelId>)>) -> HashMap<ChannelId, HashSet<ChannelId>> {
|
||||
let mut map: HashMap<ChannelId, HashSet<ChannelId>> = HashMap::default();
|
||||
|
||||
for (child, parent) in association_table {
|
||||
let entry = map.entry(child).or_default();
|
||||
if let Some(parent) = parent {
|
||||
entry.insert(parent);
|
||||
}
|
||||
}
|
||||
|
||||
map
|
||||
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 actual = actual
|
||||
.iter()
|
||||
.map(|channel| (channel.id, channel.parent_id));
|
||||
|
||||
let actual_map = make_parents_map(actual);
|
||||
let expected_map = make_parents_map(expected.iter().copied());
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -575,458 +575,6 @@ async fn test_fuzzy_search_users() {
|
|||
}
|
||||
}
|
||||
|
||||
test_both_dbs!(test_channels, test_channels_postgres, test_channels_sqlite);
|
||||
|
||||
async fn test_channels(db: &Arc<Database>) {
|
||||
let a_id = db
|
||||
.create_user(
|
||||
"user1@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user1".into(),
|
||||
github_user_id: 5,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
|
||||
let b_id = db
|
||||
.create_user(
|
||||
"user2@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user2".into(),
|
||||
github_user_id: 6,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
|
||||
let zed_id = db.create_root_channel("zed", "1", a_id).await.unwrap();
|
||||
|
||||
// Make sure that people cannot read channels they haven't been invited to
|
||||
assert!(db.get_channel(zed_id, b_id).await.unwrap().is_none());
|
||||
|
||||
db.invite_channel_member(zed_id, b_id, a_id, false)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
db.respond_to_channel_invite(zed_id, b_id, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let crdb_id = db
|
||||
.create_channel("crdb", Some(zed_id), "2", a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
let livestreaming_id = db
|
||||
.create_channel("livestreaming", Some(zed_id), "3", a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
let replace_id = db
|
||||
.create_channel("replace", Some(zed_id), "4", a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut members = db.get_channel_members(replace_id).await.unwrap();
|
||||
members.sort();
|
||||
assert_eq!(members, &[a_id, b_id]);
|
||||
|
||||
let rust_id = db.create_root_channel("rust", "5", a_id).await.unwrap();
|
||||
let cargo_id = db
|
||||
.create_channel("cargo", Some(rust_id), "6", a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let cargo_ra_id = db
|
||||
.create_channel("cargo-ra", Some(cargo_id), "7", a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_eq!(
|
||||
result.channels,
|
||||
vec![
|
||||
Channel {
|
||||
id: zed_id,
|
||||
name: "zed".to_string(),
|
||||
parent_id: None,
|
||||
},
|
||||
Channel {
|
||||
id: crdb_id,
|
||||
name: "crdb".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: livestreaming_id,
|
||||
name: "livestreaming".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: replace_id,
|
||||
name: "replace".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: rust_id,
|
||||
name: "rust".to_string(),
|
||||
parent_id: None,
|
||||
},
|
||||
Channel {
|
||||
id: cargo_id,
|
||||
name: "cargo".to_string(),
|
||||
parent_id: Some(rust_id),
|
||||
},
|
||||
Channel {
|
||||
id: cargo_ra_id,
|
||||
name: "cargo-ra".to_string(),
|
||||
parent_id: Some(cargo_id),
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
let result = db.get_channels_for_user(b_id).await.unwrap();
|
||||
assert_eq!(
|
||||
result.channels,
|
||||
vec![
|
||||
Channel {
|
||||
id: zed_id,
|
||||
name: "zed".to_string(),
|
||||
parent_id: None,
|
||||
},
|
||||
Channel {
|
||||
id: crdb_id,
|
||||
name: "crdb".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: livestreaming_id,
|
||||
name: "livestreaming".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: replace_id,
|
||||
name: "replace".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
// Update member permissions
|
||||
let set_subchannel_admin = db.set_channel_member_admin(crdb_id, a_id, b_id, true).await;
|
||||
assert!(set_subchannel_admin.is_err());
|
||||
let set_channel_admin = db.set_channel_member_admin(zed_id, a_id, b_id, true).await;
|
||||
assert!(set_channel_admin.is_ok());
|
||||
|
||||
let result = db.get_channels_for_user(b_id).await.unwrap();
|
||||
assert_eq!(
|
||||
result.channels,
|
||||
vec![
|
||||
Channel {
|
||||
id: zed_id,
|
||||
name: "zed".to_string(),
|
||||
parent_id: None,
|
||||
},
|
||||
Channel {
|
||||
id: crdb_id,
|
||||
name: "crdb".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: livestreaming_id,
|
||||
name: "livestreaming".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
Channel {
|
||||
id: replace_id,
|
||||
name: "replace".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
// Remove a single channel
|
||||
db.delete_channel(crdb_id, a_id).await.unwrap();
|
||||
assert!(db.get_channel(crdb_id, a_id).await.unwrap().is_none());
|
||||
|
||||
// Remove a channel tree
|
||||
let (mut channel_ids, user_ids) = db.delete_channel(rust_id, a_id).await.unwrap();
|
||||
channel_ids.sort();
|
||||
assert_eq!(channel_ids, &[rust_id, cargo_id, cargo_ra_id]);
|
||||
assert_eq!(user_ids, &[a_id]);
|
||||
|
||||
assert!(db.get_channel(rust_id, a_id).await.unwrap().is_none());
|
||||
assert!(db.get_channel(cargo_id, a_id).await.unwrap().is_none());
|
||||
assert!(db.get_channel(cargo_ra_id, a_id).await.unwrap().is_none());
|
||||
}
|
||||
|
||||
test_both_dbs!(
|
||||
test_joining_channels,
|
||||
test_joining_channels_postgres,
|
||||
test_joining_channels_sqlite
|
||||
);
|
||||
|
||||
async fn test_joining_channels(db: &Arc<Database>) {
|
||||
let owner_id = db.create_server("test").await.unwrap().0 as u32;
|
||||
|
||||
let user_1 = db
|
||||
.create_user(
|
||||
"user1@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user1".into(),
|
||||
github_user_id: 5,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
let user_2 = db
|
||||
.create_user(
|
||||
"user2@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user2".into(),
|
||||
github_user_id: 6,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
|
||||
let channel_1 = db
|
||||
.create_root_channel("channel_1", "1", user_1)
|
||||
.await
|
||||
.unwrap();
|
||||
let room_1 = db.room_id_for_channel(channel_1).await.unwrap();
|
||||
|
||||
// can join a room with membership to its channel
|
||||
let joined_room = db
|
||||
.join_room(room_1, user_1, ConnectionId { owner_id, id: 1 })
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(joined_room.room.participants.len(), 1);
|
||||
|
||||
drop(joined_room);
|
||||
// cannot join a room without membership to its channel
|
||||
assert!(db
|
||||
.join_room(room_1, user_2, ConnectionId { owner_id, id: 1 })
|
||||
.await
|
||||
.is_err());
|
||||
}
|
||||
|
||||
test_both_dbs!(
|
||||
test_channel_invites,
|
||||
test_channel_invites_postgres,
|
||||
test_channel_invites_sqlite
|
||||
);
|
||||
|
||||
async fn test_channel_invites(db: &Arc<Database>) {
|
||||
db.create_server("test").await.unwrap();
|
||||
|
||||
let user_1 = db
|
||||
.create_user(
|
||||
"user1@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user1".into(),
|
||||
github_user_id: 5,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
let user_2 = db
|
||||
.create_user(
|
||||
"user2@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user2".into(),
|
||||
github_user_id: 6,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
|
||||
let user_3 = db
|
||||
.create_user(
|
||||
"user3@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user3".into(),
|
||||
github_user_id: 7,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
|
||||
let channel_1_1 = db
|
||||
.create_root_channel("channel_1", "1", user_1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let channel_1_2 = db
|
||||
.create_root_channel("channel_2", "2", user_1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
db.invite_channel_member(channel_1_1, user_2, user_1, false)
|
||||
.await
|
||||
.unwrap();
|
||||
db.invite_channel_member(channel_1_2, user_2, user_1, false)
|
||||
.await
|
||||
.unwrap();
|
||||
db.invite_channel_member(channel_1_1, user_3, user_1, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let user_2_invites = db
|
||||
.get_channel_invites_for_user(user_2) // -> [channel_1_1, channel_1_2]
|
||||
.await
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|channel| channel.id)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(user_2_invites, &[channel_1_1, channel_1_2]);
|
||||
|
||||
let user_3_invites = db
|
||||
.get_channel_invites_for_user(user_3) // -> [channel_1_1]
|
||||
.await
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|channel| channel.id)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(user_3_invites, &[channel_1_1]);
|
||||
|
||||
let members = db
|
||||
.get_channel_member_details(channel_1_1, user_1)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
members,
|
||||
&[
|
||||
proto::ChannelMember {
|
||||
user_id: user_1.to_proto(),
|
||||
kind: proto::channel_member::Kind::Member.into(),
|
||||
admin: true,
|
||||
},
|
||||
proto::ChannelMember {
|
||||
user_id: user_2.to_proto(),
|
||||
kind: proto::channel_member::Kind::Invitee.into(),
|
||||
admin: false,
|
||||
},
|
||||
proto::ChannelMember {
|
||||
user_id: user_3.to_proto(),
|
||||
kind: proto::channel_member::Kind::Invitee.into(),
|
||||
admin: true,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
db.respond_to_channel_invite(channel_1_1, user_2, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let channel_1_3 = db
|
||||
.create_channel("channel_3", Some(channel_1_1), "1", user_1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let members = db
|
||||
.get_channel_member_details(channel_1_3, user_1)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
members,
|
||||
&[
|
||||
proto::ChannelMember {
|
||||
user_id: user_1.to_proto(),
|
||||
kind: proto::channel_member::Kind::Member.into(),
|
||||
admin: true,
|
||||
},
|
||||
proto::ChannelMember {
|
||||
user_id: user_2.to_proto(),
|
||||
kind: proto::channel_member::Kind::AncestorMember.into(),
|
||||
admin: false,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
test_both_dbs!(
|
||||
test_channel_renames,
|
||||
test_channel_renames_postgres,
|
||||
test_channel_renames_sqlite
|
||||
);
|
||||
|
||||
async fn test_channel_renames(db: &Arc<Database>) {
|
||||
db.create_server("test").await.unwrap();
|
||||
|
||||
let user_1 = db
|
||||
.create_user(
|
||||
"user1@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user1".into(),
|
||||
github_user_id: 5,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
|
||||
let user_2 = db
|
||||
.create_user(
|
||||
"user2@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user2".into(),
|
||||
github_user_id: 6,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
|
||||
let zed_id = db.create_root_channel("zed", "1", user_1).await.unwrap();
|
||||
|
||||
db.rename_channel(zed_id, user_1, "#zed-archive")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let zed_archive_id = zed_id;
|
||||
|
||||
let (channel, _) = db
|
||||
.get_channel(zed_archive_id, user_1)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(channel.name, "zed-archive");
|
||||
|
||||
let non_permissioned_rename = db
|
||||
.rename_channel(zed_archive_id, user_2, "hacked-lol")
|
||||
.await;
|
||||
assert!(non_permissioned_rename.is_err());
|
||||
|
||||
let bad_name_rename = db.rename_channel(zed_id, user_1, "#").await;
|
||||
assert!(bad_name_rename.is_err())
|
||||
}
|
||||
|
||||
fn build_background_executor() -> Arc<Background> {
|
||||
Deterministic::new(0).build_background()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue