From 819a3304f46424c59698e94d1517bc93e3e0be57 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Sat, 12 Mar 2022 16:32:24 +0000 Subject: [PATCH] Outlier/rejected tests for `/state` and `/state_ids` Extend the tests for `/state` and `/state_ids` to check the behaviour on outliers and rejected events. The rejected-event behaviour has always been to return a 404, but we add a test to make sure it stays that way. The outlier behaviour is changed by https://github.com/matrix-org/synapse/pull/12087. --- tests/50federation/00prepare.pl | 131 +++++++++++++++++++++++++++++++- tests/50federation/36state.pl | 80 ++++++++++++++++++- 2 files changed, 208 insertions(+), 3 deletions(-) diff --git a/tests/50federation/00prepare.pl b/tests/50federation/00prepare.pl index 986d70991..b745207bb 100644 --- a/tests/50federation/00prepare.pl +++ b/tests/50federation/00prepare.pl @@ -136,7 +136,7 @@ =head2 send_and_await_event I<$room> should be the L in which to send the event. -I<$user> should be a L which will poll for receiving the event. +I<$server_user> should be a L which will poll for receiving the event. The remainder of the arguments (I<%fields>) are passed into L. They should include at @@ -169,6 +169,135 @@ sub send_and_await_event { push @EXPORT, qw( send_and_await_event ); +=head2 send_and_await_outlier + + send_and_await_outlier( + $inbound_server, $outbound_client, $room, $sending_user_id, $receiving_user, + )->then( sub { + my ( $outlier_event ) = @_; + }); + +Arranges for an outlier event to be sent over federation. + +I<$inbound_server> should be a L, likely +I<$main::INBOUND_SERVER>, which will handle the incoming federation requests +involved. + +I<$outbound_client> should be a L, most likely +I<$main::OUTBOUND_CLIENT>, which is used to send the event. + +I<$room> should be the L in which to send the event. + +I<$sending_user_id> is a user on the Sytest federation server, and should be a +member of the room. + +I<$receiving_user> should be a L on the server under test, which will +poll for receiving the event. Must also be a member of the room. + +The created outlier is returned. + +=cut + +sub send_and_await_outlier { + my ( $inbound_server, $outbound_client, $room, $sending_user_id, $receiving_user ) = @_; + + # to construct an outlier, we create three events, Q, R, S. + # + # We send S over federation, and allow the server to backfill R, leaving + # the server with a gap in the dag. It therefore requests the state at Q, + # which leads to Q being persisted as an outlier. + + my $first_home_server = $receiving_user->server_name; + my $room_id = $room->room_id; + my %initial_room_state = %{ $room->{current_state} }; + + my ( $outlier_event_Q, $outlier_event_id_Q ) = $room->create_and_insert_event( + type => 'm.room.member', + sender => $sending_user_id, + state_key => $sending_user_id, + content => { membership => 'join' }, + auth_events => $room->make_event_refs( + $room->get_current_state_event( "m.room.create" ), + $room->get_current_state_event( "m.room.power_levels" ), + $room->get_current_state_event( "m.room.member", $sending_user_id ), + ), + ); + + my ( $backfilled_event_R, $backfilled_event_id_R ) = $room->create_and_insert_event( + type => "m.room.message", + sender => $sending_user_id, + content => { body => "backfilled event R" }, + ); + + my ( $sent_event_S, $sent_event_id_S ) = $room->create_and_insert_event( + type => "m.room.message", + sender => $sending_user_id, + content => { body => "sent event S" }, + ); + + log_if_fail "create_outlier_event: events Q, R, S", [ $outlier_event_id_Q, $backfilled_event_id_R, $sent_event_id_S ]; + + Future->needs_all( + # send S + $outbound_client->send_event( + event => $sent_event_S, + destination => $first_home_server, + ), + + # we expect to get a missing_events request + $inbound_server->await_request_get_missing_events( $room_id ) + ->then( sub { + my ( $req ) = @_; + my $body = $req->body_from_json; + log_if_fail "create_outlier_event: /get_missing_events request", $body; + + assert_deeply_eq( + $body->{latest_events}, + [ $sent_event_id_S ], + "create_outlier_event: latest_events in /get_missing_events request", + ); + + # just return R + my $resp = { events => [ $backfilled_event_R ] }; + + log_if_fail "create_outlier_event: /get_missing_events response", $resp; + $req->respond_json( $resp ); + Future->done(1); + }), + + # there will still be a gap, so then we expect a state_ids request + $inbound_server->await_request_state_ids( + $room_id, $outlier_event_id_Q, + )->then( sub { + my ( $req, @params ) = @_; + log_if_fail "create_outlier_event: /state_ids request", \@params; + + my $resp = { + pdu_ids => [ + map { $room->id_for_event( $_ ) } values( %initial_room_state ), + ], + auth_chain_ids => $room->event_ids_from_refs( $outlier_event_Q->{auth_events} ), + }; + + log_if_fail "create_outlier_event: /state_ids response", $resp; + $req->respond_json( $resp ); + Future->done(1); + }), + )->then( sub { + # wait for S to turn up in /sync + await_sync_timeline_contains( + $receiving_user, $room_id, check => sub { + my ( $event ) = @_; + log_if_fail "create_outlier_event: Got event", $event; + my $event_id = $event->{event_id}; + return $event_id eq $sent_event_id_S; + }, + ); + })->then_done( $outlier_event_Q, $backfilled_event_R, $sent_event_S ); +} +push @EXPORT, qw( send_and_await_outlier ); + + my $next_user_id = 0; =head2 federation_user_id_fixture diff --git a/tests/50federation/36state.pl b/tests/50federation/36state.pl index 6d2d9adad..8e5337b72 100644 --- a/tests/50federation/36state.pl +++ b/tests/50federation/36state.pl @@ -243,8 +243,84 @@ sub get_state_ids_from_server { }; +foreach my $endpoint( qw( state state_ids ) ) { + test "/$endpoint returns M_NOT_FOUND for an outlier", + requires => [ + $main::INBOUND_SERVER, + $main::OUTBOUND_CLIENT, + federated_rooms_fixture(), + ], + + do => sub { + my ( $inbound_server, $outbound_client, $creator, $user_id, $room ) = @_; + + send_and_await_outlier( + $inbound_server, $outbound_client, $room, $user_id, $creator, + )->then( sub { + my ( $outlier_event ) = @_; + my $event_id = $room->id_for_event( $outlier_event ); + + my $uri = "/v1/$endpoint/".$room->room_id; + log_if_fail "Making request /_matrix/federation/$uri for outlier event $event_id"; + + $outbound_client->do_request_json( + method => "GET", + hostname => $creator->server_name, + uri => "/v1/state/".$room->room_id, + params => { event_id => $event_id }, + ); + })->main::expect_m_not_found(); + }; +} + foreach my $type ( qw( message state ) ) { - test "Room state at a rejected $type event is the same as its predecessor", + foreach my $endpoint( qw( state state_ids ) ) { + test "/$endpoint returns M_NOT_FOUND for a rejected $type event", + requires => [ + $main::OUTBOUND_CLIENT, + federated_rooms_fixture(), + ], + + do => sub { + my ( $outbound_client, $creator_user, $sytest_user_id, $room ) = @_; + my $first_home_server = $creator_user->server_name; + + # we send an event which will be rejected (due to having a sender not in the room) + my ( $rejected_event, $rejected_event_id ) = $room->create_and_insert_event( + type => "m.room.$type", + $type eq 'state' ? ( state_key => "" ) : (), + + sender => '@fake_sender:' . $outbound_client->server_name, + content => { body => "Rejected" }, + auth_events => $room->make_event_refs( + $room->get_current_state_event( "m.room.create" ), + $room->get_current_state_event( "m.room.power_levels" ), + ), + ); + + log_if_fail "sending rejected event $rejected_event_id", $rejected_event; + + $outbound_client->send_event( + event => $rejected_event, + destination => $first_home_server, + )->then( sub { + # follow up with a regular event, to make sure the rejected event got through + send_and_await_event( $outbound_client, $room, $creator_user, sender => $sytest_user_id ); + })->then( sub { + # now request the state at the rejected event + my $uri = "/v1/$endpoint/".$room->room_id; + log_if_fail "Making request /_matrix/federation/$uri for event $rejected_event_id"; + $outbound_client->do_request_json( + method => "GET", + hostname => $first_home_server, + uri => $uri, + params => { event_id => $rejected_event_id }, + ); + })->main::expect_m_not_found(); + }; + } + + test "Room state after a rejected $type event is the same as before", requires => [ $main::OUTBOUND_CLIENT, federated_rooms_fixture(), @@ -309,7 +385,7 @@ sub get_state_ids_from_server { ); })->then( sub { my ( $body ) = @_; - log_if_fail "state_ids response", $body; + log_if_fail "state_ids response after regular event $regular_event_id", $body; my @sorted_state = sort @{ $body->{pdu_ids} }; assert_deeply_eq( \@sorted_state, \@initial_state_events );