@@ -324,6 +324,141 @@ def test_needs_to_be_in_room(self) -> None:
324324 self .assertEqual (channel .json_body ["errcode" ], "M_FORBIDDEN" )
325325
326326
327+ class UnstableGetExtremitiesTests (unittest .FederatingHomeserverTestCase ):
328+ servlets = [
329+ admin .register_servlets ,
330+ room .register_servlets ,
331+ login .register_servlets ,
332+ ]
333+
334+ def prepare (self , reactor : MemoryReactor , clock : Clock , hs : HomeServer ) -> None :
335+ super ().prepare (reactor , clock , hs )
336+ self ._storage_controllers = hs .get_storage_controllers ()
337+
338+ def _make_endpoint_path (self , room_id : str ) -> str :
339+ return f"/_matrix/federation/unstable/org.matrix.msc4370/extremities/{ room_id } "
340+
341+ def _remote_join (self , room_id : str , room_version : str ) -> str :
342+ # Note: other tests ensure the called endpoints in this function return useful
343+ # and proper data.
344+
345+ # make_join first
346+ joining_user = "@misspiggy:" + self .OTHER_SERVER_NAME
347+ channel = self .make_signed_federation_request (
348+ "GET" ,
349+ f"/_matrix/federation/v1/make_join/{ room_id } /{ joining_user } ?ver={ room_version } " ,
350+ )
351+ self .assertEqual (channel .code , HTTPStatus .OK , channel .json_body )
352+ join_result = channel .json_body
353+
354+ # Sign/populate the join
355+ join_event_dict = join_result ["event" ]
356+ self .add_hashes_and_signatures_from_other_server (
357+ join_event_dict ,
358+ KNOWN_ROOM_VERSIONS [room_version ],
359+ )
360+ if room_version in ["1" , "2" ]:
361+ add_hashes_and_signatures (
362+ KNOWN_ROOM_VERSIONS [room_version ],
363+ join_event_dict ,
364+ signature_name = self .hs .hostname ,
365+ signing_key = self .hs .signing_key ,
366+ )
367+
368+ # Send the join
369+ channel = self .make_signed_federation_request (
370+ "PUT" ,
371+ f"/_matrix/federation/v2/send_join/{ room_id } /x" ,
372+ content = join_event_dict ,
373+ )
374+
375+ # Check that things went okay so the test doesn't become a total train wreck
376+ self .assertEqual (channel .code , HTTPStatus .OK , channel .json_body )
377+ r = self .get_success (self ._storage_controllers .state .get_current_state (room_id ))
378+ self .assertEqual (r [("m.room.member" , joining_user )].membership , "join" )
379+
380+ return r [("m.room.member" , joining_user )].event_id
381+
382+ def _test_get_extremities_common (self , room_version : str ) -> None :
383+ # Create a room to test with
384+ creator_user_id = self .register_user ("kermit" , "test" )
385+ tok = self .login ("kermit" , "test" )
386+ room_id = self .helper .create_room_as (
387+ room_creator = creator_user_id ,
388+ tok = tok ,
389+ room_version = room_version ,
390+ extra_content = {
391+ # Public preset uses `shared` history visibility, but makes joins
392+ # easier in our tests.
393+ # https://spec.matrix.org/v1.16/client-server-api/#post_matrixclientv3createroom
394+ "preset" : "public_chat"
395+ },
396+ )
397+
398+ # At this stage we should fail to get the extremities because we're not joined
399+ # and therefore can't see the events (`shared` history visibility).
400+ channel = self .make_signed_federation_request (
401+ "GET" , self ._make_endpoint_path (room_id )
402+ )
403+ self .assertEqual (channel .code , HTTPStatus .FORBIDDEN , channel .json_body )
404+ self .assertEqual (channel .json_body ["error" ], "Host not in room." )
405+ self .assertEqual (channel .json_body ["errcode" ], "M_FORBIDDEN" )
406+
407+ # Now join the room and try again
408+ # Note: there should be just one extremity: the join
409+ join_event_id = self ._remote_join (room_id , room_version )
410+ channel = self .make_signed_federation_request (
411+ "GET" , self ._make_endpoint_path (room_id )
412+ )
413+ self .assertEqual (channel .code , HTTPStatus .OK , channel .json_body )
414+ self .assertEqual (channel .json_body ["prev_events" ], [join_event_id ])
415+
416+ # ACL the calling server and try again. This should cause an error getting extremities.
417+ self .helper .send_state (
418+ room_id ,
419+ "m.room.server_acl" ,
420+ {
421+ "allow" : ["*" ],
422+ "allow_ip_literals" : False ,
423+ "deny" : [self .OTHER_SERVER_NAME ],
424+ },
425+ tok = tok ,
426+ expect_code = HTTPStatus .OK ,
427+ )
428+ channel = self .make_signed_federation_request (
429+ "GET" , self ._make_endpoint_path (room_id )
430+ )
431+ self .assertEqual (channel .code , HTTPStatus .FORBIDDEN , channel .json_body )
432+ self .assertEqual (channel .json_body ["error" ], "Server is banned from room" )
433+ self .assertEqual (channel .json_body ["errcode" ], "M_FORBIDDEN" )
434+
435+ @parameterized .expand ([(k ,) for k in KNOWN_ROOM_VERSIONS .keys ()])
436+ @override_config (
437+ {"use_frozen_dicts" : True , "experimental_features" : {"msc4370_enabled" : True }}
438+ )
439+ def test_get_extremities_with_frozen_dicts (self , room_version : str ) -> None :
440+ """Test GET /extremities with USE_FROZEN_DICTS=True"""
441+ self ._test_get_extremities_common (room_version )
442+
443+ @parameterized .expand ([(k ,) for k in KNOWN_ROOM_VERSIONS .keys ()])
444+ @override_config (
445+ {"use_frozen_dicts" : False , "experimental_features" : {"msc4370_enabled" : True }}
446+ )
447+ def test_get_extremities_without_frozen_dicts (self , room_version : str ) -> None :
448+ """Test GET /extremities with USE_FROZEN_DICTS=False"""
449+ self ._test_get_extremities_common (room_version )
450+
451+ # note the lack of config-setting stuff on this test.
452+ def test_get_extremities_unstable_not_enabled (self ) -> None :
453+ """Test that GET /extremities returns M_UNRECOGNIZED when MSC4370 is not enabled"""
454+ # We shouldn't even have to create a room - the endpoint should just fail.
455+ channel = self .make_signed_federation_request (
456+ "GET" , self ._make_endpoint_path ("!room:example.org" )
457+ )
458+ self .assertEqual (channel .code , HTTPStatus .NOT_FOUND , channel .json_body )
459+ self .assertEqual (channel .json_body ["errcode" ], "M_UNRECOGNIZED" )
460+
461+
327462class SendJoinFederationTests (unittest .FederatingHomeserverTestCase ):
328463 servlets = [
329464 admin .register_servlets ,
0 commit comments