mirror of
https://github.com/Blah-IM/blahrs.git
synced 2025-05-01 08:41:09 +00:00
refactor(test): use real RNG and simplify
We do not assert on the fake RNG and it should work well with any RNG.
This commit is contained in:
parent
8f20aa0cf2
commit
9acf857781
1 changed files with 32 additions and 51 deletions
|
@ -1,9 +1,9 @@
|
||||||
#![expect(clippy::unwrap_used, reason = "FIXME: random false positive")]
|
#![expect(
|
||||||
#![expect(clippy::toplevel_ref_arg, reason = "easy to use for fixtures")]
|
clippy::unwrap_used,
|
||||||
use std::cell::RefCell;
|
reason = "WAIT: https://github.com/rust-lang/rust-clippy/issues/11119"
|
||||||
|
)]
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::future::{Future, IntoFuture};
|
use std::future::{Future, IntoFuture};
|
||||||
use std::ops::DerefMut;
|
|
||||||
use std::sync::{Arc, LazyLock};
|
use std::sync::{Arc, LazyLock};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
@ -11,18 +11,16 @@ use anyhow::Result;
|
||||||
use axum::http::HeaderMap;
|
use axum::http::HeaderMap;
|
||||||
use blah_types::identity::{IdUrl, UserActKeyDesc, UserIdentityDesc, UserProfile};
|
use blah_types::identity::{IdUrl, UserActKeyDesc, UserIdentityDesc, UserProfile};
|
||||||
use blah_types::{
|
use blah_types::{
|
||||||
get_timestamp, AuthPayload, ChatPayload, CreateGroup, CreatePeerChat, CreateRoomPayload, Id,
|
AuthPayload, ChatPayload, CreateGroup, CreatePeerChat, CreateRoomPayload, Id, MemberPermission,
|
||||||
MemberPermission, PubKey, RichText, RoomAdminOp, RoomAdminPayload, RoomAttrs, RoomMetadata,
|
PubKey, RichText, RoomAdminOp, RoomAdminPayload, RoomAttrs, RoomMetadata, ServerPermission,
|
||||||
ServerPermission, SignExt, Signed, SignedChatMsg, UserKey, UserRegisterPayload, WithMsgId,
|
SignExt, Signed, SignedChatMsg, UserKey, UserRegisterPayload, WithMsgId, X_BLAH_DIFFICULTY,
|
||||||
X_BLAH_DIFFICULTY, X_BLAH_NONCE,
|
X_BLAH_NONCE,
|
||||||
};
|
};
|
||||||
use blahd::{ApiError, AppState, Database, RoomList, RoomMsgs};
|
use blahd::{ApiError, AppState, Database, RoomList, RoomMsgs};
|
||||||
use ed25519_dalek::SigningKey;
|
use ed25519_dalek::SigningKey;
|
||||||
use futures_util::future::BoxFuture;
|
use futures_util::future::BoxFuture;
|
||||||
use futures_util::TryFutureExt;
|
use futures_util::TryFutureExt;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rand::rngs::mock::StepRng;
|
|
||||||
use rand::RngCore;
|
|
||||||
use reqwest::{header, Method, StatusCode};
|
use reqwest::{header, Method, StatusCode};
|
||||||
use rstest::{fixture, rstest};
|
use rstest::{fixture, rstest};
|
||||||
use rusqlite::{params, Connection};
|
use rusqlite::{params, Connection};
|
||||||
|
@ -79,11 +77,6 @@ static ALICE: LazyLock<User> = LazyLock::new(|| User::new(b'A'));
|
||||||
static BOB: LazyLock<User> = LazyLock::new(|| User::new(b'B'));
|
static BOB: LazyLock<User> = LazyLock::new(|| User::new(b'B'));
|
||||||
static CAROL: LazyLock<User> = LazyLock::new(|| User::new(b'C'));
|
static CAROL: LazyLock<User> = LazyLock::new(|| User::new(b'C'));
|
||||||
|
|
||||||
#[fixture]
|
|
||||||
fn rng() -> impl RngCore {
|
|
||||||
rand::rngs::mock::StepRng::new(42, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
enum NoContent {}
|
enum NoContent {}
|
||||||
|
|
||||||
|
@ -125,7 +118,6 @@ impl std::error::Error for ApiErrorWithHeaders {}
|
||||||
struct Server {
|
struct Server {
|
||||||
port: u16,
|
port: u16,
|
||||||
client: reqwest::Client,
|
client: reqwest::Client,
|
||||||
rng: RefCell<StepRng>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
|
@ -137,10 +129,6 @@ impl Server {
|
||||||
format!("http://{}:{}", LOCALHOST, self.port)
|
format!("http://{}:{}", LOCALHOST, self.port)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rng(&self) -> impl DerefMut<Target = impl RngCore> + use<'_> {
|
|
||||||
self.rng.borrow_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn request<Req: Serialize, Resp: DeserializeOwned>(
|
fn request<Req: Serialize, Resp: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
method: Method,
|
method: Method,
|
||||||
|
@ -188,13 +176,7 @@ impl Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign<T: Serialize>(&self, user: &User, msg: T) -> Signed<T> {
|
fn sign<T: Serialize>(&self, user: &User, msg: T) -> Signed<T> {
|
||||||
msg.sign_msg_with(
|
msg.sign_msg(&user.pubkeys.id_key, &user.act_priv).unwrap()
|
||||||
&user.pubkeys.id_key,
|
|
||||||
&user.act_priv,
|
|
||||||
get_timestamp(),
|
|
||||||
&mut *self.rng.borrow_mut(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_room(
|
fn create_room(
|
||||||
|
@ -331,8 +313,7 @@ fn server() -> Server {
|
||||||
|
|
||||||
tokio::spawn(axum::serve(listener, router).into_future());
|
tokio::spawn(axum::serve(listener, router).into_future());
|
||||||
let client = reqwest::ClientBuilder::new().no_proxy().build().unwrap();
|
let client = reqwest::ClientBuilder::new().no_proxy().build().unwrap();
|
||||||
let rng = StepRng::new(24, 1).into();
|
Server { port, client }
|
||||||
Server { port, client, rng }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
|
@ -346,9 +327,9 @@ async fn smoke(server: Server) {
|
||||||
assert_eq!(got, exp);
|
assert_eq!(got, exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn auth(user: &User, rng: &mut impl RngCore) -> String {
|
fn auth(user: &User) -> String {
|
||||||
let msg = AuthPayload {}
|
let msg = AuthPayload {}
|
||||||
.sign_msg_with(&user.pubkeys.id_key, &user.act_priv, get_timestamp(), rng)
|
.sign_msg(&user.pubkeys.id_key, &user.act_priv)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
serde_json::to_string(&msg).unwrap()
|
serde_json::to_string(&msg).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -357,7 +338,7 @@ fn auth(user: &User, rng: &mut impl RngCore) -> String {
|
||||||
#[case::public(true)]
|
#[case::public(true)]
|
||||||
#[case::private(false)]
|
#[case::private(false)]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn room_create_get(server: Server, ref mut rng: impl RngCore, #[case] public: bool) {
|
async fn room_create_get(server: Server, #[case] public: bool) {
|
||||||
let title = "test room";
|
let title = "test room";
|
||||||
let mut room_meta = RoomMetadata {
|
let mut room_meta = RoomMetadata {
|
||||||
rid: Id(0),
|
rid: Id(0),
|
||||||
|
@ -389,13 +370,13 @@ async fn room_create_get(server: Server, ref mut rng: impl RngCore, #[case] publ
|
||||||
|
|
||||||
// Alice can always access it.
|
// Alice can always access it.
|
||||||
let got_meta = server
|
let got_meta = server
|
||||||
.get::<RoomMetadata>(&format!("/room/{rid}"), Some(&auth(&ALICE, rng)))
|
.get::<RoomMetadata>(&format!("/room/{rid}"), Some(&auth(&ALICE)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(got_meta, room_meta);
|
assert_eq!(got_meta, room_meta);
|
||||||
|
|
||||||
// Bob or public can access it when it is public.
|
// Bob or public can access it when it is public.
|
||||||
for auth in [None, Some(auth(&BOB, rng))] {
|
for auth in [None, Some(auth(&BOB))] {
|
||||||
let resp = server
|
let resp = server
|
||||||
.get::<RoomMetadata>(&format!("/room/{rid}"), auth.as_deref())
|
.get::<RoomMetadata>(&format!("/room/{rid}"), auth.as_deref())
|
||||||
.await;
|
.await;
|
||||||
|
@ -432,13 +413,13 @@ async fn room_create_get(server: Server, ref mut rng: impl RngCore, #[case] publ
|
||||||
.await
|
.await
|
||||||
.expect_api_err(StatusCode::UNAUTHORIZED, "unauthorized");
|
.expect_api_err(StatusCode::UNAUTHORIZED, "unauthorized");
|
||||||
let got_joined = server
|
let got_joined = server
|
||||||
.get::<RoomList>("/room?filter=joined", Some(&auth(&ALICE, rng)))
|
.get::<RoomList>("/room?filter=joined", Some(&auth(&ALICE)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(got_joined, expect_list(true, Some(MemberPermission::ALL)));
|
assert_eq!(got_joined, expect_list(true, Some(MemberPermission::ALL)));
|
||||||
|
|
||||||
let got_joined = server
|
let got_joined = server
|
||||||
.get::<RoomList>("/room?filter=joined", Some(&auth(&BOB, rng)))
|
.get::<RoomList>("/room?filter=joined", Some(&auth(&BOB)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(got_joined, expect_list(false, None));
|
assert_eq!(got_joined, expect_list(false, None));
|
||||||
|
@ -446,7 +427,7 @@ async fn room_create_get(server: Server, ref mut rng: impl RngCore, #[case] publ
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn room_join_leave(server: Server, ref mut rng: impl RngCore) {
|
async fn room_join_leave(server: Server) {
|
||||||
let rid_pub = server
|
let rid_pub = server
|
||||||
.create_room(&ALICE, RoomAttrs::PUBLIC_JOINABLE, "public room")
|
.create_room(&ALICE, RoomAttrs::PUBLIC_JOINABLE, "public room")
|
||||||
.await
|
.await
|
||||||
|
@ -481,7 +462,7 @@ async fn room_join_leave(server: Server, ref mut rng: impl RngCore) {
|
||||||
// Bob is joined now.
|
// Bob is joined now.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
server
|
server
|
||||||
.get::<RoomList>("/room?filter=joined", Some(&auth(&BOB, rng)))
|
.get::<RoomList>("/room?filter=joined", Some(&auth(&BOB)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -509,7 +490,7 @@ async fn room_join_leave(server: Server, ref mut rng: impl RngCore) {
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn room_chat_post_read(server: Server, ref mut rng: impl RngCore) {
|
async fn room_chat_post_read(server: Server) {
|
||||||
let rid_pub = server
|
let rid_pub = server
|
||||||
.create_room(
|
.create_room(
|
||||||
&ALICE,
|
&ALICE,
|
||||||
|
@ -634,13 +615,13 @@ async fn room_chat_post_read(server: Server, ref mut rng: impl RngCore) {
|
||||||
|
|
||||||
// Not a member.
|
// Not a member.
|
||||||
server
|
server
|
||||||
.get::<RoomMsgs>(&format!("/room/{rid_priv}/msg"), Some(&auth(&BOB, rng)))
|
.get::<RoomMsgs>(&format!("/room/{rid_priv}/msg"), Some(&auth(&BOB)))
|
||||||
.await
|
.await
|
||||||
.expect_api_err(StatusCode::NOT_FOUND, "room_not_found");
|
.expect_api_err(StatusCode::NOT_FOUND, "room_not_found");
|
||||||
|
|
||||||
// Ok.
|
// Ok.
|
||||||
let msgs = server
|
let msgs = server
|
||||||
.get::<RoomMsgs>(&format!("/room/{rid_priv}/msg"), Some(&auth(&ALICE, rng)))
|
.get::<RoomMsgs>(&format!("/room/{rid_priv}/msg"), Some(&auth(&ALICE)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(msgs, RoomMsgs::default());
|
assert_eq!(msgs, RoomMsgs::default());
|
||||||
|
@ -648,7 +629,7 @@ async fn room_chat_post_read(server: Server, ref mut rng: impl RngCore) {
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn last_seen(server: Server, ref mut rng: impl RngCore) {
|
async fn last_seen(server: Server) {
|
||||||
let title = "public room";
|
let title = "public room";
|
||||||
let attrs = RoomAttrs::PUBLIC_READABLE | RoomAttrs::PUBLIC_JOINABLE;
|
let attrs = RoomAttrs::PUBLIC_READABLE | RoomAttrs::PUBLIC_JOINABLE;
|
||||||
let member_perm = MemberPermission::ALL;
|
let member_perm = MemberPermission::ALL;
|
||||||
|
@ -663,7 +644,7 @@ async fn last_seen(server: Server, ref mut rng: impl RngCore) {
|
||||||
|
|
||||||
// 2 new msgs.
|
// 2 new msgs.
|
||||||
let rooms = server
|
let rooms = server
|
||||||
.get::<RoomList>("/room?filter=unseen", Some(&auth(&ALICE, rng)))
|
.get::<RoomList>("/room?filter=unseen", Some(&auth(&ALICE)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -687,7 +668,7 @@ async fn last_seen(server: Server, ref mut rng: impl RngCore) {
|
||||||
server.request::<NoContent, NoContent>(
|
server.request::<NoContent, NoContent>(
|
||||||
Method::POST,
|
Method::POST,
|
||||||
&format!("/room/{rid}/msg/{cid}/seen"),
|
&format!("/room/{rid}/msg/{cid}/seen"),
|
||||||
Some(&auth(user, &mut *server.rng.borrow_mut())),
|
Some(&auth(user)),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -697,7 +678,7 @@ async fn last_seen(server: Server, ref mut rng: impl RngCore) {
|
||||||
|
|
||||||
// 1 new msg.
|
// 1 new msg.
|
||||||
let rooms = server
|
let rooms = server
|
||||||
.get::<RoomList>("/room?filter=unseen", Some(&auth(&ALICE, rng)))
|
.get::<RoomList>("/room?filter=unseen", Some(&auth(&ALICE)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -720,7 +701,7 @@ async fn last_seen(server: Server, ref mut rng: impl RngCore) {
|
||||||
// Mark the second one seen. Now there is no new messages.
|
// Mark the second one seen. Now there is no new messages.
|
||||||
seen(&ALICE, alice_chat2.cid).await.unwrap();
|
seen(&ALICE, alice_chat2.cid).await.unwrap();
|
||||||
let rooms = server
|
let rooms = server
|
||||||
.get::<RoomList>("/room?filter=unseen", Some(&auth(&ALICE, rng)))
|
.get::<RoomList>("/room?filter=unseen", Some(&auth(&ALICE)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(rooms, RoomList::default());
|
assert_eq!(rooms, RoomList::default());
|
||||||
|
@ -728,7 +709,7 @@ async fn last_seen(server: Server, ref mut rng: impl RngCore) {
|
||||||
// Marking a seen message seen is a no-op.
|
// Marking a seen message seen is a no-op.
|
||||||
seen(&ALICE, alice_chat2.cid).await.unwrap();
|
seen(&ALICE, alice_chat2.cid).await.unwrap();
|
||||||
let rooms = server
|
let rooms = server
|
||||||
.get::<RoomList>("/room?filter=unseen", Some(&auth(&ALICE, rng)))
|
.get::<RoomList>("/room?filter=unseen", Some(&auth(&ALICE)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(rooms, RoomList::default());
|
assert_eq!(rooms, RoomList::default());
|
||||||
|
@ -736,7 +717,7 @@ async fn last_seen(server: Server, ref mut rng: impl RngCore) {
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn peer_chat(server: Server, ref mut rng: impl RngCore) {
|
async fn peer_chat(server: Server) {
|
||||||
let create_chat = |src: &User, tgt: &User| {
|
let create_chat = |src: &User, tgt: &User| {
|
||||||
let req = server.sign(
|
let req = server.sign(
|
||||||
src,
|
src,
|
||||||
|
@ -787,7 +768,7 @@ async fn peer_chat(server: Server, ref mut rng: impl RngCore) {
|
||||||
};
|
};
|
||||||
|
|
||||||
let meta = server
|
let meta = server
|
||||||
.get::<RoomMetadata>(&format!("/room/{rid}"), Some(&auth(key, rng)))
|
.get::<RoomMetadata>(&format!("/room/{rid}"), Some(&auth(key)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(meta, expect_meta);
|
assert_eq!(meta, expect_meta);
|
||||||
|
@ -795,7 +776,7 @@ async fn peer_chat(server: Server, ref mut rng: impl RngCore) {
|
||||||
expect_meta.member_permission = Some(MemberPermission::MAX_PEER_CHAT);
|
expect_meta.member_permission = Some(MemberPermission::MAX_PEER_CHAT);
|
||||||
expect_meta.peer_user = Some(peer.pubkeys.id_key.clone());
|
expect_meta.peer_user = Some(peer.pubkeys.id_key.clone());
|
||||||
let rooms = server
|
let rooms = server
|
||||||
.get::<RoomList>("/room?filter=joined", Some(&auth(key, rng)))
|
.get::<RoomList>("/room?filter=joined", Some(&auth(key)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -821,7 +802,7 @@ async fn register(server: Server) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let get_me = |user: Option<&User>| {
|
let get_me = |user: Option<&User>| {
|
||||||
let auth = user.map(|user| auth(user, &mut *server.rng()));
|
let auth = user.map(auth);
|
||||||
server
|
server
|
||||||
.request::<(), ()>(Method::GET, "/user/me", auth.as_deref(), None)
|
.request::<(), ()>(Method::GET, "/user/me", auth.as_deref(), None)
|
||||||
.map_ok(|_| ())
|
.map_ok(|_| ())
|
||||||
|
|
Loading…
Add table
Reference in a new issue