mirror of
https://github.com/Blah-IM/blahrs.git
synced 2025-05-01 08:41:09 +00:00
test(webapi): test room item posting and listing
This commit is contained in:
parent
1a0347337c
commit
c0ec429c24
3 changed files with 158 additions and 5 deletions
|
@ -38,6 +38,12 @@ pub struct WithItemId<T> {
|
||||||
pub item: T,
|
pub item: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> WithItemId<T> {
|
||||||
|
pub fn new(cid: Id, item: T) -> Self {
|
||||||
|
Self { cid, item }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct UserKey(#[serde(with = "hex::serde")] pub [u8; PUBLIC_KEY_LENGTH]);
|
pub struct UserKey(#[serde(with = "hex::serde")] pub [u8; PUBLIC_KEY_LENGTH]);
|
||||||
|
|
|
@ -413,7 +413,7 @@ impl Pagination {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct RoomItems {
|
pub struct RoomItems {
|
||||||
pub items: Vec<WithItemId<ChatItem>>,
|
pub items: Vec<WithItemId<ChatItem>>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -790,7 +790,7 @@ async fn room_admin(
|
||||||
"only self-adding is implemented yet",
|
"only self-adding is implemented yet",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if permission.is_empty() || !MemberPermission::MAX_SELF_ADD.contains(permission) {
|
if !MemberPermission::MAX_SELF_ADD.contains(permission) {
|
||||||
return Err(error_response!(
|
return Err(error_response!(
|
||||||
StatusCode::BAD_REQUEST,
|
StatusCode::BAD_REQUEST,
|
||||||
"deserialization",
|
"deserialization",
|
||||||
|
|
|
@ -7,10 +7,11 @@ use std::sync::{Arc, LazyLock};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use blah_types::{
|
use blah_types::{
|
||||||
get_timestamp, AuthPayload, CreateRoomPayload, Id, MemberPermission, RoomAdminOp,
|
get_timestamp, AuthPayload, ChatItem, ChatPayload, CreateRoomPayload, Id, MemberPermission,
|
||||||
RoomAdminPayload, RoomAttrs, RoomMember, RoomMemberList, ServerPermission, UserKey, WithSig,
|
RichText, RoomAdminOp, RoomAdminPayload, RoomAttrs, RoomMember, RoomMemberList,
|
||||||
|
ServerPermission, UserKey, WithItemId, WithSig,
|
||||||
};
|
};
|
||||||
use blahd::{ApiError, AppState, Database, RoomList, RoomMetadata};
|
use blahd::{ApiError, AppState, Database, RoomItems, RoomList, RoomMetadata};
|
||||||
use ed25519_dalek::SigningKey;
|
use ed25519_dalek::SigningKey;
|
||||||
use futures_util::TryFutureExt;
|
use futures_util::TryFutureExt;
|
||||||
use rand::rngs::mock::StepRng;
|
use rand::rngs::mock::StepRng;
|
||||||
|
@ -363,3 +364,149 @@ async fn room_join_leave(server: Server, ref mut rng: impl RngCore) {
|
||||||
.await
|
.await
|
||||||
.expect_api_err(StatusCode::NOT_FOUND, "not_found");
|
.expect_api_err(StatusCode::NOT_FOUND, "not_found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn room_item_post_read(server: Server, ref mut rng: impl RngCore) {
|
||||||
|
let rid_pub = server
|
||||||
|
.create_room(
|
||||||
|
&ALICE_PRIV,
|
||||||
|
RoomAttrs::PUBLIC_READABLE | RoomAttrs::PUBLIC_JOINABLE,
|
||||||
|
"public room",
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let rid_priv = server
|
||||||
|
.create_room(&ALICE_PRIV, RoomAttrs::empty(), "private room")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut chat = |rid: Id, key: &SigningKey, msg: &str| {
|
||||||
|
sign(
|
||||||
|
key,
|
||||||
|
rng,
|
||||||
|
ChatPayload {
|
||||||
|
room: rid,
|
||||||
|
rich_text: RichText::from(msg),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let post = |rid: Id, chat: ChatItem| {
|
||||||
|
server
|
||||||
|
.request::<_, Id>(Method::POST, format!("/room/{rid}/item"), None, Some(chat))
|
||||||
|
.map_ok(|opt| opt.unwrap())
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ok.
|
||||||
|
let chat1 = chat(rid_pub, &ALICE_PRIV, "one");
|
||||||
|
let chat2 = chat(rid_pub, &ALICE_PRIV, "two");
|
||||||
|
let cid1 = post(rid_pub, chat1.clone()).await.unwrap();
|
||||||
|
let cid2 = post(rid_pub, chat2.clone()).await.unwrap();
|
||||||
|
|
||||||
|
// Duplicated chat.
|
||||||
|
post(rid_pub, chat2.clone())
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::BAD_REQUEST, "duplicated_nonce");
|
||||||
|
|
||||||
|
// Wrong room.
|
||||||
|
post(rid_pub, chat(rid_priv, &ALICE_PRIV, "wrong room"))
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::BAD_REQUEST, "invalid_request");
|
||||||
|
|
||||||
|
// Not a member.
|
||||||
|
post(rid_pub, chat(rid_pub, &BOB_PRIV, "not a member"))
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::NOT_FOUND, "not_found");
|
||||||
|
|
||||||
|
// Is a member but without permission.
|
||||||
|
server
|
||||||
|
.join_room(rid_pub, &BOB_PRIV, MemberPermission::empty())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
post(rid_pub, chat(rid_pub, &BOB_PRIV, "no permission"))
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::FORBIDDEN, "permission_denied");
|
||||||
|
|
||||||
|
// Room not exists.
|
||||||
|
post(Id::INVALID, chat(Id::INVALID, &ALICE_PRIV, "not permitted"))
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::NOT_FOUND, "not_found");
|
||||||
|
|
||||||
|
//// Item listing ////
|
||||||
|
|
||||||
|
let chat1 = WithItemId::new(cid1, chat1);
|
||||||
|
let chat2 = WithItemId::new(cid2, chat2);
|
||||||
|
|
||||||
|
// List with default page size.
|
||||||
|
let items = server
|
||||||
|
.get::<RoomItems>(format!("/room/{rid_pub}/item"), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
items,
|
||||||
|
RoomItems {
|
||||||
|
items: vec![chat2.clone(), chat1.clone()],
|
||||||
|
skip_token: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// List with small page size.
|
||||||
|
let items = server
|
||||||
|
.get::<RoomItems>(format!("/room/{rid_pub}/item?top=1"), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
items,
|
||||||
|
RoomItems {
|
||||||
|
items: vec![chat2.clone()],
|
||||||
|
skip_token: Some(cid2),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Second page.
|
||||||
|
let items = server
|
||||||
|
.get::<RoomItems>(format!("/room/{rid_pub}/item?skipToken={cid2}&top=1"), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
items,
|
||||||
|
RoomItems {
|
||||||
|
items: vec![chat1.clone()],
|
||||||
|
skip_token: Some(cid1),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// No more.
|
||||||
|
let items = server
|
||||||
|
.get::<RoomItems>(format!("/room/{rid_pub}/item?skipToken={cid1}&top=1"), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(items, RoomItems::default());
|
||||||
|
|
||||||
|
//// Private room ////
|
||||||
|
|
||||||
|
// Access without token.
|
||||||
|
server
|
||||||
|
.get::<RoomItems>(format!("/room/{rid_priv}/item"), None)
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::NOT_FOUND, "not_found");
|
||||||
|
|
||||||
|
// Not a member.
|
||||||
|
server
|
||||||
|
.get::<RoomItems>(
|
||||||
|
format!("/room/{rid_priv}/item"),
|
||||||
|
Some(&auth(&BOB_PRIV, rng)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::NOT_FOUND, "not_found");
|
||||||
|
|
||||||
|
// Ok.
|
||||||
|
let items = server
|
||||||
|
.get::<RoomItems>(
|
||||||
|
format!("/room/{rid_priv}/item"),
|
||||||
|
Some(&auth(&ALICE_PRIV, rng)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(items, RoomItems::default());
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue