atagen

macltok

public
Star0
Watch0
002c79e4c6ec3bd46e13dff862d52859b5fc9915
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git
tests/mentions_discovery.rsRust232 lines8.2 KB
1
use matrix_sdk::{
2
    Room,
3
    ruma::{
4
        UInt,
5
        api::client::push::get_notifications::v3,
6
        event_id,
7
        events::{AnySyncTimelineEvent, Mentions},
8
        owned_user_id,
9
        push::{Action, EventMatchConditionData, NewConditionalPushRule, PushCondition, Ruleset},
10
        room_id,
11
        serde::Raw,
12
    },
13
    test_utils::mocks::MatrixMockServer,
14
};
15
use matrix_sdk_test::{JoinedRoomBuilder, event_factory::EventFactory};
16
use serde_json::json;
17
use std::time::Duration;
18
use wiremock::{
19
    Mock, ResponseTemplate,
20
    matchers::{method, path, query_param},
21
};
22
23
fn explicit_mention(
24
    room_id: &matrix_sdk::ruma::RoomId,
25
    event_id: &matrix_sdk::ruma::EventId,
26
    sender: &matrix_sdk::ruma::UserId,
27
    mentioned: &matrix_sdk::ruma::UserId,
28
) -> Raw<AnySyncTimelineEvent> {
29
    EventFactory::new()
30
        .room(room_id)
31
        .text_msg("hello")
32
        .mentions(Mentions::with_user_ids([mentioned.to_owned()]))
33
        .event_id(event_id)
34
        .sender(sender)
35
        .into_raw_sync()
36
}
37
38
fn assert_mention_actions(actions: &[Action]) {
39
    assert!(
40
        actions
41
            .iter()
42
            .any(|action| matches!(action, Action::Notify))
43
    );
44
    assert!(actions.iter().any(Action::is_highlight));
45
}
46
47
async fn sync_room_with_mention(
48
    server: &MatrixMockServer,
49
    client: &matrix_sdk::Client,
50
    room_id: &matrix_sdk::ruma::RoomId,
51
    event_id: &matrix_sdk::ruma::EventId,
52
) {
53
    let user_id = client.user_id().unwrap();
54
    let factory = EventFactory::new().room(room_id);
55
    let sender = owned_user_id!("@alice:localhost");
56
    let event = explicit_mention(room_id, event_id, &sender, user_id);
57
    let rules = Ruleset::server_default(user_id);
58
59
    server
60
        .mock_sync()
61
        .ok_and_run(client, |builder| {
62
            builder.add_global_account_data(factory.push_rules(rules));
63
            builder.add_joined_room(
64
                JoinedRoomBuilder::new(room_id)
65
                    .add_state_event(factory.member(user_id))
66
                    .add_timeline_event(event),
67
            );
68
        })
69
        .await;
70
}
71
72
async fn actions_for(room: &Room, event: &Raw<AnySyncTimelineEvent>) -> Option<Vec<Action>> {
73
    room.event_push_actions(event).await.unwrap()
74
}
75
76
#[tokio::test]
77
async fn joined_room_update_exposes_mention_actions_without_opening_a_ui_timeline() {
78
    let server = MatrixMockServer::new().await;
79
    let client = server.client_builder().build().await;
80
    let room_id = room_id!("!mentions:localhost");
81
    let event_id = event_id!("$mention:localhost");
82
    let mut updates = client.subscribe_to_all_room_updates();
83
84
    sync_room_with_mention(&server, &client, room_id, event_id).await;
85
86
    let room_updates = tokio::time::timeout(Duration::from_secs(2), updates.recv())
87
        .await
88
        .unwrap()
89
        .unwrap();
90
    let joined = room_updates
91
        .joined
92
        .get(room_id)
93
        .expect("the joined room update was absent");
94
    let event = joined
95
        .timeline
96
        .events
97
        .iter()
98
        .find(|event| event.event_id().as_deref().map(|id| id.as_str()) == Some(event_id.as_str()))
99
        .expect("the explicit mention was absent from the room update");
100
    let event_json: serde_json::Value =
101
        serde_json::from_str(event.kind.raw().json().get()).unwrap();
102
103
    assert_eq!(
104
        event_json["content"]["m.mentions"]["user_ids"],
105
        json!([client.user_id().unwrap().as_str()])
106
    );
107
    assert_mention_actions(
108
        event
109
            .push_actions()
110
            .expect("push actions were not computed"),
111
    );
112
}
113
114
#[tokio::test]
115
async fn event_cache_generic_update_finds_the_mention_without_opening_a_ui_timeline() {
116
    let server = MatrixMockServer::new().await;
117
    let client = server.client_builder().build().await;
118
    let room_id = room_id!("!cached-mentions:localhost");
119
    let event_id = event_id!("$cached-mention:localhost");
120
    client.event_cache().subscribe().unwrap();
121
    let mut updates = client.event_cache().subscribe_to_room_generic_updates();
122
123
    sync_room_with_mention(&server, &client, room_id, event_id).await;
124
125
    let update = tokio::time::timeout(Duration::from_secs(2), updates.recv())
126
        .await
127
        .unwrap()
128
        .unwrap();
129
    assert_eq!(update.room_id, room_id);
130
    let room = client.get_room(room_id).unwrap();
131
    let (event_cache, _drop_handles) = room.event_cache().await.unwrap();
132
    let event = event_cache.find_event(event_id).await.unwrap().unwrap();
133
    assert_eq!(
134
        event.event_id().as_deref().map(|id| id.as_str()),
135
        Some(event_id.as_str())
136
    );
137
    assert_mention_actions(
138
        event
139
            .push_actions()
140
            .expect("cached push actions were absent"),
141
    );
142
}
143
144
#[tokio::test]
145
async fn ignored_sender_and_muted_room_actions_remain_sdk_push_rule_results() {
146
    let server = MatrixMockServer::new().await;
147
    let client = server.client_builder().build().await;
148
    let ignored_room_id = room_id!("!ignored-sender:localhost");
149
    let muted_room_id = room_id!("!muted-room:localhost");
150
    let ignored_sender = owned_user_id!("@ignored:localhost");
151
    let own_user = client.user_id().unwrap();
152
    let factory = EventFactory::new();
153
    let ignored_event_id = event_id!("$ignored-mention:localhost");
154
    let muted_event_id = event_id!("$muted-message:localhost");
155
    let ignored_event =
156
        explicit_mention(ignored_room_id, ignored_event_id, &ignored_sender, own_user);
157
    let sender = owned_user_id!("@alice:localhost");
158
    let muted_event = explicit_mention(muted_room_id, muted_event_id, &sender, own_user);
159
    let mut rules = Ruleset::server_default(own_user);
160
    rules.override_.shift_insert(
161
        1,
162
        NewConditionalPushRule::new(
163
            "mute-room".to_owned(),
164
            vec![PushCondition::EventMatch(EventMatchConditionData::new(
165
                "room_id".to_owned(),
166
                muted_room_id.to_string(),
167
            ))],
168
            Vec::new(),
169
        )
170
        .into(),
171
    );
172
173
    server
174
        .mock_sync()
175
        .ok_and_run(&client, |builder| {
176
            builder.add_global_account_data(factory.ignored_user_list([ignored_sender.clone()]));
177
            builder.add_global_account_data(factory.push_rules(rules));
178
            builder.add_joined_room(
179
                JoinedRoomBuilder::new(ignored_room_id)
180
                    .add_state_event(EventFactory::new().room(ignored_room_id).member(own_user))
181
                    .add_timeline_event(ignored_event.clone()),
182
            );
183
            builder.add_joined_room(
184
                JoinedRoomBuilder::new(muted_room_id)
185
                    .add_state_event(EventFactory::new().room(muted_room_id).member(own_user))
186
                    .add_timeline_event(muted_event.clone()),
187
            );
188
        })
189
        .await;
190
191
    assert!(client.is_user_ignored(&ignored_sender).await);
192
    let ignored_room = client.get_room(ignored_room_id).unwrap();
193
    let ignored_actions = actions_for(&ignored_room, &ignored_event)
194
        .await
195
        .expect("the ignored sender's push actions could not be computed");
196
    assert_mention_actions(&ignored_actions);
197
198
    let muted_room = client.get_room(muted_room_id).unwrap();
199
    let muted_actions = actions_for(&muted_room, &muted_event)
200
        .await
201
        .expect("the muted room's push actions could not be computed");
202
    assert!(muted_actions.is_empty());
203
}
204
205
#[tokio::test]
206
async fn notifications_v3_request_round_trips_opaque_pagination() {
207
    let server = MatrixMockServer::new().await;
208
    let client = server.client_builder().build().await;
209
    let cursor = "opaque / cursor?x=1&y=2";
210
    let next_cursor = "next / cursor?x=3&y=4";
211
212
    Mock::given(method("GET"))
213
        .and(path("/_matrix/client/v3/notifications"))
214
        .and(query_param("from", cursor))
215
        .and(query_param("limit", "8"))
216
        .and(query_param("only", "highlight"))
217
        .respond_with(ResponseTemplate::new(200).set_body_json(json!({
218
            "next_token": next_cursor,
219
            "notifications": [],
220
        })))
221
        .expect(1)
222
        .mount(server.server())
223
        .await;
224
225
    let mut request = v3::Request::new();
226
    request.from = Some(cursor.to_owned());
227
    request.limit = Some(UInt::new(8).unwrap());
228
    request.only = Some("highlight".to_owned());
229
    let response = client.send(request).await.unwrap();
230
231
    assert_eq!(response.next_token.as_deref(), Some(next_cursor));
232
}