Line data Source code
1 : /**
2 : * @file test_domain_dialogs.c
3 : * @brief Unit tests for domain_get_dialogs (US-04, v1).
4 : *
5 : * Covers the request builder and the single-Dialog parse path. Multi-dialog
6 : * parsing is deferred until PeerNotifySettings skip logic is added.
7 : */
8 :
9 : #include "test_helpers.h"
10 : #include "domain/read/dialogs.h"
11 : #include "tl_serial.h"
12 : #include "tl_registry.h"
13 : #include "mock_socket.h"
14 : #include "mock_crypto.h"
15 : #include "mtproto_session.h"
16 : #include "transport.h"
17 : #include "api_call.h"
18 :
19 : #include <stdlib.h>
20 : #include <string.h>
21 :
22 : #define CRC_dialog 0xd58a08c6u
23 : #define CRC_peerNotifySettings 0xa83b0426u
24 :
25 10 : static void build_fake_encrypted_response(const uint8_t *payload, size_t plen,
26 : uint8_t *out, size_t *out_len) {
27 : TlWriter w;
28 10 : tl_writer_init(&w);
29 10 : uint8_t zeros24[24] = {0};
30 10 : tl_write_raw(&w, zeros24, 24);
31 10 : uint8_t header[32] = {0};
32 10 : uint32_t plen32 = (uint32_t)plen;
33 10 : memcpy(header + 28, &plen32, 4);
34 10 : tl_write_raw(&w, header, 32);
35 10 : tl_write_raw(&w, payload, plen);
36 :
37 10 : size_t enc = w.len - 24;
38 10 : if (enc % 16 != 0) {
39 8 : uint8_t pad[16] = {0};
40 8 : tl_write_raw(&w, pad, 16 - (enc % 16));
41 : }
42 10 : size_t units = w.len / 4;
43 10 : out[0] = (uint8_t)units;
44 10 : memcpy(out + 1, w.data, w.len);
45 10 : *out_len = 1 + w.len;
46 10 : tl_writer_free(&w);
47 10 : }
48 :
49 11 : static void fix_session(MtProtoSession *s) {
50 11 : mtproto_session_init(s);
51 11 : s->session_id = 0; /* match the zero session_id in fake encrypted frames */
52 11 : uint8_t key[256] = {0};
53 11 : mtproto_session_set_auth_key(s, key);
54 11 : mtproto_session_set_salt(s, 0xAAAAAAAAAAAAAAAAULL);
55 11 : }
56 :
57 11 : static void fix_transport(Transport *t) {
58 11 : transport_init(t);
59 11 : t->fd = 42; t->connected = 1; t->dc_id = 1;
60 11 : }
61 :
62 11 : static void fix_cfg(ApiConfig *cfg) {
63 11 : api_config_init(cfg);
64 11 : cfg->api_id = 12345; cfg->api_hash = "deadbeef";
65 11 : }
66 :
67 : /* Write a full Dialog record including the required trailer (counts +
68 : * empty PeerNotifySettings). */
69 10 : static void write_dialog(TlWriter *w,
70 : uint32_t peer_crc, int64_t peer_id,
71 : int32_t top_msg, int32_t unread) {
72 10 : tl_write_uint32(w, CRC_dialog);
73 10 : tl_write_uint32(w, 0); /* flags */
74 10 : tl_write_uint32(w, peer_crc);
75 10 : tl_write_int64 (w, peer_id);
76 10 : tl_write_int32 (w, top_msg); /* top_message */
77 10 : tl_write_int32 (w, 0); /* read_inbox_max_id */
78 10 : tl_write_int32 (w, 0); /* read_outbox_max_id */
79 10 : tl_write_int32 (w, unread); /* unread_count */
80 10 : tl_write_int32 (w, 0); /* unread_mentions_count */
81 10 : tl_write_int32 (w, 0); /* unread_reactions_count */
82 : /* PeerNotifySettings with no flags. */
83 10 : tl_write_uint32(w, CRC_peerNotifySettings);
84 10 : tl_write_uint32(w, 0);
85 10 : }
86 :
87 : /* Build a messages.dialogsSlice with exactly ONE dialog. */
88 2 : static size_t make_one_dialog_payload(uint8_t *buf, size_t max,
89 : uint32_t peer_crc, int64_t peer_id,
90 : int32_t top_msg, int32_t unread) {
91 : TlWriter w;
92 2 : tl_writer_init(&w);
93 2 : tl_write_uint32(&w, TL_messages_dialogsSlice);
94 2 : tl_write_int32 (&w, 1); /* count */
95 2 : tl_write_uint32(&w, TL_vector);
96 2 : tl_write_uint32(&w, 1); /* dialogs count */
97 2 : write_dialog(&w, peer_crc, peer_id, top_msg, unread);
98 :
99 2 : size_t n = w.len < max ? w.len : max;
100 2 : memcpy(buf, w.data, n);
101 2 : tl_writer_free(&w);
102 2 : return n;
103 : }
104 :
105 : /* Build a messages.dialogsSlice with N dialogs (for v2 iteration test). */
106 1 : static size_t make_multi_dialog_payload(uint8_t *buf, size_t max,
107 : int n_dialogs) {
108 : TlWriter w;
109 1 : tl_writer_init(&w);
110 1 : tl_write_uint32(&w, TL_messages_dialogsSlice);
111 1 : tl_write_int32 (&w, n_dialogs);
112 1 : tl_write_uint32(&w, TL_vector);
113 1 : tl_write_uint32(&w, (uint32_t)n_dialogs);
114 6 : for (int i = 0; i < n_dialogs; i++) {
115 5 : write_dialog(&w, TL_peerUser, 1000 + i, 100 + i, i);
116 : }
117 1 : size_t res = w.len < max ? w.len : max;
118 1 : memcpy(buf, w.data, res);
119 1 : tl_writer_free(&w);
120 1 : return res;
121 : }
122 :
123 : /* P5-08: build a full messages.dialogsSlice with 1 dialog (user peer),
124 : * 1 message, 0 chats, 1 user with first_name+last_name+username, and
125 : * verify the DialogEntry gets the user's name + username populated. */
126 1 : static void test_dialogs_title_join_user(void) {
127 1 : mock_socket_reset(); mock_crypto_reset(); dialogs_cache_flush();
128 :
129 1 : TlWriter w; tl_writer_init(&w);
130 1 : tl_write_uint32(&w, TL_messages_dialogsSlice);
131 1 : tl_write_int32 (&w, 1);
132 1 : tl_write_uint32(&w, TL_vector);
133 1 : tl_write_uint32(&w, 1);
134 1 : write_dialog(&w, TL_peerUser, 7777LL, 42, 3);
135 :
136 : /* messages vector: 1 simple message for peer_id=7777 */
137 1 : tl_write_uint32(&w, TL_vector);
138 1 : tl_write_uint32(&w, 1);
139 1 : tl_write_uint32(&w, TL_message);
140 1 : tl_write_uint32(&w, 0);
141 1 : tl_write_uint32(&w, 0);
142 1 : tl_write_int32 (&w, 42);
143 1 : tl_write_uint32(&w, TL_peerUser);
144 1 : tl_write_int64 (&w, 7777LL);
145 1 : tl_write_int32 (&w, 1700000000);
146 1 : tl_write_string(&w, "msg");
147 :
148 : /* chats vector: empty */
149 1 : tl_write_uint32(&w, TL_vector);
150 1 : tl_write_uint32(&w, 0);
151 :
152 : /* users vector: one user with first_name + last_name + username */
153 1 : tl_write_uint32(&w, TL_vector);
154 1 : tl_write_uint32(&w, 1);
155 1 : tl_write_uint32(&w, TL_user);
156 1 : uint32_t uflags = (1u << 1) | (1u << 2) | (1u << 3);
157 1 : tl_write_uint32(&w, uflags);
158 1 : tl_write_uint32(&w, 0); /* flags2 */
159 1 : tl_write_int64 (&w, 7777LL);
160 1 : tl_write_string(&w, "Alice");
161 1 : tl_write_string(&w, "Smith");
162 1 : tl_write_string(&w, "alice_s");
163 :
164 1 : uint8_t payload[1024]; memcpy(payload, w.data, w.len);
165 1 : size_t plen = w.len; tl_writer_free(&w);
166 :
167 1 : uint8_t resp[2048]; size_t rlen = 0;
168 1 : build_fake_encrypted_response(payload, plen, resp, &rlen);
169 1 : mock_socket_set_response(resp, rlen);
170 :
171 : MtProtoSession s; Transport t; ApiConfig cfg;
172 1 : fix_session(&s); fix_transport(&t); fix_cfg(&cfg);
173 :
174 1 : DialogEntry entries[5] = {0};
175 1 : int count = 0;
176 1 : int rc = domain_get_dialogs(&cfg, &s, &t, 5, 0, entries, &count, NULL);
177 1 : ASSERT(rc == 0, "title join ok");
178 1 : ASSERT(count == 1, "one dialog");
179 1 : ASSERT(entries[0].peer_id == 7777LL, "peer_id");
180 1 : ASSERT(strcmp(entries[0].title, "Alice Smith") == 0, "joined name");
181 1 : ASSERT(strcmp(entries[0].username, "alice_s") == 0, "username");
182 : }
183 :
184 : /* TUI-08: access_hash from a user with flags.0 set is threaded onto DialogEntry. */
185 1 : static void test_dialogs_user_access_hash_threaded(void) {
186 1 : mock_socket_reset(); mock_crypto_reset(); dialogs_cache_flush();
187 :
188 1 : TlWriter w; tl_writer_init(&w);
189 1 : tl_write_uint32(&w, TL_messages_dialogsSlice);
190 1 : tl_write_int32 (&w, 1);
191 1 : tl_write_uint32(&w, TL_vector);
192 1 : tl_write_uint32(&w, 1);
193 1 : write_dialog(&w, TL_peerUser, 42LL, 1, 0);
194 :
195 : /* messages vector: empty */
196 1 : tl_write_uint32(&w, TL_vector);
197 1 : tl_write_uint32(&w, 0);
198 :
199 : /* chats vector: empty */
200 1 : tl_write_uint32(&w, TL_vector);
201 1 : tl_write_uint32(&w, 0);
202 :
203 : /* users vector: one user with access_hash + first_name + username */
204 1 : tl_write_uint32(&w, TL_vector);
205 1 : tl_write_uint32(&w, 1);
206 1 : tl_write_uint32(&w, TL_user);
207 1 : uint32_t uflags = (1u << 0) | (1u << 1) | (1u << 3);
208 1 : tl_write_uint32(&w, uflags);
209 1 : tl_write_uint32(&w, 0);
210 1 : tl_write_int64 (&w, 42LL);
211 1 : tl_write_int64 (&w, 0xFEEDFACEDEADBEEFLL);
212 1 : tl_write_string(&w, "Bob");
213 1 : tl_write_string(&w, "bob");
214 :
215 1 : uint8_t payload[1024]; memcpy(payload, w.data, w.len);
216 1 : size_t plen = w.len; tl_writer_free(&w);
217 :
218 1 : uint8_t resp[2048]; size_t rlen = 0;
219 1 : build_fake_encrypted_response(payload, plen, resp, &rlen);
220 1 : mock_socket_set_response(resp, rlen);
221 :
222 : MtProtoSession s; Transport t; ApiConfig cfg;
223 1 : fix_session(&s); fix_transport(&t); fix_cfg(&cfg);
224 :
225 1 : DialogEntry entries[5] = {0};
226 1 : int count = 0;
227 1 : int rc = domain_get_dialogs(&cfg, &s, &t, 5, 0, entries, &count, NULL);
228 1 : ASSERT(rc == 0, "dialog with access_hash ok");
229 1 : ASSERT(count == 1, "one dialog");
230 1 : ASSERT(entries[0].have_access_hash == 1, "access_hash threaded");
231 1 : ASSERT(entries[0].access_hash == (int64_t)0xFEEDFACEDEADBEEFLL,
232 : "access_hash value");
233 : }
234 :
235 : /* TUI-08: channel access_hash is threaded too. */
236 1 : static void test_dialogs_channel_access_hash_threaded(void) {
237 1 : mock_socket_reset(); mock_crypto_reset(); dialogs_cache_flush();
238 :
239 1 : TlWriter w; tl_writer_init(&w);
240 1 : tl_write_uint32(&w, TL_messages_dialogsSlice);
241 1 : tl_write_int32 (&w, 1);
242 1 : tl_write_uint32(&w, TL_vector);
243 1 : tl_write_uint32(&w, 1);
244 1 : write_dialog(&w, TL_peerChannel, -1001234567LL, 1, 0);
245 :
246 : /* messages vector: empty */
247 1 : tl_write_uint32(&w, TL_vector);
248 1 : tl_write_uint32(&w, 0);
249 :
250 : /* chats vector: one channel with access_hash */
251 1 : tl_write_uint32(&w, TL_vector);
252 1 : tl_write_uint32(&w, 1);
253 1 : tl_write_uint32(&w, TL_channel);
254 1 : uint32_t cflags = (1u << 13); /* access_hash */
255 1 : tl_write_uint32(&w, cflags);
256 1 : tl_write_uint32(&w, 0); /* flags2 */
257 1 : tl_write_int64 (&w, -1001234567LL);
258 1 : tl_write_int64 (&w, 0xAABBCCDDEEFF0011LL);
259 1 : tl_write_string(&w, "Chan");
260 1 : tl_write_uint32(&w, 0x37c1011cu); /* chatPhotoEmpty */
261 1 : tl_write_int32 (&w, 1700000000);
262 :
263 : /* users vector: empty */
264 1 : tl_write_uint32(&w, TL_vector);
265 1 : tl_write_uint32(&w, 0);
266 :
267 1 : uint8_t payload[1024]; memcpy(payload, w.data, w.len);
268 1 : size_t plen = w.len; tl_writer_free(&w);
269 :
270 1 : uint8_t resp[2048]; size_t rlen = 0;
271 1 : build_fake_encrypted_response(payload, plen, resp, &rlen);
272 1 : mock_socket_set_response(resp, rlen);
273 :
274 : MtProtoSession s; Transport t; ApiConfig cfg;
275 1 : fix_session(&s); fix_transport(&t); fix_cfg(&cfg);
276 :
277 1 : DialogEntry entries[5] = {0};
278 1 : int count = 0;
279 1 : int rc = domain_get_dialogs(&cfg, &s, &t, 5, 0, entries, &count, NULL);
280 1 : ASSERT(rc == 0, "channel dialog ok");
281 1 : ASSERT(count == 1, "one dialog");
282 1 : ASSERT(entries[0].kind == DIALOG_PEER_CHANNEL, "channel kind");
283 1 : ASSERT(entries[0].have_access_hash == 1, "channel access_hash threaded");
284 1 : ASSERT(entries[0].access_hash == (int64_t)0xAABBCCDDEEFF0011LL,
285 : "channel access_hash value");
286 : }
287 :
288 1 : static void test_dialogs_multi_entries(void) {
289 1 : mock_socket_reset(); mock_crypto_reset(); dialogs_cache_flush();
290 :
291 : uint8_t payload[1024];
292 1 : size_t plen = make_multi_dialog_payload(payload, sizeof(payload), 5);
293 1 : uint8_t resp[2048]; size_t rlen = 0;
294 1 : build_fake_encrypted_response(payload, plen, resp, &rlen);
295 1 : mock_socket_set_response(resp, rlen);
296 :
297 : MtProtoSession s; Transport t; ApiConfig cfg;
298 1 : fix_session(&s); fix_transport(&t); fix_cfg(&cfg);
299 :
300 1 : DialogEntry entries[10] = {0};
301 1 : int count = 0;
302 1 : int rc = domain_get_dialogs(&cfg, &s, &t, 10, 0, entries, &count, NULL);
303 1 : ASSERT(rc == 0, "multi-entry dialogs parsed");
304 1 : ASSERT(count == 5, "all 5 dialogs iterated");
305 1 : ASSERT(entries[0].peer_id == 1000, "first peer id");
306 1 : ASSERT(entries[4].peer_id == 1004, "last peer id");
307 1 : ASSERT(entries[2].top_message_id == 102, "middle top_msg");
308 : }
309 :
310 1 : static void test_dialogs_single_user(void) {
311 1 : mock_socket_reset();
312 1 : mock_crypto_reset();
313 1 : dialogs_cache_flush();
314 :
315 : uint8_t payload[256];
316 1 : size_t plen = make_one_dialog_payload(payload, sizeof(payload),
317 : TL_peerUser, 123456789LL, 42, 3);
318 :
319 1 : uint8_t resp[1024]; size_t rlen = 0;
320 1 : build_fake_encrypted_response(payload, plen, resp, &rlen);
321 1 : mock_socket_set_response(resp, rlen);
322 :
323 : MtProtoSession s; Transport t; ApiConfig cfg;
324 1 : fix_session(&s); fix_transport(&t); fix_cfg(&cfg);
325 :
326 1 : DialogEntry entries[10] = {0};
327 1 : int count = 0;
328 1 : int rc = domain_get_dialogs(&cfg, &s, &t, 10, 0, entries, &count, NULL);
329 1 : ASSERT(rc == 0, "dialogs: must succeed");
330 1 : ASSERT(count == 1, "one dialog parsed");
331 1 : ASSERT(entries[0].kind == DIALOG_PEER_USER, "peer kind=user");
332 1 : ASSERT(entries[0].peer_id == 123456789LL, "peer_id matches");
333 1 : ASSERT(entries[0].top_message_id == 42, "top_message_id matches");
334 1 : ASSERT(entries[0].unread_count == 3, "unread_count matches");
335 : }
336 :
337 1 : static void test_dialogs_single_channel(void) {
338 1 : mock_socket_reset();
339 1 : mock_crypto_reset();
340 1 : dialogs_cache_flush();
341 :
342 : uint8_t payload[256];
343 1 : size_t plen = make_one_dialog_payload(payload, sizeof(payload),
344 : TL_peerChannel,
345 : -1001234567890LL, 17, 0);
346 :
347 1 : uint8_t resp[1024]; size_t rlen = 0;
348 1 : build_fake_encrypted_response(payload, plen, resp, &rlen);
349 1 : mock_socket_set_response(resp, rlen);
350 :
351 : MtProtoSession s; Transport t; ApiConfig cfg;
352 1 : fix_session(&s); fix_transport(&t); fix_cfg(&cfg);
353 :
354 1 : DialogEntry entries[10] = {0};
355 1 : int count = 0;
356 1 : int rc = domain_get_dialogs(&cfg, &s, &t, 10, 0, entries, &count, NULL);
357 1 : ASSERT(rc == 0, "dialogs: must succeed");
358 1 : ASSERT(count == 1, "one dialog parsed");
359 1 : ASSERT(entries[0].kind == DIALOG_PEER_CHANNEL, "peer kind=channel");
360 1 : ASSERT(entries[0].peer_id == -1001234567890LL, "peer_id matches");
361 : }
362 :
363 1 : static void test_dialogs_rpc_error(void) {
364 1 : mock_socket_reset();
365 1 : mock_crypto_reset();
366 1 : dialogs_cache_flush();
367 :
368 : uint8_t payload[128];
369 : TlWriter w;
370 1 : tl_writer_init(&w);
371 1 : tl_write_uint32(&w, TL_rpc_error);
372 1 : tl_write_int32(&w, 401);
373 1 : tl_write_string(&w, "AUTH_KEY_UNREGISTERED");
374 1 : memcpy(payload, w.data, w.len);
375 1 : size_t plen = w.len;
376 1 : tl_writer_free(&w);
377 :
378 1 : uint8_t resp[512]; size_t rlen = 0;
379 1 : build_fake_encrypted_response(payload, plen, resp, &rlen);
380 1 : mock_socket_set_response(resp, rlen);
381 :
382 : MtProtoSession s; Transport t; ApiConfig cfg;
383 1 : fix_session(&s); fix_transport(&t); fix_cfg(&cfg);
384 :
385 1 : DialogEntry entries[5] = {0};
386 1 : int count = 0;
387 1 : int rc = domain_get_dialogs(&cfg, &s, &t, 5, 0, entries, &count, NULL);
388 1 : ASSERT(rc != 0, "RPC error must propagate");
389 : }
390 :
391 1 : static void test_dialogs_unexpected_top(void) {
392 1 : mock_socket_reset();
393 1 : mock_crypto_reset();
394 1 : dialogs_cache_flush();
395 :
396 : uint8_t payload[32];
397 : TlWriter w;
398 1 : tl_writer_init(&w);
399 1 : tl_write_uint32(&w, 0xBADBADBAu);
400 1 : tl_write_uint32(&w, 0);
401 1 : memcpy(payload, w.data, w.len);
402 1 : size_t plen = w.len;
403 1 : tl_writer_free(&w);
404 :
405 1 : uint8_t resp[512]; size_t rlen = 0;
406 1 : build_fake_encrypted_response(payload, plen, resp, &rlen);
407 1 : mock_socket_set_response(resp, rlen);
408 :
409 : MtProtoSession s; Transport t; ApiConfig cfg;
410 1 : fix_session(&s); fix_transport(&t); fix_cfg(&cfg);
411 :
412 1 : DialogEntry entries[5] = {0};
413 1 : int count = 0;
414 1 : int rc = domain_get_dialogs(&cfg, &s, &t, 5, 0, entries, &count, NULL);
415 1 : ASSERT(rc != 0, "unexpected constructor must fail");
416 : }
417 :
418 1 : static void test_dialogs_null_args(void) {
419 : DialogEntry e[1];
420 1 : int c = 0;
421 1 : ASSERT(domain_get_dialogs(NULL, NULL, NULL, 5, 0, e, &c, NULL) == -1, "null cfg");
422 1 : ASSERT(domain_get_dialogs((ApiConfig *)1, NULL, NULL, 5, 0, e, &c, NULL) == -1, "null s");
423 : /* max_entries <= 0 rejected */
424 1 : ApiConfig cfg; fix_cfg(&cfg);
425 1 : MtProtoSession s; fix_session(&s);
426 1 : Transport t; fix_transport(&t);
427 1 : ASSERT(domain_get_dialogs(&cfg, &s, &t, 0, 0, e, &c, NULL) == -1, "zero limit");
428 : }
429 :
430 : /* Wire-inspection: when archived=1, folder_id=1 (flags bit 1 set) must appear
431 : * in the outbound buffer before the response is processed.
432 : *
433 : * messages.getDialogs with archived:
434 : * CRC 0xa0f4cb4f (LE: 4f cb f4 a0)
435 : * flags 0x00000002 (bit 1 = folder_id present)
436 : * folder_id 0x00000001
437 : * ...
438 : *
439 : * We scan the raw sent buffer for the 4-byte little-endian sequence
440 : * {0x02, 0x00, 0x00, 0x00} immediately following the CRC, and then
441 : * {0x01, 0x00, 0x00, 0x00} as folder_id. Because the mock AES is the
442 : * identity cipher the TL bytes appear verbatim in the sent buffer.
443 : */
444 1 : static void test_dialogs_archived_folder_id_on_wire(void) {
445 1 : mock_socket_reset(); mock_crypto_reset(); dialogs_cache_flush();
446 :
447 : /* Minimal valid response: empty messages.dialogs */
448 1 : TlWriter pw; tl_writer_init(&pw);
449 1 : tl_write_uint32(&pw, TL_messages_dialogs);
450 1 : tl_write_uint32(&pw, TL_vector); tl_write_uint32(&pw, 0); /* dialogs */
451 1 : tl_write_uint32(&pw, TL_vector); tl_write_uint32(&pw, 0); /* messages */
452 1 : tl_write_uint32(&pw, TL_vector); tl_write_uint32(&pw, 0); /* chats */
453 1 : tl_write_uint32(&pw, TL_vector); tl_write_uint32(&pw, 0); /* users */
454 1 : uint8_t resp[512]; size_t rlen = 0;
455 1 : build_fake_encrypted_response(pw.data, pw.len, resp, &rlen);
456 1 : tl_writer_free(&pw);
457 1 : mock_socket_set_response(resp, rlen);
458 :
459 : MtProtoSession s; Transport t; ApiConfig cfg;
460 1 : fix_session(&s); fix_transport(&t); fix_cfg(&cfg);
461 :
462 1 : DialogEntry entries[5] = {0};
463 1 : int count = 0;
464 1 : int rc = domain_get_dialogs(&cfg, &s, &t, 5, 1 /* archived */, entries, &count, NULL);
465 1 : ASSERT(rc == 0, "archived dialogs: call succeeds");
466 :
467 : /* Inspect the wire bytes for: CRC(4) + flags=2(4) + folder_id=1(4) */
468 : static const uint8_t crc_le[4] = {0x4f, 0xcb, 0xf4, 0xa0};
469 : static const uint8_t flags_le[4] = {0x02, 0x00, 0x00, 0x00};
470 : static const uint8_t folder_id_le[4] = {0x01, 0x00, 0x00, 0x00};
471 :
472 1 : size_t sent_len = 0;
473 1 : const uint8_t *sent = mock_socket_get_sent(&sent_len);
474 1 : ASSERT(sent != NULL && sent_len > 0, "client transmitted bytes");
475 :
476 1 : int found = 0;
477 114 : for (size_t i = 0; i + 12 <= sent_len; i++) {
478 114 : if (memcmp(sent + i, crc_le, 4) == 0 &&
479 1 : memcmp(sent + i + 4, flags_le, 4) == 0 &&
480 1 : memcmp(sent + i + 8, folder_id_le, 4) == 0) {
481 1 : found = 1;
482 1 : break;
483 : }
484 : }
485 1 : ASSERT(found, "folder_id=1 appears on wire when archived=1");
486 : }
487 :
488 : /* Inverse: when archived=0, flags=0 and no folder_id in the outbound buffer. */
489 1 : static void test_dialogs_default_no_folder_id_on_wire(void) {
490 1 : mock_socket_reset(); mock_crypto_reset(); dialogs_cache_flush();
491 :
492 1 : TlWriter pw; tl_writer_init(&pw);
493 1 : tl_write_uint32(&pw, TL_messages_dialogs);
494 1 : tl_write_uint32(&pw, TL_vector); tl_write_uint32(&pw, 0);
495 1 : tl_write_uint32(&pw, TL_vector); tl_write_uint32(&pw, 0);
496 1 : tl_write_uint32(&pw, TL_vector); tl_write_uint32(&pw, 0);
497 1 : tl_write_uint32(&pw, TL_vector); tl_write_uint32(&pw, 0);
498 1 : uint8_t resp[512]; size_t rlen = 0;
499 1 : build_fake_encrypted_response(pw.data, pw.len, resp, &rlen);
500 1 : tl_writer_free(&pw);
501 1 : mock_socket_set_response(resp, rlen);
502 :
503 : MtProtoSession s; Transport t; ApiConfig cfg;
504 1 : fix_session(&s); fix_transport(&t); fix_cfg(&cfg);
505 :
506 1 : DialogEntry entries[5] = {0};
507 1 : int count = 0;
508 1 : int rc = domain_get_dialogs(&cfg, &s, &t, 5, 0 /* not archived */, entries, &count, NULL);
509 1 : ASSERT(rc == 0, "default dialogs: call succeeds");
510 :
511 : /* flags must be 0 (not 2) right after the CRC */
512 : static const uint8_t crc_le[4] = {0x4f, 0xcb, 0xf4, 0xa0};
513 : static const uint8_t flags_zero_le[4] = {0x00, 0x00, 0x00, 0x00};
514 :
515 1 : size_t sent_len = 0;
516 1 : const uint8_t *sent = mock_socket_get_sent(&sent_len);
517 1 : ASSERT(sent != NULL && sent_len > 0, "client transmitted bytes");
518 :
519 1 : int found = 0;
520 114 : for (size_t i = 0; i + 8 <= sent_len; i++) {
521 114 : if (memcmp(sent + i, crc_le, 4) == 0 &&
522 1 : memcmp(sent + i + 4, flags_zero_le, 4) == 0) {
523 1 : found = 1;
524 1 : break;
525 : }
526 : }
527 1 : ASSERT(found, "flags=0 (no folder_id) on wire when archived=0");
528 : }
529 :
530 1 : void run_domain_dialogs_tests(void) {
531 1 : RUN_TEST(test_dialogs_title_join_user);
532 1 : RUN_TEST(test_dialogs_user_access_hash_threaded);
533 1 : RUN_TEST(test_dialogs_channel_access_hash_threaded);
534 1 : RUN_TEST(test_dialogs_multi_entries);
535 1 : RUN_TEST(test_dialogs_single_user);
536 1 : RUN_TEST(test_dialogs_single_channel);
537 1 : RUN_TEST(test_dialogs_rpc_error);
538 1 : RUN_TEST(test_dialogs_unexpected_top);
539 1 : RUN_TEST(test_dialogs_null_args);
540 1 : RUN_TEST(test_dialogs_archived_folder_id_on_wire);
541 1 : RUN_TEST(test_dialogs_default_no_folder_id_on_wire);
542 1 : }
|