mirror of
https://github.com/Blah-IM/blahrs.git
synced 2025-05-01 08:41:09 +00:00
Add tests for room join/leave and fix response status code
This commit is contained in:
parent
5cd45232f4
commit
e40ec6a324
3 changed files with 115 additions and 8 deletions
|
@ -819,8 +819,8 @@ async fn room_join(
|
||||||
.is_some_and(|attrs| attrs.contains(RoomAttrs::PUBLIC_JOINABLE));
|
.is_some_and(|attrs| attrs.contains(RoomAttrs::PUBLIC_JOINABLE));
|
||||||
if !is_public_joinable {
|
if !is_public_joinable {
|
||||||
return Err(error_response!(
|
return Err(error_response!(
|
||||||
StatusCode::FORBIDDEN,
|
StatusCode::NOT_FOUND,
|
||||||
"permission_denied",
|
"not_found",
|
||||||
"room does not exists or user is not allowed to join this room",
|
"room does not exists or user is not allowed to join this room",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -833,14 +833,13 @@ async fn room_join(
|
||||||
",
|
",
|
||||||
params![user],
|
params![user],
|
||||||
)?;
|
)?;
|
||||||
txn.execute(
|
let updated = txn.execute(
|
||||||
r"
|
r"
|
||||||
INSERT INTO `room_member` (`rid`, `uid`, `permission`)
|
INSERT INTO `room_member` (`rid`, `uid`, `permission`)
|
||||||
SELECT :rid, `uid`, :perm
|
SELECT :rid, `uid`, :perm
|
||||||
FROM `user`
|
FROM `user`
|
||||||
WHERE `userkey` = :userkey
|
WHERE `userkey` = :userkey
|
||||||
ON CONFLICT (`rid`, `uid`) DO UPDATE SET
|
ON CONFLICT (`rid`, `uid`) DO NOTHING
|
||||||
`permission` = :perm
|
|
||||||
",
|
",
|
||||||
named_params! {
|
named_params! {
|
||||||
":rid": rid,
|
":rid": rid,
|
||||||
|
@ -848,6 +847,13 @@ async fn room_join(
|
||||||
":perm": permission,
|
":perm": permission,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
if updated == 0 {
|
||||||
|
return Err(error_response!(
|
||||||
|
StatusCode::CONFLICT,
|
||||||
|
"exists",
|
||||||
|
"the user is already in the room",
|
||||||
|
));
|
||||||
|
}
|
||||||
txn.commit()?;
|
txn.commit()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ use std::sync::{Arc, LazyLock};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use blah::types::{
|
use blah::types::{
|
||||||
get_timestamp, AuthPayload, CreateRoomPayload, Id, MemberPermission, RoomAttrs, RoomMember,
|
get_timestamp, AuthPayload, CreateRoomPayload, Id, MemberPermission, RoomAdminOp,
|
||||||
RoomMemberList, ServerPermission, UserKey, WithSig,
|
RoomAdminPayload, RoomAttrs, RoomMember, RoomMemberList, ServerPermission, UserKey, WithSig,
|
||||||
};
|
};
|
||||||
use blahd::{ApiError, AppState, Database, RoomList, RoomMetadata};
|
use blahd::{ApiError, AppState, Database, RoomList, RoomMetadata};
|
||||||
use ed25519_dalek::SigningKey;
|
use ed25519_dalek::SigningKey;
|
||||||
|
@ -31,6 +31,9 @@ fn mock_rng() -> impl RngCore {
|
||||||
rand::rngs::mock::StepRng::new(9, 1)
|
rand::rngs::mock::StepRng::new(9, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
enum NoContent {}
|
||||||
|
|
||||||
trait ResultExt {
|
trait ResultExt {
|
||||||
fn expect_api_err(self, status: StatusCode, code: &str);
|
fn expect_api_err(self, status: StatusCode, code: &str);
|
||||||
}
|
}
|
||||||
|
@ -258,3 +261,97 @@ async fn room_create_get(server: Server, #[case] public: bool) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(got_joined, expect_list(false));
|
assert_eq!(got_joined, expect_list(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn room_join_leave(server: Server) {
|
||||||
|
let rng = &mut mock_rng();
|
||||||
|
let rid_pub = create_room(
|
||||||
|
&server,
|
||||||
|
&ALICE_PRIV,
|
||||||
|
rng,
|
||||||
|
RoomAttrs::PUBLIC_JOINABLE,
|
||||||
|
"public room",
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let rid_priv = create_room(
|
||||||
|
&server,
|
||||||
|
&ALICE_PRIV,
|
||||||
|
rng,
|
||||||
|
RoomAttrs::empty(),
|
||||||
|
"private room",
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut join = |rid: Id, key: &SigningKey| {
|
||||||
|
let req = sign(
|
||||||
|
key,
|
||||||
|
rng,
|
||||||
|
RoomAdminPayload {
|
||||||
|
room: rid,
|
||||||
|
op: RoomAdminOp::AddMember {
|
||||||
|
permission: MemberPermission::MAX_SELF_ADD,
|
||||||
|
user: UserKey(key.verifying_key().to_bytes()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
server.request::<_, NoContent>(Method::POST, format!("/room/{rid}/admin"), None, Some(req))
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ok.
|
||||||
|
join(rid_pub, &BOB_PRIV).await.unwrap();
|
||||||
|
// Already joined.
|
||||||
|
join(rid_pub, &BOB_PRIV)
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::CONFLICT, "exists");
|
||||||
|
// Not permitted.
|
||||||
|
join(rid_priv, &BOB_PRIV)
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::NOT_FOUND, "not_found");
|
||||||
|
// Not exists.
|
||||||
|
join(Id::INVALID, &BOB_PRIV)
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::NOT_FOUND, "not_found");
|
||||||
|
|
||||||
|
// Bob is joined now.
|
||||||
|
assert_eq!(
|
||||||
|
server
|
||||||
|
.get::<RoomList>("/room?filter=joined", Some(&auth(&BOB_PRIV, rng)))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.rooms
|
||||||
|
.len(),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut leave = |rid: Id, key: &SigningKey| {
|
||||||
|
let req = sign(
|
||||||
|
key,
|
||||||
|
rng,
|
||||||
|
RoomAdminPayload {
|
||||||
|
room: rid,
|
||||||
|
op: RoomAdminOp::RemoveMember {
|
||||||
|
user: UserKey(key.verifying_key().to_bytes()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
server.request::<_, NoContent>(Method::POST, format!("/room/{rid}/admin"), None, Some(req))
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ok.
|
||||||
|
leave(rid_pub, &BOB_PRIV).await.unwrap();
|
||||||
|
// Already left.
|
||||||
|
leave(rid_pub, &BOB_PRIV)
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::NOT_FOUND, "not_found");
|
||||||
|
// Unpermitted and not inside.
|
||||||
|
leave(rid_priv, &BOB_PRIV)
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::NOT_FOUND, "not_found");
|
||||||
|
// Unpermitted and not inside.
|
||||||
|
leave(Id::INVALID, &BOB_PRIV)
|
||||||
|
.await
|
||||||
|
.expect_api_err(StatusCode::NOT_FOUND, "not_found");
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,10 @@ impl fmt::Display for Id {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Id {
|
||||||
|
pub const INVALID: Self = Id(i64::MAX);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct WithItemId<T> {
|
pub struct WithItemId<T> {
|
||||||
pub cid: Id,
|
pub cid: Id,
|
||||||
|
@ -347,7 +351,7 @@ pub struct RoomMember {
|
||||||
pub struct AuthPayload {}
|
pub struct AuthPayload {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(tag = "typ", rename_all = "snake_case")]
|
// `typ` is provided by `RoomAdminOp`.
|
||||||
pub struct RoomAdminPayload {
|
pub struct RoomAdminPayload {
|
||||||
pub room: Id,
|
pub room: Id,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
|
Loading…
Add table
Reference in a new issue