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

Add a passing test for recovery from gappy device list updates #761

Merged
merged 3 commits into from
Jan 27, 2020
Merged
Changes from all 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
212 changes: 202 additions & 10 deletions tests/50federation/40devicelists.pl
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,17 @@

assert_eq( $req->method, "GET", 'request method' );

$req->respond_json( {
user_id => $creator_id,
stream_id => 1,
devices => [ {
device_id => $device_id,
$req->respond_json( {
user_id => $creator_id,
stream_id => 1,
devices => [ {
device_id => $device_id,

keys => {
keys => {
device_keys => {}
}
} ]
} );

} ]
} );
Future->done(1);
}),
$outbound_client->send_edu(
Expand Down Expand Up @@ -287,6 +286,8 @@

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

requires => [
$main::OUTBOUND_CLIENT,
$main::INBOUND_SERVER,
Expand Down Expand Up @@ -362,7 +363,7 @@
})
);

# First we succesfully request the remote users keys while the remote server is up.
# First we succesfully request the remote user's keys while the remote server is up.
# We do this once they share a room.
matrix_create_room(
$local_user,
Expand Down Expand Up @@ -431,3 +432,194 @@
Future->done(1)
})
};

# for https://github.com/matrix-org/synapse/issues/4827
use Data::Dumper;
test "If a device list update goes missing, the server resyncs on the next one",
requires => [ local_user_fixture(),
$main::INBOUND_SERVER, $main::OUTBOUND_CLIENT,
federation_user_id_fixture(),
room_alias_name_fixture() ],

check => sub {
my ( $user, $inbound_server, $outbound_client, $creator_id, $room_alias_name ) = @_;

my ( $room_id );

my $local_server_name = $user->server_name;

my $remote_server_name = $inbound_server->server_name;
my $datastore = $inbound_server->datastore;

my $room_alias = "#$room_alias_name:$remote_server_name";

my $device_id = "random_device_id";

my $prev_stream_id;

my $room = $datastore->create_room(
creator => $creator_id,
alias => $room_alias,
);

my $client_keys = {
user_id => $creator_id,
device_id => $device_id,
algorithms => ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
# start off with no keys
keys => {
},
signatures => {
},
};

my $client_user_devices = {
user_id => $creator_id,
stream_id => 1,
devices => [{
device_id => $device_id,
keys => $client_keys,
device_display_name => "Original name"
}],
};

my $client_user_keys = {
device_keys => {
$creator_id => {
$device_id => $client_keys
},
},
};

do_request_json_for( $user,
method => "POST",
uri => "/r0/join/$room_alias",

content => {},
)->then( sub {
Future->needs_all(
$inbound_server->await_request_user_devices( $creator_id )
->then( sub {
my ( $req, undef ) = @_;

assert_eq( $req->method, "GET", 'request method' );

$req->respond_json( $client_user_devices );

Future->done(1);
}),
$outbound_client->send_edu(
edu_type => "m.device_list_update",
destination => $local_server_name,
content => {
user_id => $creator_id,
device_id => $device_id,
stream_id => 1,

keys => {
device_keys => $client_user_keys,
}
}
)
)
})->then( sub {
# in stream_id 2, we add keys to the device but deliberately don't
# tell the remote server about it.

Future->needs_all(
$inbound_server->await_request_user_devices( $creator_id )->then( sub {
my ( $req ) = @_;
log_if_fail "await_request_user_devices after out-of-order EDU";

# add the missing keys from stream_id 2
$client_keys->{ keys } = {
"curve25519:JJQDHPZKYD" => "MAtX5CLJXvHJ4wjvMBwc53+NnMceHiFch5r4mxOnOCA",
"ed25519:JJQDHPZKYD" => "LOu9tc6Sg7+mCEu3elrps3IiiotpefyaNnScTpSRQbU"
};

# then in stream_id 3, we rename the device
# await a hit to federation/query, responding with stream ID 3
$client_user_devices->{ stream_id } = 3;
$client_user_devices->{ devices }->[0]->{ device_display_name } = "New device name";

$req->respond_json($client_user_devices);
Future->done(1)
}),

# deliberately send an out of order EDU to check that we get requeried
$outbound_client->send_edu(
edu_type => "m.device_list_update",
destination => $local_server_name,
content => {
user_id => $creator_id,
device_id => $device_id,
device_display_name => "New device name",
# deliberately skip an EDU in sequence to check
# that we get re-queried
prev_id => [ 2 ],
stream_id => 3,

keys => {
device_keys => $client_user_keys
}
}
)
);
})->then( sub {
do_request_json_for( $user,
method => "POST",
uri => "/unstable/keys/query",
content => {
device_keys => {
$creator_id => [ $device_id ],
}
}
)
})->then( sub {
my ( $content ) = @_;

log_if_fail "query response", $content;

assert_json_keys( $content, "device_keys" );

my $device_keys = $content->{device_keys};
assert_json_keys( $device_keys, $creator_id );

my $creator_keys = $device_keys->{ $creator_id };
assert_json_keys( $creator_keys, $device_id );

my $creator_device_keys = $creator_keys->{ $device_id };
assert_json_keys( $creator_device_keys, "unsigned" );

my $unsigned = $creator_device_keys->{unsigned};

# check the keys are there from stream id 2
assert_json_keys( $creator_device_keys->{keys}, "curve25519:JJQDHPZKYD");
assert_json_keys( $creator_device_keys->{keys}, "ed25519:JJQDHPZKYD");

# check the name is there from stream id 3
assert_eq( $unsigned->{device_display_name}, "New device name" );

Future->done( 1 )
});
};

# for https://github.com/matrix-org/synapse/issues/6399
# test "When a room is upgraded to E2E, device lists caches should be flushed"
# in practice, if you share a room with a user, your device list should be synced
# irrespective of E2E, so let's not bother testing this now

# for https://github.com/matrix-org/synapse/issues/6399
#
# If you see you have a room in common with a user, you blindly assume you
# have been receiving device_list updates for them. But this fails if there
# was some period where you didn't have a room in common (or if an EDU got dropped).
# So instead, we should either flush the devicelist cache when we stop sharing a room
# with a user, or flush it when we start sharing a room.
#
# test "Device lists caches should be flushed when you re-encounter a user"

# test "Device lists get refreshed when you encounter an unrecognised device" # for https://github.com/matrix-org/synapse/issues/5095#issuecomment-501512352
# however, this doesn't help us if the keys just change

# test "If you send >20 device lists updates in a row, they don't get lost?" # for https://github.com/matrix-org/synapse/issues/5153