refactor: use fixtures to populate db

refactor: method for creating server channels
This commit is contained in:
Paul Makles
2023-10-27 16:31:37 +01:00
parent 7705354d4d
commit cd0737911b
9 changed files with 340 additions and 331 deletions

View File

@@ -6,7 +6,10 @@ use revolt_permissions::OverrideField;
use revolt_result::Result;
use serde::{Deserialize, Serialize};
use crate::{events::client::EventV1, Database, File, IntoDocumentPath, SystemMessage, User};
use crate::{
events::client::EventV1, Database, File, IntoDocumentPath, PartialServer, Server,
SystemMessage, User,
};
auto_derived!(
#[serde(tag = "channel_type")]
@@ -189,6 +192,67 @@ impl Channel {
Ok(())
}*/
/// Create a new server channel
pub async fn create_server_channel(
db: &Database,
server: &mut Server,
data: v0::DataCreateServerChannel,
update_server: bool,
) -> Result<Channel> {
let config = config().await;
if server.channels.len() > config.features.limits.default.server_channels {
return Err(create_error!(TooManyChannels {
max: config.features.limits.default.server_channels,
}));
};
let id = ulid::Ulid::new().to_string();
let channel = match data.channel_type {
v0::LegacyServerChannelType::Text => Channel::TextChannel {
id: id.clone(),
server: server.id.to_owned(),
name: data.name,
description: data.description,
icon: None,
last_message_id: None,
default_permissions: None,
role_permissions: HashMap::new(),
nsfw: data.nsfw.unwrap_or(false),
},
v0::LegacyServerChannelType::Voice => Channel::VoiceChannel {
id: id.clone(),
server: server.id.to_owned(),
name: data.name,
description: data.description,
icon: None,
default_permissions: None,
role_permissions: HashMap::new(),
nsfw: data.nsfw.unwrap_or(false),
},
};
db.insert_channel(&channel).await?;
if update_server {
server
.update(
db,
PartialServer {
channels: Some([server.channels.clone(), [id].into()].concat()),
..Default::default()
},
vec![],
)
.await?;
EventV1::ChannelCreate(channel.clone().into())
.p(server.id.clone())
.await;
}
Ok(channel)
}
/// Create a group
pub async fn create_group(
db: &Database,
@@ -627,130 +691,18 @@ impl IntoDocumentPath for FieldsChannel {
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_permissions::{calculate_channel_permissions, ChannelPermission, OverrideField};
use crate::{
util::permissions::DatabasePermissionQuery, Channel, Member, MemberCompositeKey, Role,
Server, User,
};
use crate::{fixture, util::permissions::DatabasePermissionQuery};
#[async_std::test]
async fn permissions_text_channel() {
database_test!(|db| async move {
let owner = User::create(&db, "Owner".to_string(), None, None)
.await
.unwrap();
let moderator = User::create(&db, "Moderator".to_string(), None, None)
.await
.unwrap();
let user = User::create(&db, "User".to_string(), None, None)
.await
.unwrap();
let server_id = ulid::Ulid::new().to_string();
let channel = Channel::TextChannel {
id: ulid::Ulid::new().to_string(),
server: server_id.clone(),
name: "Channel".to_string(),
description: None,
icon: None,
last_message_id: None,
default_permissions: Some(OverrideField {
d: 1048576, // TODO: bitfield
..Default::default()
}),
role_permissions: HashMap::from([(
"01F9HFTSBWTNA2F4TMSV7VM3FG".to_string(),
OverrideField {
a: 1048576, // TODO: bitfield
..Default::default()
},
)]),
nsfw: false,
};
let server = Server {
id: server_id,
owner: owner.id.clone(),
name: "My Server".to_string(),
description: None,
channels: vec![channel.id()],
categories: None,
system_messages: None,
roles: HashMap::from([
(
"01F9HFTSBWTNA2F4TMSV7VM3FG".to_string(),
Role {
name: "Moderator".to_string(),
permissions: OverrideField {
a: 545270208, // TODO: explicit
..Default::default()
},
colour: None,
hoist: true,
rank: 3,
},
),
(
"01FBF9DNHSRPVTWFMNB3JNB8FK".to_string(),
Role {
name: "Owner".to_string(),
permissions: Default::default(),
colour: None,
hoist: true,
rank: 0,
},
),
]),
default_permissions: 4000322560, // TODO: use bitfield
icon: None,
banner: None,
flags: None,
nsfw: false,
analytics: false,
discoverable: false,
};
// TODO: proper creation
db.insert_channel(&channel).await.unwrap();
server.create(&db).await.unwrap();
db.insert_member(&Member {
id: MemberCompositeKey {
user: owner.id.clone(),
server: server.id.clone(),
},
roles: vec!["01FBF9DNHSRPVTWFMNB3JNB8FK".to_string()],
..Default::default()
})
.await
.unwrap();
db.insert_member(&Member {
id: MemberCompositeKey {
user: moderator.id.clone(),
server: server.id.clone(),
},
roles: vec!["01F9HFTSBWTNA2F4TMSV7VM3FG".to_string()],
..Default::default()
})
.await
.unwrap();
db.insert_member(&Member {
id: MemberCompositeKey {
user: user.id.clone(),
server: server.id.clone(),
},
..Default::default()
})
.await
.unwrap();
fixture!(db, "server_with_roles",
owner user 0
moderator user 1
user user 2
channel channel 3);
let mut query = DatabasePermissionQuery::new(&db, &owner).channel(&channel);
assert!(calculate_channel_permissions(&mut query)

View File

@@ -453,109 +453,18 @@ impl SystemMessageChannels {
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
use revolt_permissions::{calculate_server_permissions, ChannelPermission, OverrideField};
use crate::{
util::permissions::DatabasePermissionQuery, Member, MemberCompositeKey, Role, Server, User,
};
use crate::{fixture, util::permissions::DatabasePermissionQuery};
#[async_std::test]
async fn permissions() {
database_test!(|db| async move {
let owner = User::create(&db, "Owner".to_string(), None, None)
.await
.unwrap();
let moderator = User::create(&db, "Moderator".to_string(), None, None)
.await
.unwrap();
let user = User::create(&db, "User".to_string(), None, None)
.await
.unwrap();
let server_id = ulid::Ulid::new().to_string();
// TODO: seeder functions
// e.g. seed!("channel", "file.json")
let server = Server {
id: server_id,
owner: owner.id.clone(),
name: "My Server".to_string(),
description: None,
channels: vec![],
categories: None,
system_messages: None,
roles: HashMap::from([
(
"01F9HFTSBWTNA2F4TMSV7VM3FG".to_string(),
Role {
name: "Moderator".to_string(),
permissions: OverrideField {
a: 545270208, // TODO: explicit
..Default::default()
},
colour: None,
hoist: true,
rank: 3,
},
),
(
"01FBF9DNHSRPVTWFMNB3JNB8FK".to_string(),
Role {
name: "Owner".to_string(),
permissions: Default::default(),
colour: None,
hoist: true,
rank: 0,
},
),
]),
default_permissions: 4000322560, // TODO: use bitfield
icon: None,
banner: None,
flags: None,
nsfw: false,
analytics: false,
discoverable: false,
};
// TODO: proper creation
server.create(&db).await.unwrap();
db.insert_member(&Member {
id: MemberCompositeKey {
user: owner.id.clone(),
server: server.id.clone(),
},
roles: vec!["01FBF9DNHSRPVTWFMNB3JNB8FK".to_string()],
..Default::default()
})
.await
.unwrap();
db.insert_member(&Member {
id: MemberCompositeKey {
user: moderator.id.clone(),
server: server.id.clone(),
},
roles: vec!["01F9HFTSBWTNA2F4TMSV7VM3FG".to_string()],
..Default::default()
})
.await
.unwrap();
db.insert_member(&Member {
id: MemberCompositeKey {
user: user.id.clone(),
server: server.id.clone(),
},
..Default::default()
})
.await
.unwrap();
fixture!(db, "server_with_roles",
owner user 0
moderator user 1
user user 2
server server 4);
let mut query = DatabasePermissionQuery::new(&db, &owner).server(&server);
assert!(calculate_server_permissions(&mut query)

View File

@@ -2,3 +2,6 @@ pub mod bridge;
pub mod idempotency;
pub mod permissions;
pub mod reference;
#[cfg(test)]
pub mod test_fixtures;

View File

@@ -7,7 +7,7 @@ use schemars::{
JsonSchema,
};
use crate::{Bot, Channel, Database, Emoji, Message, User, Webhook};
use crate::{Bot, Channel, Database, Emoji, Message, Server, User, Webhook};
/// Reference to some object in the database
#[derive(Serialize, Deserialize)]
@@ -42,6 +42,11 @@ impl Reference {
db.fetch_message(&self.id).await
}
/// Fetch server from Ref
pub async fn as_server(&self, db: &Database) -> Result<Server> {
db.fetch_server(&self.id).await
}
/// Fetch user from Ref
pub async fn as_user(&self, db: &Database) -> Result<User> {
db.fetch_user(&self.id).await

View File

@@ -0,0 +1,103 @@
use std::collections::HashMap;
use once_cell::sync::Lazy;
use regex::{Captures, Regex};
use serde_json::from_str;
use crate::{Channel, Database, Member, Server, User};
static RE_ID: Lazy<Regex> = Lazy::new(|| Regex::new("__ID:(\\d+)__").unwrap());
#[derive(Debug, Deserialize)]
#[serde(tag = "_object_type")]
enum LoadedFixture {
User(User),
Channel(Channel),
Server(Server),
ServerMember(Member),
}
pub async fn load_fixture(db: &Database, input: &str) -> HashMap<String, String> {
let mut ids = HashMap::<String, String>::new();
let input = RE_ID.replace_all(input, |cap: &Captures| {
let d = cap.get(1).unwrap().as_str();
if !ids.contains_key(d) {
ids.insert(d.to_string(), ulid::Ulid::new().to_string());
}
ids.get(d).unwrap().clone()
});
// Deserialise the fixtures
let items: Vec<LoadedFixture> = from_str(&input).expect("Failed to deserialise fixture");
// Load all of the items within
for item in items {
#[allow(clippy::disallowed_methods)]
match item {
LoadedFixture::User(user) => db.insert_user(&user).await.unwrap(),
LoadedFixture::Channel(channel) => db.insert_channel(&channel).await.unwrap(),
LoadedFixture::Server(server) => db.insert_server(&server).await.unwrap(),
LoadedFixture::ServerMember(member) => db.insert_member(&member).await.unwrap(),
}
}
// Return IDs for ease of use
ids
}
#[async_trait]
pub trait FetchFixture {
async fn user(&self, db: &Database, d: usize) -> User;
async fn channel(&self, db: &Database, d: usize) -> Channel;
async fn server(&self, db: &Database, d: usize) -> Server;
async fn member(&self, db: &Database, d_server: usize, d_user: usize) -> Member;
}
#[async_trait]
impl FetchFixture for HashMap<String, String> {
async fn user(&self, db: &Database, d: usize) -> User {
db.fetch_user(self.get(&d.to_string()).unwrap())
.await
.unwrap()
}
async fn channel(&self, db: &Database, d: usize) -> Channel {
db.fetch_channel(self.get(&d.to_string()).unwrap())
.await
.unwrap()
}
async fn server(&self, db: &Database, d: usize) -> Server {
db.fetch_server(self.get(&d.to_string()).unwrap())
.await
.unwrap()
}
async fn member(&self, db: &Database, d_server: usize, d_user: usize) -> Member {
db.fetch_member(
self.get(&d_server.to_string()).unwrap(),
self.get(&d_user.to_string()).unwrap(),
)
.await
.unwrap()
}
}
#[macro_export]
macro_rules! fixture {
( $database:expr, $name:expr, $( $variable:ident $type:ident $id: expr )+ ) => {
use $crate::util::test_fixtures::FetchFixture;
let fixtures = $crate::util::test_fixtures::load_fixture(
&$database,
include_str!(concat!("../../../fixtures/", $name, ".json")),
)
.await;
$(
let $variable = fixtures.$type(&$database, $id).await;
)+
};
}