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

            Line data    Source code
       1              : /**
       2              :  * @file test_dialogs_cache_ttl.c
       3              :  * @brief TEST-04 — dialogs cache TTL functional tests.
       4              :  *
       5              :  * Verifies that domain_get_dialogs:
       6              :  *   1. Serves a second consecutive call from the in-memory cache (no RPC).
       7              :  *   2. Issues a fresh RPC once the TTL has expired.
       8              :  *
       9              :  * The production `DIALOGS_CACHE_TTL_S` is 60 seconds.  Rather than
      10              :  * sleeping, we replace the TTL clock with a fake via
      11              :  * `dialogs_cache_set_now_fn()` and advance it explicitly.
      12              :  */
      13              : 
      14              : #include "test_helpers.h"
      15              : 
      16              : #include "mock_socket.h"
      17              : #include "mock_tel_server.h"
      18              : 
      19              : #include "api_call.h"
      20              : #include "mtproto_session.h"
      21              : #include "transport.h"
      22              : #include "app/session_store.h"
      23              : #include "tl_registry.h"
      24              : #include "tl_serial.h"
      25              : #include "domain/read/dialogs.h"
      26              : 
      27              : #include <stdio.h>
      28              : #include <stdlib.h>
      29              : #include <string.h>
      30              : #include <unistd.h>
      31              : #include <time.h>
      32              : 
      33              : /* ---- CRCs ---- */
      34              : #define CRC_messages_getDialogs  0xa0f4cb4fU
      35              : #define CRC_dialog               0xd58a08c6U
      36              : #define CRC_peerNotifySettings   0xa83b0426U
      37              : 
      38              : /* ---- Fake clock ---- */
      39              : 
      40              : static time_t s_fake_time = 0;
      41              : 
      42           10 : static time_t fake_now(void) { return s_fake_time; }
      43              : 
      44              : /* ---- Helpers ---- */
      45              : 
      46            2 : static void with_tmp_home(const char *tag) {
      47              :     char tmp[256];
      48            2 :     snprintf(tmp, sizeof(tmp), "/tmp/tg-cli-ft-dlg-ttl-%s", tag);
      49              :     char bin[512];
      50            2 :     snprintf(bin, sizeof(bin), "%s/.config/tg-cli/session.bin", tmp);
      51            2 :     (void)unlink(bin);
      52            2 :     setenv("HOME", tmp, 1);
      53            2 : }
      54              : 
      55            2 : static void connect_mock(Transport *t) {
      56            2 :     transport_init(t);
      57            2 :     ASSERT(transport_connect(t, "127.0.0.1", 443) == 0, "connect");
      58              : }
      59              : 
      60            2 : static void init_cfg(ApiConfig *cfg) {
      61            2 :     api_config_init(cfg);
      62            2 :     cfg->api_id = 12345;
      63            2 :     cfg->api_hash = "deadbeefcafebabef00dbaadfeedc0de";
      64            2 : }
      65              : 
      66            2 : static void load_session(MtProtoSession *s) {
      67            2 :     ASSERT(mt_server_seed_session(2, NULL, NULL, NULL) == 0, "seed");
      68            2 :     mtproto_session_init(s);
      69            2 :     int dc = 0;
      70            2 :     ASSERT(session_store_load(s, &dc) == 0, "load session");
      71              : }
      72              : 
      73              : /* ---- Responder: one user dialog ---- */
      74              : 
      75            4 : static void on_dialogs_one(MtRpcContext *ctx) {
      76              :     TlWriter w;
      77            4 :     tl_writer_init(&w);
      78            4 :     tl_write_uint32(&w, TL_messages_dialogs);
      79              : 
      80            4 :     tl_write_uint32(&w, TL_vector);
      81            4 :     tl_write_uint32(&w, 1);
      82            4 :     tl_write_uint32(&w, CRC_dialog);
      83            4 :     tl_write_uint32(&w, 0);                  /* flags=0 */
      84            4 :     tl_write_uint32(&w, TL_peerUser);
      85            4 :     tl_write_int64 (&w, 42LL);               /* peer_id */
      86            4 :     tl_write_int32 (&w, 100);               /* top_message */
      87            4 :     tl_write_int32 (&w, 0);                  /* read_inbox_max_id */
      88            4 :     tl_write_int32 (&w, 0);                  /* read_outbox_max_id */
      89            4 :     tl_write_int32 (&w, 5);                  /* unread_count */
      90            4 :     tl_write_int32 (&w, 0);                  /* unread_mentions_count */
      91            4 :     tl_write_int32 (&w, 0);                  /* unread_reactions_count */
      92            4 :     tl_write_uint32(&w, CRC_peerNotifySettings);
      93            4 :     tl_write_uint32(&w, 0);
      94              : 
      95            4 :     tl_write_uint32(&w, TL_vector); tl_write_uint32(&w, 0); /* messages */
      96            4 :     tl_write_uint32(&w, TL_vector); tl_write_uint32(&w, 0); /* chats */
      97            4 :     tl_write_uint32(&w, TL_vector); tl_write_uint32(&w, 0); /* users */
      98              : 
      99            4 :     mt_server_reply_result(ctx, w.data, w.len);
     100            4 :     tl_writer_free(&w);
     101            4 : }
     102              : 
     103              : /* ================================================================ */
     104              : /* Tests                                                            */
     105              : /* ================================================================ */
     106              : 
     107              : /**
     108              :  * @brief First assert: two consecutive calls within TTL fire exactly ONE RPC.
     109              :  *        Second assert: a third call after TTL expiry fires a second RPC.
     110              :  */
     111            2 : static void test_dialogs_cache_ttl(void) {
     112            2 :     with_tmp_home("ttl");
     113            2 :     mt_server_init(); mt_server_reset();
     114            2 :     MtProtoSession s; load_session(&s);
     115              : 
     116              :     /* Arm the fake clock at t=0. */
     117            2 :     s_fake_time = 1000;
     118            2 :     dialogs_cache_set_now_fn(fake_now);
     119            2 :     dialogs_cache_flush();
     120              : 
     121            2 :     mt_server_expect(CRC_messages_getDialogs, on_dialogs_one, NULL);
     122              : 
     123            2 :     ApiConfig cfg; init_cfg(&cfg);
     124            2 :     Transport t; connect_mock(&t);
     125              : 
     126              :     DialogEntry rows[8];
     127            2 :     int n = 0;
     128              : 
     129              :     /* ---- Call 1: cold cache — must hit the mock server. ---- */
     130            2 :     ASSERT(domain_get_dialogs(&cfg, &s, &t, 8, 0, rows, &n, NULL) == 0,
     131              :            "call-1 ok");
     132            2 :     ASSERT(n == 1,                    "call-1 returns 1 dialog");
     133            2 :     ASSERT(rows[0].peer_id == 42LL,   "call-1 peer_id==42");
     134            2 :     ASSERT(mt_server_rpc_call_count() == 1, "call-1 RPC count == 1");
     135              : 
     136              :     /* ---- Call 2: within TTL — must be served from cache, no new RPC. ---- */
     137            2 :     n = 0;
     138            2 :     s_fake_time = 1030; /* +30 s, still within 60-s TTL */
     139            2 :     ASSERT(domain_get_dialogs(&cfg, &s, &t, 8, 0, rows, &n, NULL) == 0,
     140              :            "call-2 ok");
     141            2 :     ASSERT(n == 1,                    "call-2 returns 1 dialog from cache");
     142            2 :     ASSERT(rows[0].peer_id == 42LL,   "call-2 peer_id==42 (from cache)");
     143            2 :     ASSERT(mt_server_rpc_call_count() == 1, "call-2 still only 1 RPC total");
     144              : 
     145              :     /* ---- Call 3: advance clock past TTL — must hit the mock server again. ---- */
     146            2 :     n = 0;
     147            2 :     s_fake_time = 1070; /* +70 s from fetch time → TTL expired */
     148            2 :     ASSERT(domain_get_dialogs(&cfg, &s, &t, 8, 0, rows, &n, NULL) == 0,
     149              :            "call-3 ok");
     150            2 :     ASSERT(n == 1,                    "call-3 returns 1 dialog (fresh RPC)");
     151            2 :     ASSERT(rows[0].peer_id == 42LL,   "call-3 peer_id==42");
     152            2 :     ASSERT(mt_server_rpc_call_count() == 2, "call-3 triggers second RPC");
     153              : 
     154              :     /* Restore real clock. */
     155            2 :     dialogs_cache_set_now_fn(NULL);
     156              : 
     157            2 :     transport_close(&t);
     158            2 :     mt_server_reset();
     159              : }
     160              : 
     161            2 : void run_dialogs_cache_ttl_tests(void) {
     162            2 :     RUN_TEST(test_dialogs_cache_ttl);
     163            2 : }
        

Generated by: LCOV version 2.0-1