add individual events for tests, working TestRunner
This commit is contained in:
parent
854948fc6d
commit
78f92130fa
20 changed files with 377 additions and 23 deletions
|
@ -65,3 +65,4 @@ tracing-subscriber = "0.2.3"
|
|||
tempfile = "3.1.0"
|
||||
mockito = "0.23.3"
|
||||
serde = "1.0.105"
|
||||
ansi_term = "0.12.1"
|
||||
|
|
|
@ -7,8 +7,12 @@ mod test_it {
|
|||
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::panic;
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::identifiers::UserId;
|
||||
use crate::identifiers::{RoomId, UserId};
|
||||
use crate::events::{
|
||||
collections::all::RoomEvent,
|
||||
room::{
|
||||
|
@ -18,15 +22,13 @@ mod test_it {
|
|||
};
|
||||
use crate::{AsyncClient, Session, SyncSettings};
|
||||
|
||||
use ansi_term::Colour;
|
||||
use serde_json::Value;
|
||||
use mockito::{mock, Matcher};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use std::panic;
|
||||
use crate::models::Room;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ResponseBuilder {
|
||||
|
@ -34,19 +36,28 @@ mod test_it {
|
|||
}
|
||||
|
||||
pub struct TestRunner {
|
||||
/// Used when testing the whole client
|
||||
client: Option<AsyncClient>,
|
||||
/// Used To test the models
|
||||
room: Option<Room>,
|
||||
/// When testing the models a vec of RoomEvents is needed.
|
||||
events: Vec<RoomEvent>,
|
||||
/// A `Vec` of callbacks that should assert something about the client.
|
||||
///
|
||||
/// The callback should panic if the state is unexpected (use `assert_*!` macro)
|
||||
assertions: Vec<fn(&AsyncClient)>,
|
||||
client_assertions: Vec<fn(&AsyncClient)>,
|
||||
/// A `Vec` of callbacks that should assert something about the room.
|
||||
///
|
||||
/// The callback should panic if the state is unexpected (use `assert_*!` macro)
|
||||
room_assertions: Vec<fn(&Room)>,
|
||||
/// `mokito::Mock`
|
||||
mock: mockito::Mock,
|
||||
mock: Option<mockito::Mock>,
|
||||
}
|
||||
|
||||
impl ResponseBuilder {
|
||||
|
||||
/// Creates an `IncomingResponse` to hold events for a sync.
|
||||
pub fn create_sync_response(mut self) -> &mut Self {
|
||||
pub fn create_sync_response(mut self) -> Self {
|
||||
|
||||
self
|
||||
}
|
||||
|
@ -57,11 +68,10 @@ mod test_it {
|
|||
self
|
||||
}
|
||||
|
||||
/// Add an event either to the event stream or the appropriate `IncomingResponse` field.
|
||||
/// Add an event to the events `Vec`.
|
||||
pub fn add_event_from_file<Ev: TryFromRaw, P: AsRef<Path>>(mut self, path: P, variant: fn(Ev) -> RoomEvent) -> Self {
|
||||
let val = fs::read_to_string(path.as_ref()).expect(&format!("file not found {:?}", path.as_ref()));
|
||||
let json = serde_json::value::Value::from_str(&val).expect(&format!("not valid json {}", val));
|
||||
let event = serde_json::from_value::<ruma_events::EventResult<Ev>>(json).unwrap().into_result().unwrap();
|
||||
let event = serde_json::from_str::<ruma_events::EventResult<Ev>>(&val).unwrap().into_result().unwrap();
|
||||
self.add_event(variant(event));
|
||||
self
|
||||
}
|
||||
|
@ -74,38 +84,127 @@ mod test_it {
|
|||
///
|
||||
/// The `TestRunner` streams the events to the client and enables methods to set assertions
|
||||
/// about the state of the client.
|
||||
pub fn build(mut self) -> TestRunner {
|
||||
let mock = mock("POST", "/_matrix/client/r0/login")
|
||||
pub fn build_responses(mut self, method: &str, path: &str) -> TestRunner {
|
||||
let body = serde_json::to_string(&self.events).unwrap();
|
||||
let mock = Some(mock(method, path)
|
||||
.with_status(200)
|
||||
.with_body_from_file("tests/data/login_response.json")
|
||||
.create();
|
||||
.with_body(body)
|
||||
.create());
|
||||
|
||||
TestRunner {
|
||||
client: None,
|
||||
assertions: Vec::new(),
|
||||
room: None,
|
||||
events: Vec::new(),
|
||||
client_assertions: Vec::new(),
|
||||
room_assertions: Vec::new(),
|
||||
mock,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes `ResponseBuilder and returns a `TestRunner`.
|
||||
///
|
||||
/// The `TestRunner` streams the events to the client and enables methods to set assertions
|
||||
/// about the state of the client.
|
||||
pub fn build_room_events(mut self, room_id: &RoomId, user_id: &UserId) -> TestRunner {
|
||||
TestRunner {
|
||||
client: None,
|
||||
room: Some(Room::new(room_id, user_id)),
|
||||
events: self.events,
|
||||
client_assertions: Vec::new(),
|
||||
room_assertions: Vec::new(),
|
||||
mock: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TestRunner {
|
||||
pub fn set_client(&mut self, client: AsyncClient) -> &mut Self {
|
||||
pub fn set_client(mut self, client: AsyncClient) -> Self {
|
||||
self.client = Some(client);
|
||||
self
|
||||
}
|
||||
pub fn run_test(self) -> Result<(), Vec<String>> {
|
||||
|
||||
pub fn add_client_assert(mut self, assert: fn(&AsyncClient)) -> Self {
|
||||
self.client_assertions.push(assert);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_room_assert(mut self, assert: fn(&Room)) -> Self {
|
||||
self.room_assertions.push(assert);
|
||||
self
|
||||
}
|
||||
|
||||
fn run_client_tests(mut self) -> Result<(), Vec<String>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_room_tests(mut self) -> Result<(), Vec<String>> {
|
||||
let mut errs = Vec::new();
|
||||
let mut room = self.room.unwrap();
|
||||
for event in &self.events {
|
||||
match event {
|
||||
RoomEvent::RoomMember(m) => room.handle_membership(m),
|
||||
RoomEvent::RoomName(n) => room.handle_room_name(n),
|
||||
RoomEvent::RoomCanonicalAlias(ca) => room.handle_canonical(ca),
|
||||
RoomEvent::RoomAliases(a) => room.handle_room_aliases(a),
|
||||
RoomEvent::RoomPowerLevels(p) => room.handle_power_level(p),
|
||||
// RoomEvent::RoomEncryption(e) => room.handle_encryption_event(e),
|
||||
_ => todo!("implement more RoomEvent variants"),
|
||||
};
|
||||
}
|
||||
for assert in self.room_assertions {
|
||||
if let Err(e) = panic::catch_unwind(|| assert(&room)) {
|
||||
errs.push(stringify!(e).to_string());
|
||||
}
|
||||
}
|
||||
if errs.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errs)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_test(mut self) {
|
||||
let errs = if let Some(room) = &self.room {
|
||||
self.run_room_tests()
|
||||
} else if let Some(cli) = &self.client {
|
||||
self.run_client_tests()
|
||||
} else {
|
||||
panic!("must have either AsyncClient or Room")
|
||||
};
|
||||
|
||||
if let Err(errs) = errs {
|
||||
let err_str = errs.join(&format!("{}\n", Colour::Red.paint("Error: ")));
|
||||
if !errs.is_empty() {
|
||||
panic!("{}", Colour::Red.paint("some tests failed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut bld = ResponseBuilder::default();
|
||||
let runner = bld.add_event_from_file("./tests/data/events/power_levels.json", RoomEvent::RoomPowerLevels)
|
||||
.build();
|
||||
fn test_room_users(room: &Room) {
|
||||
assert_eq!(room.members.len(), 1);
|
||||
}
|
||||
|
||||
fn test_room_power(room: &Room) {
|
||||
assert!(room.power_levels.is_some());
|
||||
assert_eq!(room.power_levels.as_ref().unwrap().kick, js_int::Int::new(50).unwrap());
|
||||
let admin = room.members.get(&UserId::try_from("@example:localhost").unwrap()).unwrap();
|
||||
assert_eq!(admin.power_level.unwrap(), js_int::Int::new(100).unwrap());
|
||||
println!("{:#?}", room);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn room_events() {
|
||||
let rid = RoomId::try_from("!roomid:room.com").unwrap();
|
||||
let uid = UserId::try_from("@example:localhost").unwrap();
|
||||
let mut bld = ResponseBuilder::default();
|
||||
let runner = bld.add_event_from_file("./tests/data/events/member.json", RoomEvent::RoomMember)
|
||||
.add_event_from_file("./tests/data/events/power_levels.json", RoomEvent::RoomPowerLevels)
|
||||
.build_room_events(&rid, &uid)
|
||||
.add_room_assert(test_room_power)
|
||||
.add_room_assert(test_room_users);
|
||||
|
||||
runner.run_test();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
13
tests/data/events/alias.json
Normal file
13
tests/data/events/alias.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"content": {
|
||||
"alias": "#tutorial:localhost"
|
||||
},
|
||||
"event_id": "$15139375513VdeRF:localhost",
|
||||
"origin_server_ts": 1513937551461,
|
||||
"sender": "@example:localhost",
|
||||
"state_key": "",
|
||||
"type": "m.room.canonical_alias",
|
||||
"unsigned": {
|
||||
"age": 7034220433
|
||||
}
|
||||
}
|
15
tests/data/events/create.json
Normal file
15
tests/data/events/create.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"content": {
|
||||
"creator": "@example:localhost",
|
||||
"m.federate": true,
|
||||
"room_version": "1"
|
||||
},
|
||||
"event_id": "$151957878228ekrDs:localhost",
|
||||
"origin_server_ts": 1519578782185,
|
||||
"sender": "@example:localhost",
|
||||
"state_key": "",
|
||||
"type": "m.room.create",
|
||||
"unsigned": {
|
||||
"age": 1392989709
|
||||
}
|
||||
}
|
7
tests/data/events/fully_read.json
Normal file
7
tests/data/events/fully_read.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"content": {
|
||||
"event_id": "$someplace:example.org"
|
||||
},
|
||||
"room_id": "!somewhere:example.org",
|
||||
"type": "m.fully_read"
|
||||
}
|
13
tests/data/events/history_visibility.json
Normal file
13
tests/data/events/history_visibility.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"content": {
|
||||
"history_visibility": "world_readable"
|
||||
},
|
||||
"event_id": "$151957878235ricnD:localhost",
|
||||
"origin_server_ts": 1519578782195,
|
||||
"sender": "@example:localhost",
|
||||
"state_key": "",
|
||||
"type": "m.room.history_visibility",
|
||||
"unsigned": {
|
||||
"age": 1392989715
|
||||
}
|
||||
}
|
13
tests/data/events/join_rules.json
Normal file
13
tests/data/events/join_rules.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"content": {
|
||||
"join_rule": "public"
|
||||
},
|
||||
"event_id": "$151957878231iejdB:localhost",
|
||||
"origin_server_ts": 1519578782192,
|
||||
"sender": "@example:localhost",
|
||||
"state_key": "",
|
||||
"type": "m.room.join_rules",
|
||||
"unsigned": {
|
||||
"age": 1392989713
|
||||
}
|
||||
}
|
17
tests/data/events/member.json
Normal file
17
tests/data/events/member.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"content": {
|
||||
"avatar_url": null,
|
||||
"displayname": "example",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$151800140517rfvjc:localhost",
|
||||
"membership": "join",
|
||||
"origin_server_ts": 1518001405556,
|
||||
"sender": "@example:localhost",
|
||||
"state_key": "@example:localhost",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 2970366338,
|
||||
"replaces_state": "$151800111315tsynI:localhost"
|
||||
}
|
||||
}
|
14
tests/data/events/message_emote.json
Normal file
14
tests/data/events/message_emote.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"content": {
|
||||
"body": "is dancing", "format": "org.matrix.custom.html",
|
||||
"formatted_body": "<strong>is dancing</strong>",
|
||||
"msgtype": "m.emote"
|
||||
},
|
||||
"event_id": "$152037280074GZeOm:localhost",
|
||||
"origin_server_ts": 1520372800469,
|
||||
"sender": "@example:localhost",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 598971425
|
||||
}
|
||||
}
|
16
tests/data/events/message_notice.json
Normal file
16
tests/data/events/message_notice.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"origin_server_ts": 1533565163841,
|
||||
"sender": "@_neb_github:matrix.org",
|
||||
"event_id": "$153356516319138IHRIC:matrix.org",
|
||||
"unsigned": {
|
||||
"age": 743
|
||||
},
|
||||
"content": {
|
||||
"body": "https://github.com/matrix-org/matrix-python-sdk/issues/266 : Consider allowing MatrixClient.__init__ to take sync_token kwarg",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<a href='https://github.com/matrix-org/matrix-python-sdk/pull/313'>313: nio wins!</a>",
|
||||
"msgtype": "m.notice"
|
||||
},
|
||||
"type": "m.room.message",
|
||||
"room_id": "!YHhmBTmGBHGQOlGpaZ:matrix.org"
|
||||
}
|
14
tests/data/events/message_text.json
Normal file
14
tests/data/events/message_text.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"content": {
|
||||
"body": "is dancing", "format": "org.matrix.custom.html",
|
||||
"formatted_body": "<strong>is dancing</strong>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$152037280074GZeOm:localhost",
|
||||
"origin_server_ts": 1520372800469,
|
||||
"sender": "@example:localhost",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 598971425
|
||||
}
|
||||
}
|
13
tests/data/events/name.json
Normal file
13
tests/data/events/name.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"content": {
|
||||
"name": "#tutorial:localhost"
|
||||
},
|
||||
"event_id": "$15139375513VdeRF:localhost",
|
||||
"origin_server_ts": 1513937551461,
|
||||
"sender": "@example:localhost",
|
||||
"state_key": "",
|
||||
"type": "m.room.name",
|
||||
"unsigned": {
|
||||
"age": 7034220433
|
||||
}
|
||||
}
|
22
tests/data/events/redacted.json
Normal file
22
tests/data/events/redacted.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"content": {},
|
||||
"event_id": "$15275046980maRLj:localhost",
|
||||
"origin_server_ts": 1527504698685,
|
||||
"sender": "@example:localhost",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 19334,
|
||||
"redacted_because": {
|
||||
"content": {},
|
||||
"event_id": "$15275047031IXQRi:localhost",
|
||||
"origin_server_ts": 1527504703496,
|
||||
"redacts": "$15275046980maRLj:localhost",
|
||||
"sender": "@example:localhost",
|
||||
"type": "m.room.redaction",
|
||||
"unsigned": {
|
||||
"age": 14523
|
||||
}
|
||||
},
|
||||
"redacted_by": "$15275047031IXQRi:localhost"
|
||||
}
|
||||
}
|
7
tests/data/events/redacted_invalid.json
Normal file
7
tests/data/events/redacted_invalid.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"content": {},
|
||||
"event_id": "$15275046980maRLj:localhost",
|
||||
"origin_server_ts": 1527504698685,
|
||||
"sender": "@example:localhost",
|
||||
"type": "m.room.message"
|
||||
}
|
21
tests/data/events/redacted_state.json
Normal file
21
tests/data/events/redacted_state.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"content": {},
|
||||
"event_id": "$example_id:example.org",
|
||||
"origin_server_ts": 1532324933640,
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "test_state_key",
|
||||
"type": "m.some.state",
|
||||
"unsigned": {
|
||||
"age": 30693154231,
|
||||
"redacted_because": {
|
||||
"content": {},
|
||||
"event_id": "$redaction_example_id:example.org",
|
||||
"origin_server_ts": 1532324940702,
|
||||
"redacts": "$example_id:example.org",
|
||||
"sender": "@example:example:org",
|
||||
"type": "m.room.redaction",
|
||||
"unsigned": {"age": 30693147169}
|
||||
},
|
||||
"redacted_by": "$redaction_example_id:example.org"
|
||||
}
|
||||
}
|
10
tests/data/events/redaction.json
Normal file
10
tests/data/events/redaction.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"content": {
|
||||
"reason": "😀"
|
||||
},
|
||||
"event_id": "$151957878228ssqrJ:localhost",
|
||||
"origin_server_ts": 1519578782185,
|
||||
"sender": "@example:localhost",
|
||||
"type": "m.room.redaction",
|
||||
"redacts": "$151957878228ssqrj:localhost"
|
||||
}
|
21
tests/data/events/room_avatar.json
Normal file
21
tests/data/events/room_avatar.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"content": {
|
||||
"info": {
|
||||
"h": 398,
|
||||
"mimetype": "image/jpeg",
|
||||
"size": 31037,
|
||||
"w": 394
|
||||
},
|
||||
"url": "mxc://domain.com/JWEIFJgwEIhweiWJE"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:domain.com",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:domain.com",
|
||||
"sender": "@example:domain.com",
|
||||
"state_key": "",
|
||||
"type": "m.room.avatar",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
|
10
tests/data/events/tag.json
Normal file
10
tests/data/events/tag.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"content": {
|
||||
"tags": {
|
||||
"u.work": {
|
||||
"order": 0.9
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.tag"
|
||||
}
|
18
tests/data/events/topic.json
Normal file
18
tests/data/events/topic.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"content": {
|
||||
"topic": "😀"
|
||||
},
|
||||
"event_id": "$151957878228ssqrJ:localhost",
|
||||
"origin_server_ts": 1519578782185,
|
||||
"sender": "@example:localhost",
|
||||
"state_key": "",
|
||||
"type": "m.room.topic",
|
||||
"unsigned": {
|
||||
"age": 1392989709,
|
||||
"prev_content": {
|
||||
"topic": "test"
|
||||
},
|
||||
"prev_sender": "@example:localhost",
|
||||
"replaces_state": "$151957069225EVYKm:localhost"
|
||||
}
|
||||
}
|
10
tests/data/events/typing.json
Normal file
10
tests/data/events/typing.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"type": "m.typing"
|
||||
}
|
Loading…
Reference in a new issue