LCOV - code coverage report
Current view: top level - tests/unit - test_domain_dialogs.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 349 349
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 19 19

            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 : }
        

Generated by: LCOV version 2.0-1