Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempt to deflake Device list doesn't change if remote server is down #1142

Merged
merged 3 commits into from
Sep 16, 2021
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 58 additions & 11 deletions tests/50federation/40devicelists.pl
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,6 @@
});
};

use Data::Dumper;
test "Device list doesn't change if remote server is down",
# see https://github.com/matrix-org/synapse/issues/5441

Expand Down Expand Up @@ -563,19 +562,36 @@
Future->done(1)
})
);

# First we succesfully request the remote user's keys while the remote server is up.
my $room_id;
# First we successfully request the remote user's keys while the remote server is up.
# We do this once they share a room.
matrix_create_room(
matrix_create_room_synced(
$local_user,
preset => "public_chat",
)->then( sub {
my ( $room_id ) = @_;
( $room_id ) = @_;
$outbound_client->join_room(
server_name => $local_user->server_name,
room_id => $room_id,
user_id => $outbound_client_user,
)
# Before making the key request, we need to ensure the server under test
# recognises that $local_user and $outbound_client_user now share a room.
})->then( sub {
await_sync_timeline_contains(
$local_user,
$room_id,
check => sub {
my ($event) = @_;
return $event->{type} eq "m.room.member" &&
$event->{state_key} eq $outbound_client_user &&
$event->{content}{membership} eq "join";
}
);
})->then( sub {
# Don't expect this sync to do anything, but it sets a next_batch_token
# on the local_user object, which is required by sync_until_user_in_device_list
matrix_sync( $local_user )
})->then( sub {
do_request_json_for( $local_user,
method => "POST",
Expand All @@ -588,9 +604,13 @@
)
})->then( sub {
( $first_keys_query_body ) = @_;
log_if_fail "first_keys_query_body", $first_keys_query_body;
assert_eq(
$first_keys_query_body->{device_keys}->{$outbound_client_user}->{CURIOSITY_ROVER}->{user_id},
$outbound_client_user,
"outbound client user mxid"
);
map {$_->cancel} @respond_with_keys;
log_if_fail (Dumper $first_keys_query_body);

# We take the remote server 'offline' and then make the same request
# for the users keys. We expect no change in the keys. We respond with
# 400 instead of 503 to avoid the server being marked as down.
Expand All @@ -608,6 +628,19 @@
Future->done(1)
})
);
})->then( sub {
# Synapse with workers would often fail this test, because
# - the device list info retrieved over federation was written to DB
# by the master worker
# - the master worker notifies the others over replication, so the
# workers can invalidate their caches
# - the second request (that we're about to send out) would sometimes
# arrive at the stream_writer worker before it'd been notified over
# replication
#
# Sync so that we have a chance for the replication to propagate.
sync_until_user_in_device_list($local_user, new_User(user_id=>$outbound_client_user, http=>"", server_name=>""));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sync_until_user_in_device_list($local_user, new_User(user_id=>$outbound_client_user, http=>"", server_name=>""));
sync_until_user_in_device_list( $local_user,
new_User( user_id => $outbound_client_user, http => "", server_name => "" )
);

I wonder if instead we should change sync_until_user_in_device_list to accept a user ID as the second param? Probably a lot nicer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be tempted to make it accept either a User struct or a user ID, if only because I don't want to touch all of the other call sites. I'm not sure if there's a good perl idiom for that?

})->then( sub {
do_request_json_for( $local_user,
method => "POST",
uri => "/r0/keys/query",
Expand All @@ -616,20 +649,34 @@
$outbound_client_user => []
}
}
)
)->then( sub {
my ( $body ) = @_;
log_if_fail "Attempted second request", $body;
# Failed requests have an empty device_list and a nonempty failures list.
assert_json_keys( $body, "device_keys" );
assert_json_object( $body->{device_keys} );
assert_json_keys( $body->{device_keys}, $outbound_client_user );
my $size = scalar keys %{ $body->{device_keys} };
$size eq 1 or die "Expected device_keys to contain exactly one entry, not " . $size;

# Failed requests have an empty device_list and a nonempty failures list.
assert_json_keys( $body, "failures" );
$size = scalar keys %{ $body->{failures} };
$size eq 0 or die "Expected failures to be empty, not of size " . $size;
Future->done( $body );
})
})->then( sub {
( $second_keys_query_body ) = @_;
map {$_->cancel} @respond_400;
# The unsiged field is optional in the spec so we remove it from any response.
# The unsigned field is optional in the spec so we remove it from any response.
foreach ($first_keys_query_body, $second_keys_query_body) {
while (my ($user_key, $devices) = each %{$_->{ device_keys }} ) {
while (my ($device_key, $values) = each %$devices) {
delete $values->{ unsigned };
}
}
};

log_if_fail (Dumper $second_keys_query_body);
log_if_fail "second_keys_query_body",$second_keys_query_body;
assert_deeply_eq( $second_keys_query_body->{ device_keys }, $first_keys_query_body->{ device_keys }, "Query matches while federation server is down." );
Future->done(1)
})
Expand Down