feat: sytests
This commit is contained in:
		
							parent
							
								
									8e041f90dd
								
							
						
					
					
						commit
						00a9424719
					
				
					 13 changed files with 1339 additions and 36 deletions
				
			
		
							
								
								
									
										4
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							|  | @ -1216,7 +1216,7 @@ dependencies = [ | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-federation-api" | name = "ruma-federation-api" | ||||||
| version = "0.0.1" | version = "0.0.1" | ||||||
| source = "git+https://github.com/ruma/ruma-federation-api.git#ccbf216f39bbbaa59131cc200eae5bd18aa1947c" | source = "git+https://github.com/ruma/ruma-federation-api.git?rev=ccbf216f39bbbaa59131cc200eae5bd18aa1947c#ccbf216f39bbbaa59131cc200eae5bd18aa1947c" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "js_int", |  "js_int", | ||||||
|  "ruma-api", |  "ruma-api", | ||||||
|  | @ -1253,7 +1253,7 @@ dependencies = [ | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-signatures" | name = "ruma-signatures" | ||||||
| version = "0.6.0-dev.1" | version = "0.6.0-dev.1" | ||||||
| source = "git+https://github.com/ruma/ruma-signatures.git#1ca545cba8dfd43e0fc8e3c18e1311fb73390a97" | source = "git+https://github.com/ruma/ruma-signatures.git?rev=1ca545cba8dfd43e0fc8e3c18e1311fb73390a97#1ca545cba8dfd43e0fc8e3c18e1311fb73390a97" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "base64 0.12.1", |  "base64 0.12.1", | ||||||
|  "ring", |  "ring", | ||||||
|  |  | ||||||
|  | @ -18,8 +18,8 @@ ruma-client-api = "0.8.0" | ||||||
| ruma-identifiers = "0.16.1" | ruma-identifiers = "0.16.1" | ||||||
| ruma-api = "0.16.0" | ruma-api = "0.16.0" | ||||||
| ruma-events = "0.21.0" | ruma-events = "0.21.0" | ||||||
| ruma-signatures = { git = "https://github.com/ruma/ruma-signatures.git" } | ruma-signatures = { git = "https://github.com/ruma/ruma-signatures.git", rev = "1ca545cba8dfd43e0fc8e3c18e1311fb73390a97" } | ||||||
| ruma-federation-api = { git = "https://github.com/ruma/ruma-federation-api.git" } | ruma-federation-api = { git = "https://github.com/ruma/ruma-federation-api.git", rev = "ccbf216f39bbbaa59131cc200eae5bd18aa1947c" } | ||||||
| log = "0.4.8" | log = "0.4.8" | ||||||
| sled = "0.31.0" | sled = "0.31.0" | ||||||
| directories = "2.0.2" | directories = "2.0.2" | ||||||
|  |  | ||||||
|  | @ -66,7 +66,7 @@ pub fn get_register_available_route( | ||||||
| ) -> MatrixResult<get_username_availability::Response> { | ) -> MatrixResult<get_username_availability::Response> { | ||||||
|     // Validate user id
 |     // Validate user id
 | ||||||
|     let user_id: UserId = |     let user_id: UserId = | ||||||
|         match (*format!("@{}:{}", body.username.clone(), db.globals.hostname())).try_into() { |         match (*format!("@{}:{}", body.username.clone(), db.globals.server_name())).try_into() { | ||||||
|             Err(_) => { |             Err(_) => { | ||||||
|                 debug!("Username invalid"); |                 debug!("Username invalid"); | ||||||
|                 return MatrixResult(Err(Error { |                 return MatrixResult(Err(Error { | ||||||
|  | @ -117,7 +117,7 @@ pub fn register_route( | ||||||
|         body.username |         body.username | ||||||
|             .clone() |             .clone() | ||||||
|             .unwrap_or_else(|| utils::random_string(GUEST_NAME_LENGTH)), |             .unwrap_or_else(|| utils::random_string(GUEST_NAME_LENGTH)), | ||||||
|         db.globals.hostname() |         db.globals.server_name() | ||||||
|     )) |     )) | ||||||
|     .try_into() |     .try_into() | ||||||
|     { |     { | ||||||
|  | @ -229,7 +229,7 @@ pub fn login_route( | ||||||
|             (body.user.clone(), body.login_info.clone()) |             (body.user.clone(), body.login_info.clone()) | ||||||
|         { |         { | ||||||
|             if !username.contains(':') { |             if !username.contains(':') { | ||||||
|                 username = format!("@{}:{}", username, db.globals.hostname()); |                 username = format!("@{}:{}", username, db.globals.server_name()); | ||||||
|             } |             } | ||||||
|             if let Ok(user_id) = (*username).try_into() { |             if let Ok(user_id) = (*username).try_into() { | ||||||
|                 if let Some(hash) = db.users.password_hash(&user_id).unwrap() { |                 if let Some(hash) = db.users.password_hash(&user_id).unwrap() { | ||||||
|  | @ -288,7 +288,7 @@ pub fn login_route( | ||||||
|     MatrixResult(Ok(login::Response { |     MatrixResult(Ok(login::Response { | ||||||
|         user_id, |         user_id, | ||||||
|         access_token: token, |         access_token: token, | ||||||
|         home_server: Some(db.globals.hostname().to_owned()), |         home_server: Some(db.globals.server_name().to_owned()), | ||||||
|         device_id, |         device_id, | ||||||
|         well_known: None, |         well_known: None, | ||||||
|     })) |     })) | ||||||
|  | @ -769,7 +769,7 @@ pub fn create_room_route( | ||||||
|     body: Ruma<create_room::Request>, |     body: Ruma<create_room::Request>, | ||||||
| ) -> MatrixResult<create_room::Response> { | ) -> MatrixResult<create_room::Response> { | ||||||
|     // TODO: check if room is unique
 |     // TODO: check if room is unique
 | ||||||
|     let room_id = RoomId::try_from(db.globals.hostname()).expect("host is valid"); |     let room_id = RoomId::try_from(db.globals.server_name()).expect("host is valid"); | ||||||
|     let user_id = body.user_id.as_ref().expect("user is authenticated"); |     let user_id = body.user_id.as_ref().expect("user is authenticated"); | ||||||
| 
 | 
 | ||||||
|     db.rooms |     db.rooms | ||||||
|  | @ -858,7 +858,7 @@ pub fn get_alias_route( | ||||||
|     _room_alias: String, |     _room_alias: String, | ||||||
| ) -> MatrixResult<get_alias::Response> { | ) -> MatrixResult<get_alias::Response> { | ||||||
|     // TODO
 |     // TODO
 | ||||||
|     let room_id = if body.room_alias.server_name() == db.globals.hostname() { |     let room_id = if body.room_alias.server_name() == db.globals.server_name() { | ||||||
|         match body.room_alias.alias() { |         match body.room_alias.alias() { | ||||||
|             "conduit" => "!lgOCCXQKtXOAPlAlG5:conduit.rs", |             "conduit" => "!lgOCCXQKtXOAPlAlG5:conduit.rs", | ||||||
|             _ => { |             _ => { | ||||||
|  | @ -923,7 +923,7 @@ pub fn join_room_by_id_or_alias_route( | ||||||
|     let room_id = match RoomId::try_from(body.room_id_or_alias.clone()) { |     let room_id = match RoomId::try_from(body.room_id_or_alias.clone()) { | ||||||
|         Ok(room_id) => room_id, |         Ok(room_id) => room_id, | ||||||
|         Err(room_alias) => { |         Err(room_alias) => { | ||||||
|             if room_alias.server_name() == db.globals.hostname() { |             if room_alias.server_name() == db.globals.server_name() { | ||||||
|                 return MatrixResult(Err(Error { |                 return MatrixResult(Err(Error { | ||||||
|                     kind: ErrorKind::NotFound, |                     kind: ErrorKind::NotFound, | ||||||
|                     message: "Room alias not found.".to_owned(), |                     message: "Room alias not found.".to_owned(), | ||||||
|  |  | ||||||
|  | @ -7,6 +7,8 @@ pub(self) mod users; | ||||||
| use directories::ProjectDirs; | use directories::ProjectDirs; | ||||||
| use std::fs::remove_dir_all; | use std::fs::remove_dir_all; | ||||||
| 
 | 
 | ||||||
|  | use rocket::Config; | ||||||
|  | 
 | ||||||
| pub struct Database { | pub struct Database { | ||||||
|     pub globals: globals::Globals, |     pub globals: globals::Globals, | ||||||
|     pub users: users::Users, |     pub users: users::Users, | ||||||
|  | @ -18,26 +20,38 @@ pub struct Database { | ||||||
| 
 | 
 | ||||||
| impl Database { | impl Database { | ||||||
|     /// Tries to remove the old database but ignores all errors.
 |     /// Tries to remove the old database but ignores all errors.
 | ||||||
|     pub fn try_remove(hostname: &str) { |     pub fn try_remove(server_name: &str) { | ||||||
|         let mut path = ProjectDirs::from("xyz", "koesters", "conduit") |         let mut path = ProjectDirs::from("xyz", "koesters", "conduit") | ||||||
|             .unwrap() |             .unwrap() | ||||||
|             .data_dir() |             .data_dir() | ||||||
|             .to_path_buf(); |             .to_path_buf(); | ||||||
|         path.push(hostname); |         path.push(server_name); | ||||||
|         let _ = remove_dir_all(path); |         let _ = remove_dir_all(path); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Load an existing database or create a new one.
 |     /// Load an existing database or create a new one.
 | ||||||
|     pub fn load_or_create(hostname: &str) -> Self { |     pub fn load_or_create(config: &Config) -> Self { | ||||||
|         let mut path = ProjectDirs::from("xyz", "koesters", "conduit") |         let server_name = config.get_str("server_name").unwrap_or("localhost"); | ||||||
|             .unwrap() | 
 | ||||||
|             .data_dir() |         let path = config | ||||||
|             .to_path_buf(); |             .get_str("database_path") | ||||||
|         path.push(hostname); |             .map(|x| x.to_owned()) | ||||||
|  |             .unwrap_or_else(|_| { | ||||||
|  |                 let path = ProjectDirs::from("xyz", "koesters", "conduit") | ||||||
|  |                     .unwrap() | ||||||
|  |                     .data_dir() | ||||||
|  |                     .join(server_name); | ||||||
|  |                 path.to_str().unwrap().to_owned() | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|         let db = sled::open(&path).unwrap(); |         let db = sled::open(&path).unwrap(); | ||||||
|  |         log::info!("Opened sled database at {}", path); | ||||||
| 
 | 
 | ||||||
|         Self { |         Self { | ||||||
|             globals: globals::Globals::load(db.open_tree("global").unwrap(), hostname.to_owned()), |             globals: globals::Globals::load( | ||||||
|  |                 db.open_tree("global").unwrap(), | ||||||
|  |                 server_name.to_owned(), | ||||||
|  |             ), | ||||||
|             users: users::Users { |             users: users::Users { | ||||||
|                 userid_password: db.open_tree("userid_password").unwrap(), |                 userid_password: db.open_tree("userid_password").unwrap(), | ||||||
|                 userdeviceids: db.open_tree("userdeviceids").unwrap(), |                 userdeviceids: db.open_tree("userdeviceids").unwrap(), | ||||||
|  |  | ||||||
|  | @ -4,13 +4,13 @@ pub const COUNTER: &str = "c"; | ||||||
| 
 | 
 | ||||||
| pub struct Globals { | pub struct Globals { | ||||||
|     pub(super) globals: sled::Tree, |     pub(super) globals: sled::Tree, | ||||||
|     hostname: String, |     server_name: String, | ||||||
|     keypair: ruma_signatures::Ed25519KeyPair, |     keypair: ruma_signatures::Ed25519KeyPair, | ||||||
|     reqwest_client: reqwest::Client, |     reqwest_client: reqwest::Client, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Globals { | impl Globals { | ||||||
|     pub fn load(globals: sled::Tree, hostname: String) -> Self { |     pub fn load(globals: sled::Tree, server_name: String) -> Self { | ||||||
|         let keypair = ruma_signatures::Ed25519KeyPair::new( |         let keypair = ruma_signatures::Ed25519KeyPair::new( | ||||||
|             &*globals |             &*globals | ||||||
|                 .update_and_fetch("keypair", utils::generate_keypair) |                 .update_and_fetch("keypair", utils::generate_keypair) | ||||||
|  | @ -22,15 +22,15 @@ impl Globals { | ||||||
| 
 | 
 | ||||||
|         Self { |         Self { | ||||||
|             globals, |             globals, | ||||||
|             hostname, |             server_name, | ||||||
|             keypair, |             keypair, | ||||||
|             reqwest_client: reqwest::Client::new(), |             reqwest_client: reqwest::Client::new(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns the hostname of the server.
 |     /// Returns the server_name of the server.
 | ||||||
|     pub fn hostname(&self) -> &str { |     pub fn server_name(&self) -> &str { | ||||||
|         &self.hostname |         &self.server_name | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns this server's keypair.
 |     /// Returns this server's keypair.
 | ||||||
|  |  | ||||||
|  | @ -216,7 +216,7 @@ impl Rooms { | ||||||
|             event_id: EventId::try_from("$thiswillbefilledinlater").expect("we know this is valid"), |             event_id: EventId::try_from("$thiswillbefilledinlater").expect("we know this is valid"), | ||||||
|             room_id: room_id.clone(), |             room_id: room_id.clone(), | ||||||
|             sender: sender.clone(), |             sender: sender.clone(), | ||||||
|             origin: globals.hostname().to_owned(), |             origin: globals.server_name().to_owned(), | ||||||
|             origin_server_ts: utils::millis_since_unix_epoch() |             origin_server_ts: utils::millis_since_unix_epoch() | ||||||
|                 .try_into() |                 .try_into() | ||||||
|                 .expect("this only fails many years in the future"), |                 .expect("this only fails many years in the future"), | ||||||
|  | @ -245,8 +245,12 @@ impl Rooms { | ||||||
|         .expect("ruma's reference hashes are correct"); |         .expect("ruma's reference hashes are correct"); | ||||||
| 
 | 
 | ||||||
|         let mut pdu_json = serde_json::to_value(&pdu)?; |         let mut pdu_json = serde_json::to_value(&pdu)?; | ||||||
|         ruma_signatures::hash_and_sign_event(globals.hostname(), globals.keypair(), &mut pdu_json) |         ruma_signatures::hash_and_sign_event( | ||||||
|             .expect("our new event can be hashed and signed"); |             globals.server_name(), | ||||||
|  |             globals.keypair(), | ||||||
|  |             &mut pdu_json, | ||||||
|  |         ) | ||||||
|  |         .expect("our new event can be hashed and signed"); | ||||||
| 
 | 
 | ||||||
|         self.replace_pdu_leaves(&room_id, &pdu.event_id)?; |         self.replace_pdu_leaves(&room_id, &pdu.event_id)?; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -75,8 +75,7 @@ fn setup_rocket() -> rocket::Rocket { | ||||||
|             ], |             ], | ||||||
|         ) |         ) | ||||||
|         .attach(AdHoc::on_attach("Config", |rocket| { |         .attach(AdHoc::on_attach("Config", |rocket| { | ||||||
|             let hostname = rocket.config().get_str("hostname").unwrap_or("localhost"); |             let data = Database::load_or_create(&rocket.config()); | ||||||
|             let data = Database::load_or_create(&hostname); |  | ||||||
| 
 | 
 | ||||||
|             Ok(rocket.manage(data)) |             Ok(rocket.manage(data)) | ||||||
|         })) |         })) | ||||||
|  |  | ||||||
|  | @ -55,12 +55,12 @@ pub async fn send_request<T: Endpoint>( | ||||||
| 
 | 
 | ||||||
|     request_map.insert("method".to_owned(), T::METADATA.method.to_string().into()); |     request_map.insert("method".to_owned(), T::METADATA.method.to_string().into()); | ||||||
|     request_map.insert("uri".to_owned(), T::METADATA.path.into()); |     request_map.insert("uri".to_owned(), T::METADATA.path.into()); | ||||||
|     request_map.insert("origin".to_owned(), db.globals.hostname().into()); |     request_map.insert("origin".to_owned(), db.globals.server_name().into()); | ||||||
|     request_map.insert("destination".to_owned(), destination.into()); |     request_map.insert("destination".to_owned(), destination.into()); | ||||||
| 
 | 
 | ||||||
|     let mut request_json = request_map.into(); |     let mut request_json = request_map.into(); | ||||||
|     ruma_signatures::sign_json( |     ruma_signatures::sign_json( | ||||||
|         db.globals.hostname(), |         db.globals.server_name(), | ||||||
|         db.globals.keypair(), |         db.globals.keypair(), | ||||||
|         &mut request_json, |         &mut request_json, | ||||||
|     ) |     ) | ||||||
|  | @ -82,7 +82,7 @@ pub async fn send_request<T: Endpoint>( | ||||||
|             AUTHORIZATION, |             AUTHORIZATION, | ||||||
|             HeaderValue::from_str(&format!( |             HeaderValue::from_str(&format!( | ||||||
|                 "X-Matrix origin={},key=\"{}\",sig=\"{}\"", |                 "X-Matrix origin={},key=\"{}\",sig=\"{}\"", | ||||||
|                 db.globals.hostname(), |                 db.globals.server_name(), | ||||||
|                 s.0, |                 s.0, | ||||||
|                 s.1 |                 s.1 | ||||||
|             )) |             )) | ||||||
|  | @ -156,7 +156,7 @@ pub fn get_server_keys(db: State<'_, Database>) -> Json<String> { | ||||||
|     ); |     ); | ||||||
|     let mut response = serde_json::from_slice( |     let mut response = serde_json::from_slice( | ||||||
|         http::Response::try_from(get_server_keys::Response { |         http::Response::try_from(get_server_keys::Response { | ||||||
|             server_name: db.globals.hostname().to_owned(), |             server_name: db.globals.server_name().to_owned(), | ||||||
|             verify_keys, |             verify_keys, | ||||||
|             old_verify_keys: BTreeMap::new(), |             old_verify_keys: BTreeMap::new(), | ||||||
|             signatures: BTreeMap::new(), |             signatures: BTreeMap::new(), | ||||||
|  | @ -166,7 +166,7 @@ pub fn get_server_keys(db: State<'_, Database>) -> Json<String> { | ||||||
|         .body(), |         .body(), | ||||||
|     ) |     ) | ||||||
|     .unwrap(); |     .unwrap(); | ||||||
|     ruma_signatures::sign_json(db.globals.hostname(), db.globals.keypair(), &mut response).unwrap(); |     ruma_signatures::sign_json(db.globals.server_name(), db.globals.keypair(), &mut response).unwrap(); | ||||||
|     Json(response.to_string()) |     Json(response.to_string()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										836
									
								
								sytest/are-we-synapse-yet.list
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										836
									
								
								sytest/are-we-synapse-yet.list
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,836 @@ | ||||||
|  | reg GET /register yields a set of flows | ||||||
|  | reg POST /register can create a user | ||||||
|  | reg POST /register downcases capitals in usernames | ||||||
|  | reg POST /register returns the same device_id as that in the request | ||||||
|  | reg POST /register rejects registration of usernames with '!' | ||||||
|  | reg POST /register rejects registration of usernames with '"' | ||||||
|  | reg POST /register rejects registration of usernames with ':' | ||||||
|  | reg POST /register rejects registration of usernames with '?' | ||||||
|  | reg POST /register rejects registration of usernames with '\' | ||||||
|  | reg POST /register rejects registration of usernames with '@' | ||||||
|  | reg POST /register rejects registration of usernames with '[' | ||||||
|  | reg POST /register rejects registration of usernames with ']' | ||||||
|  | reg POST /register rejects registration of usernames with '{' | ||||||
|  | reg POST /register rejects registration of usernames with '|' | ||||||
|  | reg POST /register rejects registration of usernames with '}' | ||||||
|  | reg POST /register rejects registration of usernames with '£' | ||||||
|  | reg POST /register rejects registration of usernames with 'é' | ||||||
|  | reg POST /register rejects registration of usernames with '\n' | ||||||
|  | reg POST /register rejects registration of usernames with ''' | ||||||
|  | reg POST /r0/admin/register with shared secret | ||||||
|  | reg POST /r0/admin/register admin with shared secret | ||||||
|  | reg POST /r0/admin/register with shared secret downcases capitals | ||||||
|  | reg POST /r0/admin/register with shared secret disallows symbols | ||||||
|  | reg POST rejects invalid utf-8 in JSON | ||||||
|  | log GET /login yields a set of flows | ||||||
|  | log POST /login can log in as a user | ||||||
|  | log POST /login returns the same device_id as that in the request | ||||||
|  | log POST /login can log in as a user with just the local part of the id | ||||||
|  | log POST /login as non-existing user is rejected | ||||||
|  | log POST /login wrong password is rejected | ||||||
|  | log Interactive authentication types include SSO | ||||||
|  | log Can perform interactive authentication with SSO | ||||||
|  | log The user must be consistent through an interactive authentication session with SSO | ||||||
|  | log The operation must be consistent through an interactive authentication session | ||||||
|  | v1s GET /events initially | ||||||
|  | v1s GET /initialSync initially | ||||||
|  | csa Version responds 200 OK with valid structure | ||||||
|  | pro PUT /profile/:user_id/displayname sets my name | ||||||
|  | pro GET /profile/:user_id/displayname publicly accessible | ||||||
|  | pro PUT /profile/:user_id/avatar_url sets my avatar | ||||||
|  | pro GET /profile/:user_id/avatar_url publicly accessible | ||||||
|  | dev GET /device/{deviceId} | ||||||
|  | dev GET /device/{deviceId} gives a 404 for unknown devices | ||||||
|  | dev GET /devices | ||||||
|  | dev PUT /device/{deviceId} updates device fields | ||||||
|  | dev PUT /device/{deviceId} gives a 404 for unknown devices | ||||||
|  | dev DELETE /device/{deviceId} | ||||||
|  | dev DELETE /device/{deviceId} requires UI auth user to match device owner | ||||||
|  | dev DELETE /device/{deviceId} with no body gives a 401 | ||||||
|  | dev The deleted device must be consistent through an interactive auth session | ||||||
|  | dev Users receive device_list updates for their own devices | ||||||
|  | pre GET /presence/:user_id/status fetches initial status | ||||||
|  | pre PUT /presence/:user_id/status updates my presence | ||||||
|  | crm POST /createRoom makes a public room | ||||||
|  | crm POST /createRoom makes a private room | ||||||
|  | crm POST /createRoom makes a private room with invites | ||||||
|  | crm POST /createRoom makes a room with a name | ||||||
|  | crm POST /createRoom makes a room with a topic | ||||||
|  | syn Can /sync newly created room | ||||||
|  | crm POST /createRoom creates a room with the given version | ||||||
|  | crm POST /createRoom rejects attempts to create rooms with numeric versions | ||||||
|  | crm POST /createRoom rejects attempts to create rooms with unknown versions | ||||||
|  | crm POST /createRoom ignores attempts to set the room version via creation_content | ||||||
|  | mem GET /rooms/:room_id/state/m.room.member/:user_id fetches my membership | ||||||
|  | mem GET /rooms/:room_id/state/m.room.member/:user_id?format=event fetches my membership event | ||||||
|  | rst GET /rooms/:room_id/state/m.room.power_levels fetches powerlevels | ||||||
|  | mem GET /rooms/:room_id/joined_members fetches my membership | ||||||
|  | v1s GET /rooms/:room_id/initialSync fetches initial sync state | ||||||
|  | pub GET /publicRooms lists newly-created room | ||||||
|  | ali GET /directory/room/:room_alias yields room ID | ||||||
|  | mem GET /joined_rooms lists newly-created room | ||||||
|  | rst POST /rooms/:room_id/state/m.room.name sets name | ||||||
|  | rst GET /rooms/:room_id/state/m.room.name gets name | ||||||
|  | rst POST /rooms/:room_id/state/m.room.topic sets topic | ||||||
|  | rst GET /rooms/:room_id/state/m.room.topic gets topic | ||||||
|  | rst GET /rooms/:room_id/state fetches entire room state | ||||||
|  | crm POST /createRoom with creation content | ||||||
|  | ali PUT /directory/room/:room_alias creates alias | ||||||
|  | nsp GET /rooms/:room_id/aliases lists aliases | ||||||
|  | jon POST /rooms/:room_id/join can join a room | ||||||
|  | jon POST /join/:room_alias can join a room | ||||||
|  | jon POST /join/:room_id can join a room | ||||||
|  | jon POST /join/:room_id can join a room with custom content | ||||||
|  | jon POST /join/:room_alias can join a room with custom content | ||||||
|  | lev POST /rooms/:room_id/leave can leave a room | ||||||
|  | inv POST /rooms/:room_id/invite can send an invite | ||||||
|  | ban POST /rooms/:room_id/ban can ban a user | ||||||
|  | snd POST /rooms/:room_id/send/:event_type sends a message | ||||||
|  | snd PUT /rooms/:room_id/send/:event_type/:txn_id sends a message | ||||||
|  | snd PUT /rooms/:room_id/send/:event_type/:txn_id deduplicates the same txn id | ||||||
|  | get GET /rooms/:room_id/messages returns a message | ||||||
|  | get GET /rooms/:room_id/messages lazy loads members correctly | ||||||
|  | typ PUT /rooms/:room_id/typing/:user_id sets typing notification | ||||||
|  | rst GET /rooms/:room_id/state/m.room.power_levels can fetch levels | ||||||
|  | rst PUT /rooms/:room_id/state/m.room.power_levels can set levels | ||||||
|  | rst PUT power_levels should not explode if the old power levels were empty | ||||||
|  | rst Both GET and PUT work | ||||||
|  | rct POST /rooms/:room_id/receipt can create receipts | ||||||
|  | red POST /rooms/:room_id/read_markers can create read marker | ||||||
|  | med POST /media/v1/upload can create an upload | ||||||
|  | med GET /media/v1/download can fetch the value again | ||||||
|  | cap GET /capabilities is present and well formed for registered user | ||||||
|  | cap GET /r0/capabilities is not public | ||||||
|  | reg Register with a recaptcha | ||||||
|  | reg registration is idempotent, without username specified | ||||||
|  | reg registration is idempotent, with username specified | ||||||
|  | reg registration remembers parameters | ||||||
|  | reg registration accepts non-ascii passwords | ||||||
|  | reg registration with inhibit_login inhibits login | ||||||
|  | reg User signups are forbidden from starting with '_' | ||||||
|  | reg Can register using an email address | ||||||
|  | log Can login with 3pid and password using m.login.password | ||||||
|  | log login types include SSO | ||||||
|  | log /login/cas/redirect redirects if the old m.login.cas login type is listed | ||||||
|  | log Can login with new user via CAS | ||||||
|  | lox Can logout current device | ||||||
|  | lox Can logout all devices | ||||||
|  | lox Request to logout with invalid an access token is rejected | ||||||
|  | lox Request to logout without an access token is rejected | ||||||
|  | log After changing password, can't log in with old password | ||||||
|  | log After changing password, can log in with new password | ||||||
|  | log After changing password, existing session still works | ||||||
|  | log After changing password, a different session no longer works by default | ||||||
|  | log After changing password, different sessions can optionally be kept | ||||||
|  | psh Pushers created with a different access token are deleted on password change | ||||||
|  | psh Pushers created with a the same access token are not deleted on password change | ||||||
|  | acc Can deactivate account | ||||||
|  | acc Can't deactivate account with wrong password | ||||||
|  | acc After deactivating account, can't log in with password | ||||||
|  | acc After deactivating account, can't log in with an email | ||||||
|  | v1s initialSync sees my presence status | ||||||
|  | pre Presence change reports an event to myself | ||||||
|  | pre Friends presence changes reports events | ||||||
|  | crm Room creation reports m.room.create to myself | ||||||
|  | crm Room creation reports m.room.member to myself | ||||||
|  | rst Setting room topic reports m.room.topic to myself | ||||||
|  | v1s Global initialSync | ||||||
|  | v1s Global initialSync with limit=0 gives no messages | ||||||
|  | v1s Room initialSync | ||||||
|  | v1s Room initialSync with limit=0 gives no messages | ||||||
|  | rst Setting state twice is idempotent | ||||||
|  | jon Joining room twice is idempotent | ||||||
|  | syn New room members see their own join event | ||||||
|  | v1s New room members see existing users' presence in room initialSync | ||||||
|  | syn Existing members see new members' join events | ||||||
|  | syn Existing members see new members' presence | ||||||
|  | v1s All room members see all room members' presence in global initialSync | ||||||
|  | f,jon Remote users can join room by alias | ||||||
|  | syn New room members see their own join event | ||||||
|  | v1s New room members see existing members' presence in room initialSync | ||||||
|  | syn Existing members see new members' join events | ||||||
|  | syn Existing members see new member's presence | ||||||
|  | v1s New room members see first user's profile information in global initialSync | ||||||
|  | v1s New room members see first user's profile information in per-room initialSync | ||||||
|  | f,jon Remote users may not join unfederated rooms | ||||||
|  | syn Local room members see posted message events | ||||||
|  | v1s Fetching eventstream a second time doesn't yield the message again | ||||||
|  | syn Local non-members don't see posted message events | ||||||
|  | get Local room members can get room messages | ||||||
|  | f,syn Remote room members also see posted message events | ||||||
|  | f,get Remote room members can get room messages | ||||||
|  | get Message history can be paginated | ||||||
|  | f,get Message history can be paginated over federation | ||||||
|  | eph Ephemeral messages received from clients are correctly expired | ||||||
|  | ali Room aliases can contain Unicode | ||||||
|  | f,ali Remote room alias queries can handle Unicode | ||||||
|  | ali Canonical alias can be set | ||||||
|  | ali Canonical alias can include alt_aliases | ||||||
|  | ali Regular users can add and delete aliases in the default room configuration | ||||||
|  | ali Regular users can add and delete aliases when m.room.aliases is restricted | ||||||
|  | ali Deleting a non-existent alias should return a 404 | ||||||
|  | ali Users can't delete other's aliases | ||||||
|  | ali Users with sufficient power-level can delete other's aliases | ||||||
|  | ali Can delete canonical alias | ||||||
|  | ali Alias creators can delete alias with no ops | ||||||
|  | ali Alias creators can delete canonical alias with no ops | ||||||
|  | ali Only room members can list aliases of a room | ||||||
|  | inv Can invite users to invite-only rooms | ||||||
|  | inv Uninvited users cannot join the room | ||||||
|  | inv Invited user can reject invite | ||||||
|  | f,inv Invited user can reject invite over federation | ||||||
|  | f,inv Invited user can reject invite over federation several times | ||||||
|  | inv Invited user can reject invite for empty room | ||||||
|  | f,inv Invited user can reject invite over federation for empty room | ||||||
|  | inv Invited user can reject local invite after originator leaves | ||||||
|  | inv Invited user can see room metadata | ||||||
|  | f,inv Remote invited user can see room metadata | ||||||
|  | inv Users cannot invite themselves to a room | ||||||
|  | inv Users cannot invite a user that is already in the room | ||||||
|  | ban Banned user is kicked and may not rejoin until unbanned | ||||||
|  | f,ban Remote banned user is kicked and may not rejoin until unbanned | ||||||
|  | ban 'ban' event respects room powerlevel | ||||||
|  | plv setting 'm.room.name' respects room powerlevel | ||||||
|  | plv setting 'm.room.power_levels' respects room powerlevel (2 subtests) | ||||||
|  | plv Unprivileged users can set m.room.topic if it only needs level 0 | ||||||
|  | plv Users cannot set ban powerlevel higher than their own (2 subtests) | ||||||
|  | plv Users cannot set kick powerlevel higher than their own (2 subtests) | ||||||
|  | plv Users cannot set redact powerlevel higher than their own (2 subtests) | ||||||
|  | v1s Check that event streams started after a client joined a room work (SYT-1) | ||||||
|  | v1s Event stream catches up fully after many messages | ||||||
|  | xxx POST /rooms/:room_id/redact/:event_id as power user redacts message | ||||||
|  | xxx POST /rooms/:room_id/redact/:event_id as original message sender redacts message | ||||||
|  | xxx POST /rooms/:room_id/redact/:event_id as random user does not redact message | ||||||
|  | xxx POST /redact disallows redaction of event in different room | ||||||
|  | xxx Redaction of a redaction redacts the redaction reason | ||||||
|  | v1s A departed room is still included in /initialSync (SPEC-216) | ||||||
|  | v1s Can get rooms/{roomId}/initialSync for a departed room (SPEC-216) | ||||||
|  | rst Can get rooms/{roomId}/state for a departed room (SPEC-216) | ||||||
|  | mem Can get rooms/{roomId}/members for a departed room (SPEC-216) | ||||||
|  | get Can get rooms/{roomId}/messages for a departed room (SPEC-216) | ||||||
|  | rst Can get 'm.room.name' state for a departed room (SPEC-216) | ||||||
|  | syn Getting messages going forward is limited for a departed room (SPEC-216) | ||||||
|  | 3pd Can invite existing 3pid | ||||||
|  | 3pd Can invite existing 3pid with no ops into a private room | ||||||
|  | 3pd Can invite existing 3pid in createRoom | ||||||
|  | 3pd Can invite unbound 3pid | ||||||
|  | f,3pd Can invite unbound 3pid over federation | ||||||
|  | 3pd Can invite unbound 3pid with no ops into a private room | ||||||
|  | f,3pd Can invite unbound 3pid over federation with no ops into a private room | ||||||
|  | f,3pd Can invite unbound 3pid over federation with users from both servers | ||||||
|  | 3pd Can accept unbound 3pid invite after inviter leaves | ||||||
|  | 3pd Can accept third party invite with /join | ||||||
|  | 3pd 3pid invite join with wrong but valid signature are rejected | ||||||
|  | 3pd 3pid invite join valid signature but revoked keys are rejected | ||||||
|  | 3pd 3pid invite join valid signature but unreachable ID server are rejected | ||||||
|  | gst Guest user cannot call /events globally | ||||||
|  | gst Guest users can join guest_access rooms | ||||||
|  | gst Guest users can send messages to guest_access rooms if joined | ||||||
|  | gst Guest user calling /events doesn't tightloop | ||||||
|  | gst Guest users are kicked from guest_access rooms on revocation of guest_access | ||||||
|  | gst Guest user can set display names | ||||||
|  | gst Guest users are kicked from guest_access rooms on revocation of guest_access over federation | ||||||
|  | gst Guest user can upgrade to fully featured user | ||||||
|  | gst Guest user cannot upgrade other users | ||||||
|  | pub GET /publicRooms lists rooms | ||||||
|  | pub GET /publicRooms includes avatar URLs | ||||||
|  | gst Guest users can accept invites to private rooms over federation | ||||||
|  | gst Guest users denied access over federation if guest access prohibited | ||||||
|  | mem Room members can override their displayname on a room-specific basis | ||||||
|  | mem Room members can join a room with an overridden displayname | ||||||
|  | mem Users cannot kick users from a room they are not in | ||||||
|  | mem Users cannot kick users who have already left a room | ||||||
|  | typ Typing notification sent to local room members | ||||||
|  | f,typ Typing notifications also sent to remote room members | ||||||
|  | typ Typing can be explicitly stopped | ||||||
|  | rct Read receipts are visible to /initialSync | ||||||
|  | rct Read receipts are sent as events | ||||||
|  | rct Receipts must be m.read | ||||||
|  | pro displayname updates affect room member events | ||||||
|  | pro avatar_url updates affect room member events | ||||||
|  | gst m.room.history_visibility == "world_readable" allows/forbids appropriately for Guest users | ||||||
|  | gst m.room.history_visibility == "shared" allows/forbids appropriately for Guest users | ||||||
|  | gst m.room.history_visibility == "invited" allows/forbids appropriately for Guest users | ||||||
|  | gst m.room.history_visibility == "joined" allows/forbids appropriately for Guest users | ||||||
|  | gst m.room.history_visibility == "default" allows/forbids appropriately for Guest users | ||||||
|  | gst Guest non-joined user cannot call /events on shared room | ||||||
|  | gst Guest non-joined user cannot call /events on invited room | ||||||
|  | gst Guest non-joined user cannot call /events on joined room | ||||||
|  | gst Guest non-joined user cannot call /events on default room | ||||||
|  | gst Guest non-joined user can call /events on world_readable room | ||||||
|  | gst Guest non-joined users can get state for world_readable rooms | ||||||
|  | gst Guest non-joined users can get individual state for world_readable rooms | ||||||
|  | gst Guest non-joined users cannot room initalSync for non-world_readable rooms | ||||||
|  | gst Guest non-joined users can room initialSync for world_readable rooms | ||||||
|  | gst Guest non-joined users can get individual state for world_readable rooms after leaving | ||||||
|  | gst Guest non-joined users cannot send messages to guest_access rooms if not joined | ||||||
|  | gst Guest users can sync from world_readable guest_access rooms if joined | ||||||
|  | gst Guest users can sync from shared guest_access rooms if joined | ||||||
|  | gst Guest users can sync from invited guest_access rooms if joined | ||||||
|  | gst Guest users can sync from joined guest_access rooms if joined | ||||||
|  | gst Guest users can sync from default guest_access rooms if joined | ||||||
|  | ath m.room.history_visibility == "world_readable" allows/forbids appropriately for Real users | ||||||
|  | ath m.room.history_visibility == "shared" allows/forbids appropriately for Real users | ||||||
|  | ath m.room.history_visibility == "invited" allows/forbids appropriately for Real users | ||||||
|  | ath m.room.history_visibility == "joined" allows/forbids appropriately for Real users | ||||||
|  | ath m.room.history_visibility == "default" allows/forbids appropriately for Real users | ||||||
|  | ath Real non-joined user cannot call /events on shared room | ||||||
|  | ath Real non-joined user cannot call /events on invited room | ||||||
|  | ath Real non-joined user cannot call /events on joined room | ||||||
|  | ath Real non-joined user cannot call /events on default room | ||||||
|  | ath Real non-joined user can call /events on world_readable room | ||||||
|  | ath Real non-joined users can get state for world_readable rooms | ||||||
|  | ath Real non-joined users can get individual state for world_readable rooms | ||||||
|  | ath Real non-joined users cannot room initalSync for non-world_readable rooms | ||||||
|  | ath Real non-joined users can room initialSync for world_readable rooms | ||||||
|  | ath Real non-joined users can get individual state for world_readable rooms after leaving | ||||||
|  | ath Real non-joined users cannot send messages to guest_access rooms if not joined | ||||||
|  | ath Real users can sync from world_readable guest_access rooms if joined | ||||||
|  | ath Real users can sync from shared guest_access rooms if joined | ||||||
|  | ath Real users can sync from invited guest_access rooms if joined | ||||||
|  | ath Real users can sync from joined guest_access rooms if joined | ||||||
|  | ath Real users can sync from default guest_access rooms if joined | ||||||
|  | ath Only see history_visibility changes on boundaries | ||||||
|  | f,ath Backfill works correctly with history visibility set to joined | ||||||
|  | fgt Forgotten room messages cannot be paginated | ||||||
|  | fgt Forgetting room does not show up in v2 /sync | ||||||
|  | fgt Can forget room you've been kicked from | ||||||
|  | fgt Can't forget room you're still in | ||||||
|  | mem Can re-join room if re-invited | ||||||
|  | ath Only original members of the room can see messages from erased users | ||||||
|  | mem /joined_rooms returns only joined rooms | ||||||
|  | mem /joined_members return joined members | ||||||
|  | ctx /context/ on joined room works | ||||||
|  | ctx /context/ on non world readable room does not work | ||||||
|  | ctx /context/ returns correct number of events | ||||||
|  | ctx /context/ with lazy_load_members filter works | ||||||
|  | get /event/ on joined room works | ||||||
|  | get /event/ on non world readable room does not work | ||||||
|  | get /event/ does not allow access to events before the user joined | ||||||
|  | mem Can get rooms/{roomId}/members | ||||||
|  | mem Can get rooms/{roomId}/members at a given point | ||||||
|  | mem Can filter rooms/{roomId}/members | ||||||
|  | upg /upgrade creates a new room | ||||||
|  | upg /upgrade should preserve room visibility for public rooms | ||||||
|  | upg /upgrade should preserve room visibility for private rooms | ||||||
|  | upg /upgrade copies >100 power levels to the new room | ||||||
|  | upg /upgrade copies the power levels to the new room | ||||||
|  | upg /upgrade preserves the power level of the upgrading user in old and new rooms | ||||||
|  | upg /upgrade copies important state to the new room | ||||||
|  | upg /upgrade copies ban events to the new room | ||||||
|  | upg local user has push rules copied to upgraded room | ||||||
|  | f,upg remote user has push rules copied to upgraded room | ||||||
|  | upg /upgrade moves aliases to the new room | ||||||
|  | upg /upgrade moves remote aliases to the new room | ||||||
|  | upg /upgrade preserves direct room state | ||||||
|  | upg /upgrade preserves room federation ability | ||||||
|  | upg /upgrade restricts power levels in the old room | ||||||
|  | upg /upgrade restricts power levels in the old room when the old PLs are unusual | ||||||
|  | upg /upgrade to an unknown version is rejected | ||||||
|  | upg /upgrade is rejected if the user can't send state events | ||||||
|  | upg /upgrade of a bogus room fails gracefully | ||||||
|  | upg Cannot send tombstone event that points to the same room | ||||||
|  | f,upg Local and remote users' homeservers remove a room from their public directory on upgrade | ||||||
|  | rst Name/topic keys are correct | ||||||
|  | f,pub Can get remote public room list | ||||||
|  | pub Can paginate public room list | ||||||
|  | pub Can search public room list | ||||||
|  | syn Can create filter | ||||||
|  | syn Can download filter | ||||||
|  | syn Can sync | ||||||
|  | syn Can sync a joined room | ||||||
|  | syn Full state sync includes joined rooms | ||||||
|  | syn Newly joined room is included in an incremental sync | ||||||
|  | syn Newly joined room has correct timeline in incremental sync | ||||||
|  | syn Newly joined room includes presence in incremental sync | ||||||
|  | syn Get presence for newly joined members in incremental sync | ||||||
|  | syn Can sync a room with a single message | ||||||
|  | syn Can sync a room with a message with a transaction id | ||||||
|  | syn A message sent after an initial sync appears in the timeline of an incremental sync. | ||||||
|  | syn A filtered timeline reaches its limit | ||||||
|  | syn Syncing a new room with a large timeline limit isn't limited | ||||||
|  | syn A full_state incremental update returns only recent timeline | ||||||
|  | syn A prev_batch token can be used in the v1 messages API | ||||||
|  | syn A next_batch token can be used in the v1 messages API | ||||||
|  | syn User sees their own presence in a sync | ||||||
|  | syn User is offline if they set_presence=offline in their sync | ||||||
|  | syn User sees updates to presence from other users in the incremental sync. | ||||||
|  | syn State is included in the timeline in the initial sync | ||||||
|  | f,syn State from remote users is included in the state in the initial sync | ||||||
|  | syn Changes to state are included in an incremental sync | ||||||
|  | syn Changes to state are included in an gapped incremental sync | ||||||
|  | f,syn State from remote users is included in the timeline in an incremental sync | ||||||
|  | syn A full_state incremental update returns all state | ||||||
|  | syn When user joins a room the state is included in the next sync | ||||||
|  | syn A change to displayname should not result in a full state sync | ||||||
|  | syn A change to displayname should appear in incremental /sync | ||||||
|  | syn When user joins a room the state is included in a gapped sync | ||||||
|  | syn When user joins and leaves a room in the same batch, the full state is still included in the next sync | ||||||
|  | syn Current state appears in timeline in private history | ||||||
|  | syn Current state appears in timeline in private history with many messages before | ||||||
|  | syn Current state appears in timeline in private history with many messages after | ||||||
|  | syn Rooms a user is invited to appear in an initial sync | ||||||
|  | syn Rooms a user is invited to appear in an incremental sync | ||||||
|  | syn Newly joined room is included in an incremental sync after invite | ||||||
|  | syn Sync can be polled for updates | ||||||
|  | syn Sync is woken up for leaves | ||||||
|  | syn Left rooms appear in the leave section of sync | ||||||
|  | syn Newly left rooms appear in the leave section of incremental sync | ||||||
|  | syn We should see our own leave event, even if history_visibility is restricted (SYN-662) | ||||||
|  | syn We should see our own leave event when rejecting an invite, even if history_visibility is restricted (riot-web/3462) | ||||||
|  | syn Newly left rooms appear in the leave section of gapped sync | ||||||
|  | syn Previously left rooms don't appear in the leave section of sync | ||||||
|  | syn Left rooms appear in the leave section of full state sync | ||||||
|  | syn Archived rooms only contain history from before the user left | ||||||
|  | syn Banned rooms appear in the leave section of sync | ||||||
|  | syn Newly banned rooms appear in the leave section of incremental sync | ||||||
|  | syn Newly banned rooms appear in the leave section of incremental sync | ||||||
|  | syn Typing events appear in initial sync | ||||||
|  | syn Typing events appear in incremental sync | ||||||
|  | syn Typing events appear in gapped sync | ||||||
|  | syn Read receipts appear in initial v2 /sync | ||||||
|  | syn New read receipts appear in incremental v2 /sync | ||||||
|  | syn Can pass a JSON filter as a query parameter | ||||||
|  | syn Can request federation format via the filter | ||||||
|  | syn Read markers appear in incremental v2 /sync | ||||||
|  | syn Read markers appear in initial v2 /sync | ||||||
|  | syn Read markers can be updated | ||||||
|  | syn Lazy loading parameters in the filter are strictly boolean | ||||||
|  | syn The only membership state included in an initial sync is for all the senders in the timeline | ||||||
|  | syn The only membership state included in an incremental sync is for senders in the timeline | ||||||
|  | syn The only membership state included in a gapped incremental sync is for senders in the timeline | ||||||
|  | syn Gapped incremental syncs include all state changes | ||||||
|  | syn Old leaves are present in gapped incremental syncs | ||||||
|  | syn Leaves are present in non-gapped incremental syncs | ||||||
|  | syn Old members are included in gappy incr LL sync if they start speaking | ||||||
|  | syn Members from the gap are included in gappy incr LL sync | ||||||
|  | syn We don't send redundant membership state across incremental syncs by default | ||||||
|  | syn We do send redundant membership state across incremental syncs if asked | ||||||
|  | syn Unnamed room comes with a name summary | ||||||
|  | syn Named room comes with just joined member count summary | ||||||
|  | syn Room summary only has 5 heroes | ||||||
|  | syn Room summary counts change when membership changes | ||||||
|  | rmv User can create and send/receive messages in a room with version 1 | ||||||
|  | rmv User can create and send/receive messages in a room with version 1 (2 subtests) | ||||||
|  | rmv local user can join room with version 1 | ||||||
|  | rmv User can invite local user to room with version 1 | ||||||
|  | rmv remote user can join room with version 1 | ||||||
|  | rmv User can invite remote user to room with version 1 | ||||||
|  | rmv Remote user can backfill in a room with version 1 | ||||||
|  | rmv Can reject invites over federation for rooms with version 1 | ||||||
|  | rmv Can receive redactions from regular users over federation in room version 1 | ||||||
|  | rmv User can create and send/receive messages in a room with version 2 | ||||||
|  | rmv User can create and send/receive messages in a room with version 2 (2 subtests) | ||||||
|  | rmv local user can join room with version 2 | ||||||
|  | rmv User can invite local user to room with version 2 | ||||||
|  | rmv remote user can join room with version 2 | ||||||
|  | rmv User can invite remote user to room with version 2 | ||||||
|  | rmv Remote user can backfill in a room with version 2 | ||||||
|  | rmv Can reject invites over federation for rooms with version 2 | ||||||
|  | rmv Can receive redactions from regular users over federation in room version 2 | ||||||
|  | rmv User can create and send/receive messages in a room with version 3 | ||||||
|  | rmv User can create and send/receive messages in a room with version 3 (2 subtests) | ||||||
|  | rmv local user can join room with version 3 | ||||||
|  | rmv User can invite local user to room with version 3 | ||||||
|  | rmv remote user can join room with version 3 | ||||||
|  | rmv User can invite remote user to room with version 3 | ||||||
|  | rmv Remote user can backfill in a room with version 3 | ||||||
|  | rmv Can reject invites over federation for rooms with version 3 | ||||||
|  | rmv Can receive redactions from regular users over federation in room version 3 | ||||||
|  | rmv User can create and send/receive messages in a room with version 4 | ||||||
|  | rmv User can create and send/receive messages in a room with version 4 (2 subtests) | ||||||
|  | rmv local user can join room with version 4 | ||||||
|  | rmv User can invite local user to room with version 4 | ||||||
|  | rmv remote user can join room with version 4 | ||||||
|  | rmv User can invite remote user to room with version 4 | ||||||
|  | rmv Remote user can backfill in a room with version 4 | ||||||
|  | rmv Can reject invites over federation for rooms with version 4 | ||||||
|  | rmv Can receive redactions from regular users over federation in room version 4 | ||||||
|  | rmv User can create and send/receive messages in a room with version 5 | ||||||
|  | rmv User can create and send/receive messages in a room with version 5 (2 subtests) | ||||||
|  | rmv local user can join room with version 5 | ||||||
|  | rmv User can invite local user to room with version 5 | ||||||
|  | rmv remote user can join room with version 5 | ||||||
|  | rmv User can invite remote user to room with version 5 | ||||||
|  | rmv Remote user can backfill in a room with version 5 | ||||||
|  | rmv Can reject invites over federation for rooms with version 5 | ||||||
|  | rmv Can receive redactions from regular users over federation in room version 5 | ||||||
|  | pre Presence changes are reported to local room members | ||||||
|  | f,pre Presence changes are also reported to remote room members | ||||||
|  | pre Presence changes to UNAVAILABLE are reported to local room members | ||||||
|  | f,pre Presence changes to UNAVAILABLE are reported to remote room members | ||||||
|  | v1s Newly created users see their own presence in /initialSync (SYT-34) | ||||||
|  | dvk Can upload device keys | ||||||
|  | dvk Should reject keys claiming to belong to a different user | ||||||
|  | dvk Can query device keys using POST | ||||||
|  | dvk Can query specific device keys using POST | ||||||
|  | dvk query for user with no keys returns empty key dict | ||||||
|  | dvk Can claim one time key using POST | ||||||
|  | f,dvk Can query remote device keys using POST | ||||||
|  | f,dvk Can claim remote one time key using POST | ||||||
|  | dvk Local device key changes appear in v2 /sync | ||||||
|  | dvk Local new device changes appear in v2 /sync | ||||||
|  | dvk Local delete device changes appear in v2 /sync | ||||||
|  | dvk Local update device changes appear in v2 /sync | ||||||
|  | dvk Can query remote device keys using POST after notification | ||||||
|  | f,dev Device deletion propagates over federation | ||||||
|  | f,dev If remote user leaves room, changes device and rejoins we see update in sync | ||||||
|  | f,dev If remote user leaves room we no longer receive device updates | ||||||
|  | dvk Local device key changes appear in /keys/changes | ||||||
|  | dvk New users appear in /keys/changes | ||||||
|  | f,dvk If remote user leaves room, changes device and rejoins we see update in /keys/changes | ||||||
|  | dvk Get left notifs in sync and /keys/changes when other user leaves | ||||||
|  | dvk Get left notifs for other users in sync and /keys/changes when user leaves | ||||||
|  | f,dvk If user leaves room, remote user changes device and rejoins we see update in /sync and /keys/changes | ||||||
|  | dvk Can create backup version | ||||||
|  | dvk Can update backup version | ||||||
|  | dvk Responds correctly when backup is empty | ||||||
|  | dvk Can backup keys | ||||||
|  | dvk Can update keys with better versions | ||||||
|  | dvk Will not update keys with worse versions | ||||||
|  | dvk Will not back up to an old backup version | ||||||
|  | dvk Can delete backup | ||||||
|  | dvk Deleted & recreated backups are empty | ||||||
|  | dvk Can create more than 10 backup versions | ||||||
|  | dvk Can upload self-signing keys | ||||||
|  | dvk Fails to upload self-signing keys with no auth | ||||||
|  | dvk Fails to upload self-signing key without master key | ||||||
|  | dvk Changing master key notifies local users | ||||||
|  | dvk Changing user-signing key notifies local users | ||||||
|  | f,dvk can fetch self-signing keys over federation | ||||||
|  | f,dvk uploading self-signing key notifies over federation | ||||||
|  | f,dvk uploading signed devices gets propagated over federation | ||||||
|  | tag Can add tag | ||||||
|  | tag Can remove tag | ||||||
|  | tag Can list tags for a room | ||||||
|  | v1s Tags appear in the v1 /events stream | ||||||
|  | v1s Tags appear in the v1 /initalSync | ||||||
|  | v1s Tags appear in the v1 room initial sync | ||||||
|  | tag Tags appear in an initial v2 /sync | ||||||
|  | tag Newly updated tags appear in an incremental v2 /sync | ||||||
|  | tag Deleted tags appear in an incremental v2 /sync | ||||||
|  | tag local user has tags copied to the new room | ||||||
|  | f,tag remote user has tags copied to the new room | ||||||
|  | sch Can search for an event by body | ||||||
|  | sch Can get context around search results | ||||||
|  | sch Can back-paginate search results | ||||||
|  | sch Search works across an upgraded room and its predecessor | ||||||
|  | sch Search results with rank ordering do not include redacted events | ||||||
|  | sch Search results with recent ordering do not include redacted events | ||||||
|  | acc Can add account data | ||||||
|  | acc Can add account data to room | ||||||
|  | acc Can get account data without syncing | ||||||
|  | acc Can get room account data without syncing | ||||||
|  | v1s Latest account data comes down in /initialSync | ||||||
|  | v1s Latest account data comes down in room initialSync | ||||||
|  | v1s Account data appears in v1 /events stream | ||||||
|  | v1s Room account data appears in v1 /events stream | ||||||
|  | acc Latest account data appears in v2 /sync | ||||||
|  | acc New account data appears in incremental v2 /sync | ||||||
|  | oid Can generate a openid access_token that can be exchanged for information about a user | ||||||
|  | oid Invalid openid access tokens are rejected | ||||||
|  | oid Requests to userinfo without access tokens are rejected | ||||||
|  | std Can send a message directly to a device using PUT /sendToDevice | ||||||
|  | std Can recv a device message using /sync | ||||||
|  | std Can recv device messages until they are acknowledged | ||||||
|  | std Device messages with the same txn_id are deduplicated | ||||||
|  | std Device messages wake up /sync | ||||||
|  | std Can recv device messages over federation | ||||||
|  | std Device messages over federation wake up /sync | ||||||
|  | std Can send messages with a wildcard device id | ||||||
|  | std Can send messages with a wildcard device id to two devices | ||||||
|  | std Wildcard device messages wake up /sync | ||||||
|  | std Wildcard device messages over federation wake up /sync | ||||||
|  | adm /whois | ||||||
|  | nsp /purge_history | ||||||
|  | nsp /purge_history by ts | ||||||
|  | nsp Can backfill purged history | ||||||
|  | nsp Shutdown room | ||||||
|  | ign Ignore user in existing room | ||||||
|  | ign Ignore invite in full sync | ||||||
|  | ign Ignore invite in incremental sync | ||||||
|  | fky Checking local federation server | ||||||
|  | fky Federation key API allows unsigned requests for keys | ||||||
|  | fky Federation key API can act as a notary server via a GET request | ||||||
|  | fky Federation key API can act as a notary server via a POST request | ||||||
|  | fky Key notary server should return an expired key if it can't find any others | ||||||
|  | fky Key notary server must not overwrite a valid key with a spurious result from the origin server | ||||||
|  | fqu Non-numeric ports in server names are rejected | ||||||
|  | fqu Outbound federation can query profile data | ||||||
|  | fqu Inbound federation can query profile data | ||||||
|  | fqu Outbound federation can query room alias directory | ||||||
|  | fqu Inbound federation can query room alias directory | ||||||
|  | fsj Outbound federation can query v1 /send_join | ||||||
|  | fsj Outbound federation can query v2 /send_join | ||||||
|  | fmj Outbound federation passes make_join failures through to the client | ||||||
|  | fsj Inbound federation can receive v1 /send_join | ||||||
|  | fsj Inbound federation can receive v2 /send_join | ||||||
|  | fmj Inbound /v1/make_join rejects remote attempts to join local users to rooms | ||||||
|  | fsj Inbound /v1/send_join rejects incorrectly-signed joins | ||||||
|  | fsj Inbound /v1/send_join rejects joins from other servers | ||||||
|  | fau Inbound federation rejects remote attempts to kick local users to rooms | ||||||
|  | frv Inbound federation rejects attempts to join v1 rooms from servers without v1 support | ||||||
|  | frv Inbound federation rejects attempts to join v2 rooms from servers lacking version support | ||||||
|  | frv Inbound federation rejects attempts to join v2 rooms from servers only supporting v1 | ||||||
|  | frv Inbound federation accepts attempts to join v2 rooms from servers with support | ||||||
|  | frv Outbound federation correctly handles unsupported room versions | ||||||
|  | frv A pair of servers can establish a join in a v2 room | ||||||
|  | fsj Outbound federation rejects send_join responses with no m.room.create event | ||||||
|  | frv Outbound federation rejects m.room.create events with an unknown room version | ||||||
|  | fsj Event with an invalid signature in the send_join response should not cause room join to fail | ||||||
|  | fed Outbound federation can send events | ||||||
|  | fed Inbound federation can receive events | ||||||
|  | fed Inbound federation can receive redacted events | ||||||
|  | fed Ephemeral messages received from servers are correctly expired | ||||||
|  | fed Events whose auth_events are in the wrong room do not mess up the room state | ||||||
|  | fed Inbound federation can return events | ||||||
|  | fed Inbound federation redacts events from erased users | ||||||
|  | fme Outbound federation can request missing events | ||||||
|  | fme Inbound federation can return missing events for world_readable visibility | ||||||
|  | fme Inbound federation can return missing events for shared visibility | ||||||
|  | fme Inbound federation can return missing events for invite visibility | ||||||
|  | fme Inbound federation can return missing events for joined visibility | ||||||
|  | fme outliers whose auth_events are in a different room are correctly rejected | ||||||
|  | fbk Outbound federation can backfill events | ||||||
|  | fbk Inbound federation can backfill events | ||||||
|  | fbk Backfill checks the events requested belong to the room | ||||||
|  | fbk Backfilled events whose prev_events are in a different room do not allow cross-room back-pagination | ||||||
|  | fiv Outbound federation can send invites via v1 API | ||||||
|  | fiv Outbound federation can send invites via v2 API | ||||||
|  | fiv Inbound federation can receive invites via v1 API | ||||||
|  | fiv Inbound federation can receive invites via v2 API | ||||||
|  | fiv Inbound federation can receive invite and reject when remote replies with a 403 | ||||||
|  | fiv Inbound federation can receive invite and reject when remote replies with a 500 | ||||||
|  | fiv Inbound federation can receive invite and reject when remote is unreachable | ||||||
|  | fiv Inbound federation rejects invites which are not signed by the sender | ||||||
|  | fiv Inbound federation can receive invite rejections | ||||||
|  | fiv Inbound federation rejects incorrectly-signed invite rejections | ||||||
|  | fsl Inbound /v1/send_leave rejects leaves from other servers | ||||||
|  | fst Inbound federation can get state for a room | ||||||
|  | fst Inbound federation of state requires event_id as a mandatory paramater | ||||||
|  | fst Inbound federation can get state_ids for a room | ||||||
|  | fst Inbound federation of state_ids requires event_id as a mandatory paramater | ||||||
|  | fst  Federation rejects inbound events where the prev_events cannot be found | ||||||
|  | fst Room state at a rejected message event is the same as its predecessor | ||||||
|  | fst Room state at a rejected state event is the same as its predecessor | ||||||
|  | fst Outbound federation requests missing prev_events and then asks for /state_ids and resolves the state | ||||||
|  | fst Federation handles empty auth_events in state_ids sanely | ||||||
|  | fst Getting state checks the events requested belong to the room | ||||||
|  | fst Getting state IDs checks the events requested belong to the room | ||||||
|  | fst Should not be able to take over the room by pretending there is no PL event | ||||||
|  | fpb Inbound federation can get public room list | ||||||
|  | fed Outbound federation sends receipts | ||||||
|  | fed Inbound federation rejects receipts from wrong remote | ||||||
|  | fed Inbound federation ignores redactions from invalid servers room > v3 | ||||||
|  | fed An event which redacts an event in a different room should be ignored | ||||||
|  | fed An event which redacts itself should be ignored | ||||||
|  | fed A pair of events which redact each other should be ignored | ||||||
|  | fdk Local device key changes get to remote servers | ||||||
|  | fdk Server correctly handles incoming m.device_list_update | ||||||
|  | fdk Server correctly resyncs when client query keys and there is no remote cache | ||||||
|  | fdk Server correctly resyncs when server leaves and rejoins a room | ||||||
|  | fdk Local device key changes get to remote servers with correct prev_id | ||||||
|  | fdk Device list doesn't change if remote server is down | ||||||
|  | fdk If a device list update goes missing, the server resyncs on the next one | ||||||
|  | fst Name/topic keys are correct | ||||||
|  | fau Remote servers cannot set power levels in rooms without existing powerlevels | ||||||
|  | fau Remote servers should reject attempts by non-creators to set the power levels | ||||||
|  | fau Inbound federation rejects typing notifications from wrong remote | ||||||
|  | fed Forward extremities remain so even after the next events are populated as outliers | ||||||
|  | fau Banned servers cannot send events | ||||||
|  | fau Banned servers cannot /make_join | ||||||
|  | fau Banned servers cannot /send_join | ||||||
|  | fau Banned servers cannot /make_leave | ||||||
|  | fau Banned servers cannot /send_leave | ||||||
|  | fau Banned servers cannot /invite | ||||||
|  | fau Banned servers cannot get room state | ||||||
|  | fau Banned servers cannot get room state ids | ||||||
|  | fau Banned servers cannot backfill | ||||||
|  | fau Banned servers cannot /event_auth | ||||||
|  | fau Banned servers cannot get missing events | ||||||
|  | fau Server correctly handles transactions that break edu limits | ||||||
|  | fau Inbound federation correctly soft fails events | ||||||
|  | fau Inbound federation accepts a second soft-failed event | ||||||
|  | fau Inbound federation correctly handles soft failed events as extremities | ||||||
|  | med Can upload with Unicode file name | ||||||
|  | med Can download with Unicode file name locally | ||||||
|  | f,med Can download with Unicode file name over federation | ||||||
|  | med Alternative server names do not cause a routing loop | ||||||
|  | med Can download specifying a different Unicode file name | ||||||
|  | med Can upload without a file name | ||||||
|  | med Can download without a file name locally | ||||||
|  | f,med Can download without a file name over federation | ||||||
|  | med Can upload with ASCII file name | ||||||
|  | med Can download file 'ascii' | ||||||
|  | med Can download file 'name with spaces' | ||||||
|  | med Can download file 'name;with;semicolons' | ||||||
|  | med Can download specifying a different ASCII file name | ||||||
|  | med Can send image in room message | ||||||
|  | med Can fetch images in room | ||||||
|  | med POSTed media can be thumbnailed | ||||||
|  | f,med Remote media can be thumbnailed | ||||||
|  | med Test URL preview | ||||||
|  | med Can read configuration endpoint | ||||||
|  | nsp Can quarantine media in rooms | ||||||
|  | udr User appears in user directory | ||||||
|  | udr User in private room doesn't appear in user directory | ||||||
|  | udr User joining then leaving public room appears and dissappears from directory | ||||||
|  | udr Users appear/disappear from directory when join_rules are changed | ||||||
|  | udr Users appear/disappear from directory when history_visibility are changed | ||||||
|  | udr Users stay in directory when join_rules are changed but history_visibility is world_readable | ||||||
|  | f,udr User in remote room doesn't appear in user directory after server left room | ||||||
|  | udr User directory correctly update on display name change | ||||||
|  | udr User in shared private room does appear in user directory | ||||||
|  | udr User in shared private room does appear in user directory until leave | ||||||
|  | udr User in dir while user still shares private rooms | ||||||
|  | nsp Create group | ||||||
|  | nsp Add group rooms | ||||||
|  | nsp Remove group rooms | ||||||
|  | nsp Get local group profile | ||||||
|  | nsp Get local group users | ||||||
|  | nsp Add/remove local group rooms | ||||||
|  | nsp Get local group summary | ||||||
|  | nsp Get remote group profile | ||||||
|  | nsp Get remote group users | ||||||
|  | nsp Add/remove remote group rooms | ||||||
|  | nsp Get remote group summary | ||||||
|  | nsp Add local group users | ||||||
|  | nsp Remove self from local group | ||||||
|  | nsp Remove other from local group | ||||||
|  | nsp Add remote group users | ||||||
|  | nsp Remove self from remote group | ||||||
|  | nsp Listing invited users of a remote group when not a member returns a 403 | ||||||
|  | nsp Add group category | ||||||
|  | nsp Remove group category | ||||||
|  | nsp Get group categories | ||||||
|  | nsp Add group role | ||||||
|  | nsp Remove group role | ||||||
|  | nsp Get group roles | ||||||
|  | nsp Add room to group summary | ||||||
|  | nsp Adding room to group summary keeps room_id when fetching rooms in group | ||||||
|  | nsp Adding multiple rooms to group summary have correct order | ||||||
|  | nsp Remove room from group summary | ||||||
|  | nsp Add room to group summary with category | ||||||
|  | nsp Remove room from group summary with category | ||||||
|  | nsp Add user to group summary | ||||||
|  | nsp Adding multiple users to group summary have correct order | ||||||
|  | nsp Remove user from group summary | ||||||
|  | nsp Add user to group summary with role | ||||||
|  | nsp Remove user from group summary with role | ||||||
|  | nsp Local group invites come down sync | ||||||
|  | nsp Group creator sees group in sync | ||||||
|  | nsp Group creator sees group in initial sync | ||||||
|  | nsp Get/set local group publicity | ||||||
|  | nsp Bulk get group publicity | ||||||
|  | nsp Joinability comes down summary | ||||||
|  | nsp Set group joinable and join it | ||||||
|  | nsp Group is not joinable by default | ||||||
|  | nsp Group is joinable over federation | ||||||
|  | nsp Room is transitioned on local and remote groups upon room upgrade | ||||||
|  | 3pd Can bind 3PID via home server | ||||||
|  | 3pd Can bind and unbind 3PID via homeserver | ||||||
|  | 3pd Can unbind 3PID via homeserver when bound out of band | ||||||
|  | 3pd 3PIDs are unbound after account deactivation | ||||||
|  | 3pd Can bind and unbind 3PID via /unbind by specifying the identity server | ||||||
|  | 3pd Can bind and unbind 3PID via /unbind without specifying the identity server | ||||||
|  | app AS can create a user | ||||||
|  | app AS can create a user with an underscore | ||||||
|  | app AS can create a user with inhibit_login | ||||||
|  | app AS cannot create users outside its own namespace | ||||||
|  | app Regular users cannot register within the AS namespace | ||||||
|  | app AS can make room aliases | ||||||
|  | app Regular users cannot create room aliases within the AS namespace | ||||||
|  | app AS-ghosted users can use rooms via AS | ||||||
|  | app AS-ghosted users can use rooms themselves | ||||||
|  | app Ghost user must register before joining room | ||||||
|  | app AS can set avatar for ghosted users | ||||||
|  | app AS can set displayname for ghosted users | ||||||
|  | app AS can't set displayname for random users | ||||||
|  | app Inviting an AS-hosted user asks the AS server | ||||||
|  | app Accesing an AS-hosted room alias asks the AS server | ||||||
|  | app Events in rooms with AS-hosted room aliases are sent to AS server | ||||||
|  | app AS user (not ghost) can join room without registering | ||||||
|  | app AS user (not ghost) can join room without registering, with user_id query param | ||||||
|  | app HS provides query metadata | ||||||
|  | app HS can provide query metadata on a single protocol | ||||||
|  | app HS will proxy request for 3PU mapping | ||||||
|  | app HS will proxy request for 3PL mapping | ||||||
|  | app AS can publish rooms in their own list | ||||||
|  | app AS and main public room lists are separate | ||||||
|  | app AS can deactivate a user | ||||||
|  | psh Test that a message is pushed | ||||||
|  | psh Invites are pushed | ||||||
|  | psh Rooms with names are correctly named in pushed | ||||||
|  | psh Rooms with canonical alias are correctly named in pushed | ||||||
|  | psh Rooms with many users are correctly pushed | ||||||
|  | psh Don't get pushed for rooms you've muted | ||||||
|  | psh Rejected events are not pushed | ||||||
|  | psh Can add global push rule for room | ||||||
|  | psh Can add global push rule for sender | ||||||
|  | psh Can add global push rule for content | ||||||
|  | psh Can add global push rule for override | ||||||
|  | psh Can add global push rule for underride | ||||||
|  | psh Can add global push rule for content | ||||||
|  | psh New rules appear before old rules by default | ||||||
|  | psh Can add global push rule before an existing rule | ||||||
|  | psh Can add global push rule after an existing rule | ||||||
|  | psh Can delete a push rule | ||||||
|  | psh Can disable a push rule | ||||||
|  | psh Adding the same push rule twice is idempotent | ||||||
|  | psh Messages that notify from another user increment unread notification count | ||||||
|  | psh Messages that highlight from another user increment unread highlight count | ||||||
|  | psh Can change the actions of default rules | ||||||
|  | psh Changing the actions of an unknown default rule fails with 404 | ||||||
|  | psh Can change the actions of a user specified rule | ||||||
|  | psh Changing the actions of an unknown rule fails with 404 | ||||||
|  | psh Can fetch a user's pushers | ||||||
|  | psh Push rules come down in an initial /sync | ||||||
|  | psh Adding a push rule wakes up an incremental /sync | ||||||
|  | psh Disabling a push rule wakes up an incremental /sync | ||||||
|  | psh Enabling a push rule wakes up an incremental /sync | ||||||
|  | psh Setting actions for a push rule wakes up an incremental /sync | ||||||
|  | psh Can enable/disable default rules | ||||||
|  | psh Enabling an unknown default rule fails with 404 | ||||||
|  | psh Test that rejected pushers are removed. | ||||||
|  | psh Notifications can be viewed with GET /notifications | ||||||
|  | psh Trying to add push rule with no scope fails with 400 | ||||||
|  | psh Trying to add push rule with invalid scope fails with 400 | ||||||
|  | psh Trying to add push rule with missing template fails with 400 | ||||||
|  | psh Trying to add push rule with missing rule_id fails with 400 | ||||||
|  | psh Trying to add push rule with empty rule_id fails with 400 | ||||||
|  | psh Trying to add push rule with invalid template fails with 400 | ||||||
|  | psh Trying to add push rule with rule_id with slashes fails with 400 | ||||||
|  | psh Trying to add push rule with override rule without conditions fails with 400 | ||||||
|  | psh Trying to add push rule with underride rule without conditions fails with 400 | ||||||
|  | psh Trying to add push rule with condition without kind fails with 400 | ||||||
|  | psh Trying to add push rule with content rule without pattern fails with 400 | ||||||
|  | psh Trying to add push rule with no actions fails with 400 | ||||||
|  | psh Trying to add push rule with invalid action fails with 400 | ||||||
|  | psh Trying to add push rule with invalid attr fails with 400 | ||||||
|  | psh Trying to add push rule with invalid value for enabled fails with 400 | ||||||
|  | psh Trying to get push rules with no trailing slash fails with 400 | ||||||
|  | psh Trying to get push rules with scope without trailing slash fails with 400 | ||||||
|  | psh Trying to get push rules with template without tailing slash fails with 400 | ||||||
|  | psh Trying to get push rules with unknown scope fails with 400 | ||||||
|  | psh Trying to get push rules with unknown template fails with 400 | ||||||
|  | psh Trying to get push rules with unknown attribute fails with 400 | ||||||
|  | psh Trying to get push rules with unknown rule_id fails with 404 | ||||||
|  | v1s GET /initialSync with non-numeric 'limit' | ||||||
|  | v1s GET /events with non-numeric 'limit' | ||||||
|  | v1s GET /events with negative 'limit' | ||||||
|  | v1s GET /events with non-numeric 'timeout' | ||||||
|  | ath Event size limits | ||||||
|  | syn Check creating invalid filters returns 4xx | ||||||
|  | f,pre New federated private chats get full presence information (SYN-115) | ||||||
|  | pre Left room members do not cause problems for presence | ||||||
|  | crm Rooms can be created with an initial invite list (SYN-205) | ||||||
|  | typ Typing notifications don't leak | ||||||
|  | ban Non-present room members cannot ban others | ||||||
|  | psh Getting push rules doesn't corrupt the cache SYN-390 | ||||||
|  | inv Test that we can be reinvited to a room we created | ||||||
|  | syn Multiple calls to /sync should not cause 500 errors | ||||||
|  | gst Guest user can call /events on another world_readable room (SYN-606) | ||||||
|  | gst Real user can call /events on another world_readable room (SYN-606) | ||||||
|  | gst Events come down the correct room | ||||||
|  | pub Asking for a remote rooms list, but supplying the local server's name, returns the local rooms list | ||||||
|  | std Can send a to-device message to two users which both receive it using /sync | ||||||
							
								
								
									
										260
									
								
								sytest/are-we-synapse-yet.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										260
									
								
								sytest/are-we-synapse-yet.py
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,260 @@ | ||||||
|  | #!/usr/bin/env python3 | ||||||
|  | 
 | ||||||
|  | from __future__ import division | ||||||
|  | import argparse | ||||||
|  | import re | ||||||
|  | import sys | ||||||
|  | 
 | ||||||
|  | # Usage: $ ./are-we-synapse-yet.py [-v] results.tap | ||||||
|  | # This script scans a results.tap file from Dendrite's CI process and spits out | ||||||
|  | # a rating of how close we are to Synapse parity, based purely on SyTests. | ||||||
|  | # The main complexity is grouping tests sensibly into features like 'Registration' | ||||||
|  | # and 'Federation'. Then it just checks the ones which are passing and calculates | ||||||
|  | # percentages for each group. Produces results like: | ||||||
|  | # | ||||||
|  | # Client-Server APIs: 29% (196/666 tests) | ||||||
|  | # ------------------- | ||||||
|  | #   Registration             :  62% (20/32 tests) | ||||||
|  | #   Login                    :   7% (1/15 tests) | ||||||
|  | #   V1 CS APIs               :  10% (3/30 tests) | ||||||
|  | #   ... | ||||||
|  | # | ||||||
|  | # or in verbose mode: | ||||||
|  | # | ||||||
|  | # Client-Server APIs: 29% (196/666 tests) | ||||||
|  | # ------------------- | ||||||
|  | #  Registration             :  62% (20/32 tests) | ||||||
|  | #    ✓ GET /register yields a set of flows | ||||||
|  | #    ✓ POST /register can create a user | ||||||
|  | #    ✓ POST /register downcases capitals in usernames | ||||||
|  | #    ... | ||||||
|  | # | ||||||
|  | # You can also tack `-v` on to see exactly which tests each category falls under. | ||||||
|  | 
 | ||||||
|  | test_mappings = { | ||||||
|  |     "nsp": "Non-Spec API", | ||||||
|  |     "f": "Federation", # flag to mark test involves federation | ||||||
|  | 
 | ||||||
|  |     "federation_apis": { | ||||||
|  |         "fky": "Key API", | ||||||
|  |         "fsj": "send_join API", | ||||||
|  |         "fmj": "make_join API", | ||||||
|  |         "fsl": "send_leave API", | ||||||
|  |         "fiv": "Invite API", | ||||||
|  |         "fqu": "Query API", | ||||||
|  |         "frv": "room versions", | ||||||
|  |         "fau": "Auth", | ||||||
|  |         "fbk": "Backfill API", | ||||||
|  |         "fme": "get_missing_events API", | ||||||
|  |         "fst": "State APIs", | ||||||
|  |         "fpb": "Public Room API", | ||||||
|  |         "fdk": "Device Key APIs", | ||||||
|  |         "fed": "Federation API", | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     "client_apis": { | ||||||
|  |         "reg": "Registration", | ||||||
|  |         "log": "Login", | ||||||
|  |         "lox": "Logout", | ||||||
|  |         "v1s": "V1 CS APIs", | ||||||
|  |         "csa": "Misc CS APIs", | ||||||
|  |         "pro": "Profile", | ||||||
|  |         "dev": "Devices", | ||||||
|  |         "dvk": "Device Keys", | ||||||
|  |         "pre": "Presence", | ||||||
|  |         "crm": "Create Room", | ||||||
|  |         "syn": "Sync API", | ||||||
|  |         "rmv": "Room Versions", | ||||||
|  |         "rst": "Room State APIs", | ||||||
|  |         "pub": "Public Room APIs", | ||||||
|  |         "mem": "Room Membership", | ||||||
|  |         "ali": "Room Aliases", | ||||||
|  |         "jon": "Joining Rooms", | ||||||
|  |         "lev": "Leaving Rooms", | ||||||
|  |         "inv": "Inviting users to Rooms", | ||||||
|  |         "ban": "Banning users", | ||||||
|  |         "snd": "Sending events", | ||||||
|  |         "get": "Getting events for Rooms", | ||||||
|  |         "rct": "Receipts", | ||||||
|  |         "red": "Read markers", | ||||||
|  |         "med": "Media APIs", | ||||||
|  |         "cap": "Capabilities API", | ||||||
|  |         "typ": "Typing API", | ||||||
|  |         "psh": "Push APIs", | ||||||
|  |         "acc": "Account APIs", | ||||||
|  |         "eph": "Ephemeral Events", | ||||||
|  |         "plv": "Power Levels", | ||||||
|  |         "xxx": "Redaction", | ||||||
|  |         "3pd": "Third-Party ID APIs", | ||||||
|  |         "gst": "Guest APIs", | ||||||
|  |         "ath": "Room Auth", | ||||||
|  |         "fgt": "Forget APIs", | ||||||
|  |         "ctx": "Context APIs", | ||||||
|  |         "upg": "Room Upgrade APIs", | ||||||
|  |         "tag": "Tagging APIs", | ||||||
|  |         "sch": "Search APIs", | ||||||
|  |         "oid": "OpenID API", | ||||||
|  |         "std": "Send-to-Device APIs", | ||||||
|  |         "adm": "Server Admin API", | ||||||
|  |         "ign": "Ignore Users", | ||||||
|  |         "udr": "User Directory APIs", | ||||||
|  |         "app": "Application Services API", | ||||||
|  |     }, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # optional 'not ' with test number then anything but '#' | ||||||
|  | re_testname = re.compile(r"^(not )?ok [0-9]+ ([^#]+)") | ||||||
|  | 
 | ||||||
|  | # Parses lines like the following: | ||||||
|  | # | ||||||
|  | # SUCCESS:     ok 3 POST /register downcases capitals in usernames | ||||||
|  | # FAIL:        not ok 54 (expected fail) POST /createRoom creates a room with the given version | ||||||
|  | # SKIP:        ok 821 Multiple calls to /sync should not cause 500 errors # skip lack of can_post_room_receipts | ||||||
|  | # EXPECT FAIL: not ok 822 (expected fail) Guest user can call /events on another world_readable room (SYN-606) # TODO expected fail | ||||||
|  | # | ||||||
|  | # Only SUCCESS lines are treated as success, the rest are not implemented. | ||||||
|  | # | ||||||
|  | # Returns a dict like: | ||||||
|  | # { name: "...", ok: True } | ||||||
|  | def parse_test_line(line): | ||||||
|  |     if not line.startswith("ok ") and not line.startswith("not ok "): | ||||||
|  |         return | ||||||
|  |     re_match = re_testname.match(line) | ||||||
|  |     test_name = re_match.groups()[1].replace("(expected fail) ", "").strip() | ||||||
|  |     test_pass = False | ||||||
|  |     if line.startswith("ok ") and not "# skip " in line: | ||||||
|  |         test_pass = True | ||||||
|  |     return { | ||||||
|  |         "name": test_name, | ||||||
|  |         "ok": test_pass, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | # Prints the stats for a complete section. | ||||||
|  | #   header_name => "Client-Server APIs" | ||||||
|  | #   gid_to_tests => { gid: { <name>: True|False }} | ||||||
|  | #   gid_to_name  => { gid: "Group Name" } | ||||||
|  | #   verbose => True|False | ||||||
|  | # Produces: | ||||||
|  | # Client-Server APIs: 29% (196/666 tests) | ||||||
|  | # ------------------- | ||||||
|  | #   Registration             :  62% (20/32 tests) | ||||||
|  | #   Login                    :   7% (1/15 tests) | ||||||
|  | #   V1 CS APIs               :  10% (3/30 tests) | ||||||
|  | #   ... | ||||||
|  | # or in verbose mode: | ||||||
|  | # Client-Server APIs: 29% (196/666 tests) | ||||||
|  | # ------------------- | ||||||
|  | #  Registration             :  62% (20/32 tests) | ||||||
|  | #    ✓ GET /register yields a set of flows | ||||||
|  | #    ✓ POST /register can create a user | ||||||
|  | #    ✓ POST /register downcases capitals in usernames | ||||||
|  | #    ... | ||||||
|  | def print_stats(header_name, gid_to_tests, gid_to_name, verbose): | ||||||
|  |     subsections = [] # Registration: 100% (13/13 tests) | ||||||
|  |     subsection_test_names = {} # 'subsection name': ["✓ Test 1", "✓ Test 2", "× Test 3"] | ||||||
|  |     total_passing = 0 | ||||||
|  |     total_tests = 0 | ||||||
|  |     for gid, tests in gid_to_tests.items(): | ||||||
|  |         group_total = len(tests) | ||||||
|  |         group_passing = 0 | ||||||
|  |         test_names_and_marks = [] | ||||||
|  |         for name, passing in tests.items(): | ||||||
|  |             if passing: | ||||||
|  |                 group_passing += 1 | ||||||
|  |             test_names_and_marks.append(f"{'✓' if passing else '×'} {name}") | ||||||
|  | 
 | ||||||
|  |         total_tests += group_total | ||||||
|  |         total_passing += group_passing | ||||||
|  |         pct = "{0:.0f}%".format(group_passing/group_total * 100) | ||||||
|  |         line = "%s: %s (%d/%d tests)" % (gid_to_name[gid].ljust(25, ' '), pct.rjust(4, ' '), group_passing, group_total) | ||||||
|  |         subsections.append(line) | ||||||
|  |         subsection_test_names[line] = test_names_and_marks | ||||||
|  | 
 | ||||||
|  |     pct = "{0:.0f}%".format(total_passing/total_tests * 100) | ||||||
|  |     print("%s: %s (%d/%d tests)" % (header_name, pct, total_passing, total_tests)) | ||||||
|  |     print("-" * (len(header_name)+1)) | ||||||
|  |     for line in subsections: | ||||||
|  |         print("  %s" % (line,)) | ||||||
|  |         if verbose: | ||||||
|  |             for test_name_and_pass_mark in subsection_test_names[line]: | ||||||
|  |                 print("    %s" % (test_name_and_pass_mark,)) | ||||||
|  |             print("") | ||||||
|  |     print("") | ||||||
|  | 
 | ||||||
|  | def main(results_tap_path, verbose): | ||||||
|  |     # Load up test mappings | ||||||
|  |     test_name_to_group_id = {} | ||||||
|  |     fed_tests = set() | ||||||
|  |     client_tests = set() | ||||||
|  |     groupless_tests = set() | ||||||
|  |     with open("./are-we-synapse-yet.list", "r") as f: | ||||||
|  |         for line in f.readlines(): | ||||||
|  |             test_name = " ".join(line.split(" ")[1:]).strip() | ||||||
|  |             groups = line.split(" ")[0].split(",") | ||||||
|  |             for gid in groups: | ||||||
|  |                 if gid == "f" or gid in test_mappings["federation_apis"]: | ||||||
|  |                     fed_tests.add(test_name) | ||||||
|  |                 else: | ||||||
|  |                     client_tests.add(test_name) | ||||||
|  |                 if gid == "f": | ||||||
|  |                     continue # we expect another group ID | ||||||
|  |                 test_name_to_group_id[test_name] = gid | ||||||
|  | 
 | ||||||
|  |     # parse results.tap | ||||||
|  |     summary = { | ||||||
|  |         "client": { | ||||||
|  |             # gid: { | ||||||
|  |             #   test_name: OK | ||||||
|  |             # } | ||||||
|  |         }, | ||||||
|  |         "federation": { | ||||||
|  |             # gid: { | ||||||
|  |             #   test_name: OK | ||||||
|  |             # } | ||||||
|  |         }, | ||||||
|  |         "nonspec": { | ||||||
|  |             "nsp": {} | ||||||
|  |         }, | ||||||
|  |     } | ||||||
|  |     with open(results_tap_path, "r") as f: | ||||||
|  |         for line in f.readlines(): | ||||||
|  |             test_result = parse_test_line(line) | ||||||
|  |             if not test_result: | ||||||
|  |                 continue | ||||||
|  |             name = test_result["name"] | ||||||
|  |             group_id = test_name_to_group_id.get(name) | ||||||
|  |             if not group_id: | ||||||
|  |                 groupless_tests.add(name) | ||||||
|  |                 # raise Exception("The test '%s' doesn't have a group" % (name,)) | ||||||
|  |             if group_id == "nsp": | ||||||
|  |                 summary["nonspec"]["nsp"][name] = test_result["ok"] | ||||||
|  |             elif group_id in test_mappings["federation_apis"]: | ||||||
|  |                 group = summary["federation"].get(group_id, {}) | ||||||
|  |                 group[name] = test_result["ok"] | ||||||
|  |                 summary["federation"][group_id] = group | ||||||
|  |             elif group_id in test_mappings["client_apis"]: | ||||||
|  |                 group = summary["client"].get(group_id, {}) | ||||||
|  |                 group[name] = test_result["ok"] | ||||||
|  |                 summary["client"][group_id] = group | ||||||
|  | 
 | ||||||
|  |     print("Are We Synapse Yet?") | ||||||
|  |     print("===================") | ||||||
|  |     print("") | ||||||
|  |     print_stats("Non-Spec APIs", summary["nonspec"], test_mappings, verbose) | ||||||
|  |     print_stats("Client-Server APIs", summary["client"], test_mappings["client_apis"], verbose) | ||||||
|  |     print_stats("Federation APIs", summary["federation"], test_mappings["federation_apis"], verbose) | ||||||
|  |     if verbose: | ||||||
|  |         print("The following tests don't have a group:") | ||||||
|  |         for name in groupless_tests: | ||||||
|  |             print("  %s" % (name,)) | ||||||
|  |     else: | ||||||
|  |         print("%d tests don't have a group" % len(groupless_tests)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     parser = argparse.ArgumentParser() | ||||||
|  |     parser.add_argument("tap_file", help="path to results.tap") | ||||||
|  |     parser.add_argument("-v", action="store_true", help="show individual test names in output") | ||||||
|  |     args = parser.parse_args() | ||||||
|  |     main(args.tap_file, args.v) | ||||||
							
								
								
									
										105
									
								
								sytest/show-expected-fail-tests.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										105
									
								
								sytest/show-expected-fail-tests.sh
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,105 @@ | ||||||
|  | #! /bin/bash | ||||||
|  | # | ||||||
|  | # Parses a results.tap file from SyTest output and a file containing test names (a test whitelist) | ||||||
|  | # and checks whether a test name that exists in the whitelist (that should pass), failed or not. | ||||||
|  | # | ||||||
|  | # An optional blacklist file can be added, also containing test names, where if a test name is | ||||||
|  | # present, the script will not error even if the test is in the whitelist file and failed | ||||||
|  | # | ||||||
|  | # For each of these files, lines starting with '#' are ignored. | ||||||
|  | # | ||||||
|  | # Usage ./show-expected-fail-tests.sh results.tap whitelist [blacklist] | ||||||
|  | 
 | ||||||
|  | results_file=$1 | ||||||
|  | whitelist_file=$2 | ||||||
|  | blacklist_file=$3 | ||||||
|  | 
 | ||||||
|  | fail_build=0 | ||||||
|  | 
 | ||||||
|  | if [ $# -lt 2 ]; then | ||||||
|  | 	echo "Usage: $0 results.tap whitelist [blacklist]" | ||||||
|  | 	exit 1 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | if [ ! -f "$results_file" ]; then | ||||||
|  | 	echo "ERROR: Specified results file '${results_file}' doesn't exist." | ||||||
|  | 	fail_build=1 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | if [ ! -f "$whitelist_file" ]; then | ||||||
|  | 	echo "ERROR: Specified test whitelist '${whitelist_file}' doesn't exist." | ||||||
|  | 	fail_build=1 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | blacklisted_tests=() | ||||||
|  | 
 | ||||||
|  | # Check if a blacklist file was provided | ||||||
|  | if [ $# -eq 3 ]; then | ||||||
|  | 	# Read test blacklist file | ||||||
|  | 	if [ ! -f "$blacklist_file" ]; then | ||||||
|  | 		echo "ERROR: Specified test blacklist file '${blacklist_file}' doesn't exist." | ||||||
|  | 		fail_build=1 | ||||||
|  | 	fi | ||||||
|  | 
 | ||||||
|  | 	# Read each line, ignoring those that start with '#' | ||||||
|  | 	blacklisted_tests="" | ||||||
|  | 	search_non_comments=$(grep -v '^#' ${blacklist_file}) | ||||||
|  | 	while read -r line ; do | ||||||
|  | 		# Record the blacklisted test name | ||||||
|  | 		blacklisted_tests+=("${line}") | ||||||
|  | 	done <<< "${search_non_comments}"  # This allows us to edit blacklisted_tests in the while loop | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | [ "$fail_build" = 0 ] || exit 1 | ||||||
|  | 
 | ||||||
|  | passed_but_expected_fail=$(grep ' # TODO passed but expected fail' ${results_file} | sed -E 's/^ok [0-9]+ (\(expected fail\) )?//' | sed -E 's/( \([0-9]+ subtests\))? # TODO passed but expected fail$//') | ||||||
|  | tests_to_add="" | ||||||
|  | already_in_whitelist="" | ||||||
|  | 
 | ||||||
|  | while read -r test_name; do | ||||||
|  | 	# Ignore empty lines | ||||||
|  | 	[ "${test_name}" = "" ] && continue | ||||||
|  | 
 | ||||||
|  | 	grep "^${test_name}" "${whitelist_file}" > /dev/null 2>&1 | ||||||
|  | 	if [ "$?" != "0" ]; then | ||||||
|  | 		# Check if this test name is blacklisted | ||||||
|  | 		if printf '%s\n' "${blacklisted_tests[@]}" | grep -q -P "^${test_name}$"; then | ||||||
|  | 			# Don't notify about this test | ||||||
|  | 			continue | ||||||
|  | 		fi | ||||||
|  | 
 | ||||||
|  | 		# Append this test_name to the existing list | ||||||
|  | 		tests_to_add="${tests_to_add}${test_name}\n" | ||||||
|  | 		fail_build=1 | ||||||
|  | 	else | ||||||
|  | 		already_in_whitelist="${already_in_whitelist}${test_name}\n" | ||||||
|  | 	fi | ||||||
|  | done <<< "${passed_but_expected_fail}" | ||||||
|  | 
 | ||||||
|  | # TODO: Check that the same test doesn't exist in both the whitelist and blacklist | ||||||
|  | # TODO: Check that the same test doesn't appear twice in the whitelist|blacklist | ||||||
|  | 
 | ||||||
|  | # Trim test output strings | ||||||
|  | tests_to_add=$(IFS=$'\n' echo "${tests_to_add[*]%%'\n'}") | ||||||
|  | already_in_whitelist=$(IFS=$'\n' echo "${already_in_whitelist[*]%%'\n'}") | ||||||
|  | 
 | ||||||
|  | # Format output with markdown for buildkite annotation rendering purposes | ||||||
|  | if [ -n "${tests_to_add}" ] && [ -n "${already_in_whitelist}" ]; then | ||||||
|  | 	echo "### 📜 SyTest Whitelist Maintenance" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | if [ -n "${tests_to_add}" ]; then | ||||||
|  | 	echo "**ERROR**: The following tests passed but are not present in \`$2\`. Please append them to the file:" | ||||||
|  |     echo "\`\`\`" | ||||||
|  |     echo -e "${tests_to_add}" | ||||||
|  |     echo "\`\`\`" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | if [ -n "${already_in_whitelist}" ]; then | ||||||
|  | 	echo "**WARN**: Tests in the whitelist still marked as **expected fail**:" | ||||||
|  |     echo "\`\`\`" | ||||||
|  |     echo -e "${already_in_whitelist}" | ||||||
|  |     echo "\`\`\`" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | exit ${fail_build} | ||||||
							
								
								
									
										0
									
								
								sytest/sytest-blacklist
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								sytest/sytest-blacklist
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										85
									
								
								sytest/sytest-whitelist
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								sytest/sytest-whitelist
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,85 @@ | ||||||
|  | # Register endpoints implemented | ||||||
|  | GET /register yields a set of flows | ||||||
|  | POST /register can create a user | ||||||
|  | POST /register downcases capitals in usernames | ||||||
|  | POST /register rejects registration of usernames with '!' | ||||||
|  | POST /register rejects registration of usernames with '"' | ||||||
|  | POST /register rejects registration of usernames with ':' | ||||||
|  | POST /register rejects registration of usernames with '?' | ||||||
|  | POST /register rejects registration of usernames with '\' | ||||||
|  | POST /register rejects registration of usernames with '@' | ||||||
|  | POST /register rejects registration of usernames with '[' | ||||||
|  | POST /register rejects registration of usernames with ']' | ||||||
|  | POST /register rejects registration of usernames with '{' | ||||||
|  | POST /register rejects registration of usernames with '|' | ||||||
|  | POST /register rejects registration of usernames with '}' | ||||||
|  | POST /register rejects registration of usernames with '£' | ||||||
|  | POST /register rejects registration of usernames with 'é' | ||||||
|  | POST /register rejects registration of usernames with '\n' | ||||||
|  | POST /register rejects registration of usernames with ''' | ||||||
|  | # Login endpoints implemented | ||||||
|  | GET /login yields a set of flows | ||||||
|  | POST /login can log in as a user | ||||||
|  | POST /login returns the same device_id as that in the request | ||||||
|  | POST /login can log in as a user with just the local part of the id | ||||||
|  | POST /login as non-existing user is rejected | ||||||
|  | POST /login wrong password is rejected | ||||||
|  | # Room creation endpoints implemented | ||||||
|  | POST /createRoom makes a public room | ||||||
|  | POST /createRoom makes a private room | ||||||
|  | POST /createRoom makes a private room with invites | ||||||
|  | POST /createRoom makes a room with a name | ||||||
|  | POST /createRoom makes a room with a topic | ||||||
|  | Can /sync newly created room | ||||||
|  | GET /rooms/:room_id/state/m.room.member/:user_id fetches my membership | ||||||
|  | GET /rooms/:room_id/state/m.room.power_levels fetches powerlevels | ||||||
|  | POST /join/:room_alias can join a room | ||||||
|  | POST /join/:room_id can join a room | ||||||
|  | POST /join/:room_id can join a room with custom content | ||||||
|  | POST /join/:room_alias can join a room with custom content | ||||||
|  | POST /rooms/:room_id/join can join a room | ||||||
|  | POST /rooms/:room_id/leave can leave a room | ||||||
|  | POST /rooms/:room_id/invite can send an invite | ||||||
|  | POST /rooms/:room_id/ban can ban a user | ||||||
|  | POST /rooms/:room_id/send/:event_type sends a message | ||||||
|  | PUT /rooms/:room_id/send/:event_type/:txn_id sends a message | ||||||
|  | PUT /rooms/:room_id/send/:event_type/:txn_id deduplicates the same txn id | ||||||
|  | GET /rooms/:room_id/state/m.room.power_levels can fetch levels | ||||||
|  | PUT /rooms/:room_id/state/m.room.power_levels can set levels | ||||||
|  | PUT power_levels should not explode if the old power levels were empty | ||||||
|  | Both GET and PUT work | ||||||
|  | POST /rooms/:room_id/read_markers can create read marker | ||||||
|  | User signups are forbidden from starting with '_' | ||||||
|  | Request to logout with invalid an access token is rejected | ||||||
|  | Request to logout without an access token is rejected | ||||||
|  | Room creation reports m.room.create to myself | ||||||
|  | Room creation reports m.room.member to myself | ||||||
|  | Version responds 200 OK with valid structure | ||||||
|  | PUT /profile/:user_id/displayname sets my name | ||||||
|  | GET /profile/:user_id/displayname publicly accessible | ||||||
|  | GET /device/{deviceId} gives a 404 for unknown devices | ||||||
|  | PUT /device/{deviceId} gives a 404 for unknown devices | ||||||
|  | After deactivating account, can't log in with an email | ||||||
|  | Can create filter | ||||||
|  | Should reject keys claiming to belong to a different user | ||||||
|  | Can add account data | ||||||
|  | Checking local federation server | ||||||
|  | Alternative server names do not cause a routing loop | ||||||
|  | Can read configuration endpoint | ||||||
|  | AS cannot create users outside its own namespace | ||||||
|  | Changing the actions of an unknown default rule fails with 404 | ||||||
|  | Changing the actions of an unknown rule fails with 404 | ||||||
|  | Trying to add push rule with invalid scope fails with 400 | ||||||
|  | Trying to add push rule with invalid template fails with 400 | ||||||
|  | Trying to add push rule with rule_id with slashes fails with 400 | ||||||
|  | Trying to add push rule with override rule without conditions fails with 400 | ||||||
|  | Trying to add push rule with underride rule without conditions fails with 400 | ||||||
|  | Trying to add push rule with condition without kind fails with 400 | ||||||
|  | Trying to add push rule with content rule without pattern fails with 400 | ||||||
|  | Trying to add push rule with no actions fails with 400 | ||||||
|  | Trying to add push rule with invalid action fails with 400 | ||||||
|  | Trying to get push rules with unknown rule_id fails with 404 | ||||||
|  | GET /events with non-numeric 'limit' | ||||||
|  | GET /events with negative 'limit' | ||||||
|  | GET /events with non-numeric 'timeout' | ||||||
|  | Getting push rules doesn't corrupt the cache SYN-390 | ||||||
		Loading…
	
		Reference in a new issue