|  | 
|  | 1 | +use Future::Utils qw( repeat ); | 
|  | 2 | +use JSON qw( decode_json ); | 
|  | 3 | + | 
|  | 4 | +# Tests MSC2753 style peeking | 
|  | 5 | + | 
|  | 6 | +test "Local users can peek into world_readable rooms by room ID", | 
|  | 7 | +   requires => [ local_user_and_room_fixtures(), local_user_fixture() ], | 
|  | 8 | + | 
|  | 9 | +   check => sub { | 
|  | 10 | +      my ( $user, $room_id, $peeking_user ) = @_; | 
|  | 11 | + | 
|  | 12 | +      matrix_set_room_history_visibility( $user, $room_id, "world_readable" )->then(sub { | 
|  | 13 | +         do_request_json_for( $peeking_user, | 
|  | 14 | +            method => "POST", | 
|  | 15 | +            uri    => "/r0/peek/$room_id", | 
|  | 16 | +            content => {}, | 
|  | 17 | +         ) | 
|  | 18 | +      })->then( sub { | 
|  | 19 | +         matrix_send_room_text_message_synced( $user, $room_id, body => "something to peek"); | 
|  | 20 | +      })->then(sub { | 
|  | 21 | +         await_sync( $peeking_user, | 
|  | 22 | +            since => $peeking_user->sync_next_batch, | 
|  | 23 | +            check => sub { | 
|  | 24 | +               my ( $body ) = @_; | 
|  | 25 | +               return 0 unless $body->{rooms}{peek}{$room_id}; | 
|  | 26 | +               return $body; | 
|  | 27 | +            } | 
|  | 28 | +         ) | 
|  | 29 | +      })->then( sub { | 
|  | 30 | +         my ( $body ) = @_; | 
|  | 31 | +         $peeking_user->sync_next_batch = $body->{next_batch}; | 
|  | 32 | + | 
|  | 33 | +         log_if_fail "first sync response", $body; | 
|  | 34 | + | 
|  | 35 | +         my $room = $body->{rooms}{peek}{$room_id}; | 
|  | 36 | +         assert_json_keys( $room, qw( timeline state ephemeral )); | 
|  | 37 | +         assert_json_keys( $room->{timeline}, qw( events limited prev_batch )); | 
|  | 38 | +         assert_json_keys( $room->{state}, qw( events )); | 
|  | 39 | +         assert_json_keys( $room->{ephemeral}, qw( events )); | 
|  | 40 | + | 
|  | 41 | +         assert_ok( $room->{timeline}->{events}->[0]->{type} eq 'm.room.create', "peek has m.room.create" ); | 
|  | 42 | +         assert_ok( $room->{timeline}->{events}->[-1]->{type} eq 'm.room.message', "peek has message type" ); | 
|  | 43 | +         assert_ok( $room->{timeline}->{events}->[-1]->{content}->{body} eq 'something to peek', "peek has message body" ); | 
|  | 44 | +         assert_ok( @{$room->{state}->{events}} == 0 ); | 
|  | 45 | + | 
|  | 46 | +         assert_ok( scalar keys(%{$body->{rooms}{join}}) == 0, "no joined rooms present"); | 
|  | 47 | + | 
|  | 48 | +         matrix_sync_again( $peeking_user ); | 
|  | 49 | +      })->then( sub { | 
|  | 50 | +         my ( $body ) = @_; | 
|  | 51 | + | 
|  | 52 | +         log_if_fail "second sync response", $body; | 
|  | 53 | +         my $room = $body->{rooms}{peek}{$room_id}; | 
|  | 54 | +         (!defined $room) or die "Unchanged rooms shouldn't be in the sync response"; | 
|  | 55 | +      })->then( sub { | 
|  | 56 | +         matrix_send_room_text_message_synced( $user, $room_id, body => "something else to peek"); | 
|  | 57 | +      })->then( sub { | 
|  | 58 | +         await_sync( $peeking_user, | 
|  | 59 | +            since => $peeking_user->sync_next_batch, | 
|  | 60 | +            check => sub { | 
|  | 61 | +               my ( $body ) = @_; | 
|  | 62 | +               return 0 unless $body->{rooms}{peek}{$room_id}; | 
|  | 63 | +               return $body; | 
|  | 64 | +            } | 
|  | 65 | +         ) | 
|  | 66 | +      })->then( sub { | 
|  | 67 | +         my ( $body ) = @_; | 
|  | 68 | +         $peeking_user->sync_next_batch = $body->{next_batch}; | 
|  | 69 | + | 
|  | 70 | +         log_if_fail "third sync response", $body; | 
|  | 71 | +         my $room = $body->{rooms}{peek}{$room_id}; | 
|  | 72 | + | 
|  | 73 | +         assert_ok( $room->{timeline}->{events}->[-1]->{type} eq 'm.room.message', "second peek has message type" ); | 
|  | 74 | +         assert_ok( $room->{timeline}->{events}->[-1]->{content}->{body} eq 'something else to peek', "second peek has message body" ); | 
|  | 75 | + | 
|  | 76 | +         Future->done(1) | 
|  | 77 | +      }) | 
|  | 78 | +   }; | 
|  | 79 | + | 
|  | 80 | + | 
|  | 81 | +for my $visibility (qw(shared invited joined)) { | 
|  | 82 | +   test "We can't peek into rooms with $visibility history_visibility", | 
|  | 83 | +      requires => [ local_user_and_room_fixtures(), local_user_fixture() ], | 
|  | 84 | + | 
|  | 85 | +      check => sub { | 
|  | 86 | +         my ( $user, $room_id, $peeking_user ) = @_; | 
|  | 87 | + | 
|  | 88 | +         matrix_set_room_history_visibility( $user, $room_id, $visibility )->then(sub { | 
|  | 89 | +            do_request_json_for( $peeking_user, | 
|  | 90 | +               method => "POST", | 
|  | 91 | +               uri    => "/r0/peek/$room_id", | 
|  | 92 | +               content => {}, | 
|  | 93 | +            ); | 
|  | 94 | +         })->main::expect_http_403() | 
|  | 95 | +         ->then( sub { | 
|  | 96 | +            my ( $response ) = @_; | 
|  | 97 | +            my $body = decode_json( $response->content ); | 
|  | 98 | +            log_if_fail "error body", $body; | 
|  | 99 | +            assert_eq( $body->{errcode}, "M_FORBIDDEN", 'responsecode' ); | 
|  | 100 | +            Future->done( 1 ); | 
|  | 101 | +         }); | 
|  | 102 | +      }; | 
|  | 103 | +} | 
|  | 104 | + | 
|  | 105 | + | 
|  | 106 | +my $room_alias_name = sprintf("peektest-%s", $TEST_RUN_ID); | 
|  | 107 | +test "Local users can peek by room alias", | 
|  | 108 | +   requires => [ | 
|  | 109 | +      local_user_and_room_fixtures(room_opts => { room_alias_name => $room_alias_name }), | 
|  | 110 | +      local_user_fixture() | 
|  | 111 | +   ], | 
|  | 112 | + | 
|  | 113 | +   check => sub { | 
|  | 114 | +      my ( $user, $room_id, $peeking_user ) = @_; | 
|  | 115 | + | 
|  | 116 | +      matrix_set_room_history_visibility( $user, $room_id, "world_readable" )->then(sub { | 
|  | 117 | +         do_request_json_for( $peeking_user, | 
|  | 118 | +            method => "POST", | 
|  | 119 | +            uri    => "/r0/peek/#$room_alias_name:".$user->http->server_name, | 
|  | 120 | +            content => {}, | 
|  | 121 | +         ) | 
|  | 122 | +      })->then(sub { | 
|  | 123 | +         matrix_send_room_text_message_synced( $user, $room_id, body => "something to peek"); | 
|  | 124 | +      })->then(sub { | 
|  | 125 | +         await_sync( $peeking_user, | 
|  | 126 | +            since => $peeking_user->sync_next_batch, | 
|  | 127 | +            check => sub { | 
|  | 128 | +               my ( $body ) = @_; | 
|  | 129 | +               return 0 unless $body->{rooms}{peek}{$room_id}; | 
|  | 130 | +               return $body; | 
|  | 131 | +            } | 
|  | 132 | +         ) | 
|  | 133 | +      })->then( sub { | 
|  | 134 | +         my ( $body ) = @_; | 
|  | 135 | +         $peeking_user->sync_next_batch = $body->{next_batch}; | 
|  | 136 | + | 
|  | 137 | +         log_if_fail "first sync response", $body; | 
|  | 138 | + | 
|  | 139 | +         my $room = $body->{rooms}{peek}{$room_id}; | 
|  | 140 | +         assert_ok( $room->{timeline}->{events}->[-1]->{content}->{body} eq 'something to peek', "peek has message body" ); | 
|  | 141 | +         Future->done(1) | 
|  | 142 | +      }) | 
|  | 143 | +   }; | 
|  | 144 | + | 
|  | 145 | +test "Peeked rooms only turn up in the sync for the device who peeked them", | 
|  | 146 | +   requires => [ local_user_and_room_fixtures(), local_user_fixture() ], | 
|  | 147 | + | 
|  | 148 | +   check => sub { | 
|  | 149 | +      my ( $user, $room_id, $peeking_user ) = @_; | 
|  | 150 | +      my ( $peeking_user_device2 ); | 
|  | 151 | + | 
|  | 152 | +      matrix_set_room_history_visibility( $user, $room_id, "world_readable" )->then(sub { | 
|  | 153 | +         matrix_login_again_with_user($peeking_user); | 
|  | 154 | +      })->then(sub { | 
|  | 155 | +         $peeking_user_device2 = $_[0]; | 
|  | 156 | +         do_request_json_for( $peeking_user, | 
|  | 157 | +            method => "POST", | 
|  | 158 | +            uri    => "/r0/peek/$room_id", | 
|  | 159 | +            content => {}, | 
|  | 160 | +         ) | 
|  | 161 | +      })->then(sub { | 
|  | 162 | +         matrix_send_room_text_message_synced( $user, $room_id, body => "something to peek"); | 
|  | 163 | +      })->then(sub { | 
|  | 164 | +         await_sync( $peeking_user, | 
|  | 165 | +            since => $peeking_user->sync_next_batch, | 
|  | 166 | +            check => sub { | 
|  | 167 | +               my ( $body ) = @_; | 
|  | 168 | +               return 0 unless $body->{rooms}{peek}{$room_id}; | 
|  | 169 | +               return $body; | 
|  | 170 | +            } | 
|  | 171 | +         ) | 
|  | 172 | +      })->then( sub { | 
|  | 173 | +         my ( $body ) = @_; | 
|  | 174 | +         $peeking_user->sync_next_batch = $body->{next_batch}; | 
|  | 175 | +         log_if_fail "device 1 first sync response", $body; | 
|  | 176 | +         my $room = $body->{rooms}{peek}{$room_id}; | 
|  | 177 | +         assert_ok( $room->{timeline}->{events}->[-1]->{content}->{body} eq 'something to peek', "peek has message body" ); | 
|  | 178 | +      })->then(sub { | 
|  | 179 | +         # FIXME: racey - this may return blank due to the peek not having taken effect yet | 
|  | 180 | +         matrix_sync( $peeking_user_device2, timeout => 1000 ); | 
|  | 181 | +      })->then( sub { | 
|  | 182 | +         my ( $body ) = @_; | 
|  | 183 | +         log_if_fail "device 2 first sync response", $body; | 
|  | 184 | +         assert_ok( scalar keys(%{$body->{rooms}{peek}}) == 0, "no peeked rooms present"); | 
|  | 185 | +      })->then( sub { | 
|  | 186 | +         matrix_send_room_text_message_synced( $user, $room_id, body => "something else to peek") | 
|  | 187 | +      })->then( sub { | 
|  | 188 | +         await_sync( $peeking_user, | 
|  | 189 | +            since => $peeking_user->sync_next_batch, | 
|  | 190 | +            check => sub { | 
|  | 191 | +               my ( $body ) = @_; | 
|  | 192 | +               return 0 unless $body->{rooms}{peek}{$room_id}; | 
|  | 193 | +               return $body; | 
|  | 194 | +            } | 
|  | 195 | +         ) | 
|  | 196 | +      })->then( sub { | 
|  | 197 | +         my ( $body ) = @_; | 
|  | 198 | +         $peeking_user->sync_next_batch = $body->{next_batch}; | 
|  | 199 | +         log_if_fail "device 1 second sync response", $body; | 
|  | 200 | +         my $room = $body->{rooms}{peek}{$room_id}; | 
|  | 201 | +         assert_ok( $room->{timeline}->{events}->[-1]->{content}->{body} eq 'something else to peek', "second peek has message body" ); | 
|  | 202 | +         # FIXME: racey - this may return blank due to the peek not having taken effect yet | 
|  | 203 | +         matrix_sync_again( $peeking_user_device2, timeout => 1000 ); | 
|  | 204 | +      })->then( sub { | 
|  | 205 | +         my ( $body ) = @_; | 
|  | 206 | +         log_if_fail "device 2 second sync response", $body; | 
|  | 207 | +         assert_ok( scalar keys(%{$body->{rooms}{peek}}) == 0, "still no peeked rooms present"); | 
|  | 208 | +         Future->done(1) | 
|  | 209 | +      }) | 
|  | 210 | +   }; | 
|  | 211 | + | 
|  | 212 | +# test "Users can unpeek from rooms" | 
|  | 213 | + | 
|  | 214 | +# test "Users can peek, unpeek and peek again" | 
|  | 215 | + | 
|  | 216 | +# test "Peeking with full_state=true does the right thing" | 
|  | 217 | + | 
|  | 218 | +# test "Joining a peeked room moves it atomically from peeked to joined rooms and stops peeking" | 
|  | 219 | + | 
|  | 220 | +# test "Parting a room which was joined after being peeked doesn't go back to being peeked" | 
|  | 221 | + | 
|  | 222 | +# test "Changing history visibility to non-world_readable terminates peeks" | 
0 commit comments