From 78f92130fa7803d1d6b46d39e1e77870d54b1597 Mon Sep 17 00:00:00 2001 From: Devin R Date: Mon, 6 Apr 2020 15:29:38 -0400 Subject: [PATCH] add individual events for tests, working TestRunner --- Cargo.toml | 1 + src/test_builder.rs | 145 ++++++++++++++++++---- tests/data/events/alias.json | 13 ++ tests/data/events/create.json | 15 +++ tests/data/events/fully_read.json | 7 ++ tests/data/events/history_visibility.json | 13 ++ tests/data/events/join_rules.json | 13 ++ tests/data/events/member.json | 17 +++ tests/data/events/message_emote.json | 14 +++ tests/data/events/message_notice.json | 16 +++ tests/data/events/message_text.json | 14 +++ tests/data/events/name.json | 13 ++ tests/data/events/redacted.json | 22 ++++ tests/data/events/redacted_invalid.json | 7 ++ tests/data/events/redacted_state.json | 21 ++++ tests/data/events/redaction.json | 10 ++ tests/data/events/room_avatar.json | 21 ++++ tests/data/events/tag.json | 10 ++ tests/data/events/topic.json | 18 +++ tests/data/events/typing.json | 10 ++ 20 files changed, 377 insertions(+), 23 deletions(-) create mode 100644 tests/data/events/alias.json create mode 100644 tests/data/events/create.json create mode 100644 tests/data/events/fully_read.json create mode 100644 tests/data/events/history_visibility.json create mode 100644 tests/data/events/join_rules.json create mode 100644 tests/data/events/member.json create mode 100644 tests/data/events/message_emote.json create mode 100644 tests/data/events/message_notice.json create mode 100644 tests/data/events/message_text.json create mode 100644 tests/data/events/name.json create mode 100644 tests/data/events/redacted.json create mode 100644 tests/data/events/redacted_invalid.json create mode 100644 tests/data/events/redacted_state.json create mode 100644 tests/data/events/redaction.json create mode 100644 tests/data/events/room_avatar.json create mode 100644 tests/data/events/tag.json create mode 100644 tests/data/events/topic.json create mode 100644 tests/data/events/typing.json diff --git a/Cargo.toml b/Cargo.toml index 6b0e5d8e..5ed494d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/test_builder.rs b/src/test_builder.rs index bd19b17f..76dff5c5 100644 --- a/src/test_builder.rs +++ b/src/test_builder.rs @@ -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, + /// Used To test the models + room: Option, + /// When testing the models a vec of RoomEvents is needed. + events: Vec, /// 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, + client_assertions: Vec, + /// 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, /// `mokito::Mock` - mock: mockito::Mock, + mock: Option, } 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>(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::>(json).unwrap().into_result().unwrap(); + let event = serde_json::from_str::>(&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> { + 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> { Ok(()) } + + fn run_room_tests(mut self) -> Result<(), Vec> { + 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(); } } diff --git a/tests/data/events/alias.json b/tests/data/events/alias.json new file mode 100644 index 00000000..48196492 --- /dev/null +++ b/tests/data/events/alias.json @@ -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 + } +} diff --git a/tests/data/events/create.json b/tests/data/events/create.json new file mode 100644 index 00000000..8b3a5926 --- /dev/null +++ b/tests/data/events/create.json @@ -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 + } +} diff --git a/tests/data/events/fully_read.json b/tests/data/events/fully_read.json new file mode 100644 index 00000000..c736e515 --- /dev/null +++ b/tests/data/events/fully_read.json @@ -0,0 +1,7 @@ +{ + "content": { + "event_id": "$someplace:example.org" + }, + "room_id": "!somewhere:example.org", + "type": "m.fully_read" +} diff --git a/tests/data/events/history_visibility.json b/tests/data/events/history_visibility.json new file mode 100644 index 00000000..fae3be3b --- /dev/null +++ b/tests/data/events/history_visibility.json @@ -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 + } +} diff --git a/tests/data/events/join_rules.json b/tests/data/events/join_rules.json new file mode 100644 index 00000000..7c116ae6 --- /dev/null +++ b/tests/data/events/join_rules.json @@ -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 + } +} diff --git a/tests/data/events/member.json b/tests/data/events/member.json new file mode 100644 index 00000000..a1088ca9 --- /dev/null +++ b/tests/data/events/member.json @@ -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" + } +} diff --git a/tests/data/events/message_emote.json b/tests/data/events/message_emote.json new file mode 100644 index 00000000..146ce548 --- /dev/null +++ b/tests/data/events/message_emote.json @@ -0,0 +1,14 @@ +{ + "content": { + "body": "is dancing", "format": "org.matrix.custom.html", + "formatted_body": "is dancing", + "msgtype": "m.emote" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1520372800469, + "sender": "@example:localhost", + "type": "m.room.message", + "unsigned": { + "age": 598971425 + } +} diff --git a/tests/data/events/message_notice.json b/tests/data/events/message_notice.json new file mode 100644 index 00000000..6215c904 --- /dev/null +++ b/tests/data/events/message_notice.json @@ -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": "313: nio wins!", + "msgtype": "m.notice" + }, + "type": "m.room.message", + "room_id": "!YHhmBTmGBHGQOlGpaZ:matrix.org" +} diff --git a/tests/data/events/message_text.json b/tests/data/events/message_text.json new file mode 100644 index 00000000..b95607cd --- /dev/null +++ b/tests/data/events/message_text.json @@ -0,0 +1,14 @@ +{ + "content": { + "body": "is dancing", "format": "org.matrix.custom.html", + "formatted_body": "is dancing", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1520372800469, + "sender": "@example:localhost", + "type": "m.room.message", + "unsigned": { + "age": 598971425 + } +} diff --git a/tests/data/events/name.json b/tests/data/events/name.json new file mode 100644 index 00000000..79946006 --- /dev/null +++ b/tests/data/events/name.json @@ -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 + } +} diff --git a/tests/data/events/redacted.json b/tests/data/events/redacted.json new file mode 100644 index 00000000..ae1a3ce3 --- /dev/null +++ b/tests/data/events/redacted.json @@ -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" + } +} diff --git a/tests/data/events/redacted_invalid.json b/tests/data/events/redacted_invalid.json new file mode 100644 index 00000000..47fb283b --- /dev/null +++ b/tests/data/events/redacted_invalid.json @@ -0,0 +1,7 @@ +{ + "content": {}, + "event_id": "$15275046980maRLj:localhost", + "origin_server_ts": 1527504698685, + "sender": "@example:localhost", + "type": "m.room.message" +} diff --git a/tests/data/events/redacted_state.json b/tests/data/events/redacted_state.json new file mode 100644 index 00000000..3968ec14 --- /dev/null +++ b/tests/data/events/redacted_state.json @@ -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" + } +} diff --git a/tests/data/events/redaction.json b/tests/data/events/redaction.json new file mode 100644 index 00000000..5047d2f3 --- /dev/null +++ b/tests/data/events/redaction.json @@ -0,0 +1,10 @@ +{ + "content": { + "reason": "😀" + }, + "event_id": "$151957878228ssqrJ:localhost", + "origin_server_ts": 1519578782185, + "sender": "@example:localhost", + "type": "m.room.redaction", + "redacts": "$151957878228ssqrj:localhost" +} diff --git a/tests/data/events/room_avatar.json b/tests/data/events/room_avatar.json new file mode 100644 index 00000000..b92f9161 --- /dev/null +++ b/tests/data/events/room_avatar.json @@ -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 + } +} + diff --git a/tests/data/events/tag.json b/tests/data/events/tag.json new file mode 100644 index 00000000..b42b06db --- /dev/null +++ b/tests/data/events/tag.json @@ -0,0 +1,10 @@ +{ + "content": { + "tags": { + "u.work": { + "order": 0.9 + } + } + }, + "type": "m.tag" +} diff --git a/tests/data/events/topic.json b/tests/data/events/topic.json new file mode 100644 index 00000000..930ff0a3 --- /dev/null +++ b/tests/data/events/topic.json @@ -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" + } +} diff --git a/tests/data/events/typing.json b/tests/data/events/typing.json new file mode 100644 index 00000000..52949f76 --- /dev/null +++ b/tests/data/events/typing.json @@ -0,0 +1,10 @@ +{ + "content": { + "user_ids": [ + "@alice:matrix.org", + "@bob:example.com" + ] + }, + "room_id": "!jEsUZKDJdhlrceRyVU:example.org", + "type": "m.typing" +}