Skip to content

Commit

Permalink
Add a passing test for recovery from gappy device list updates (#761)
Browse files Browse the repository at this point in the history
  • Loading branch information
ara4n authored and erikjohnston committed Jan 27, 2020
1 parent b553e8c commit 337160e
Showing 1 changed file with 202 additions and 10 deletions.
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

0 comments on commit 337160e

Please sign in to comment.