Line data Source code
1 : /**
2 : * @file test_dc_session.c
3 : * @brief Unit tests for app/dc_session.
4 : *
5 : * Fast-path coverage only: when session_store has a cached auth_key for
6 : * the target DC, dc_session_open() must skip the DH handshake and just
7 : * open the TCP transport. The slow path (real DH exchange) is covered
8 : * by the integration path in auth_flow tests.
9 : */
10 :
11 : #include "test_helpers.h"
12 : #include "app/dc_session.h"
13 : #include "app/session_store.h"
14 : #include "mock_socket.h"
15 :
16 : #include <stdlib.h>
17 : #include <string.h>
18 : #include <unistd.h>
19 :
20 3 : static void with_tmp_home(const char *subdir) {
21 : char tmp[256];
22 3 : snprintf(tmp, sizeof(tmp), "/tmp/tg-cli-dc-session-test-%s", subdir);
23 : char path[512];
24 3 : snprintf(path, sizeof(path), "%s/.config/tg-cli/session.bin", tmp);
25 3 : (void)unlink(path);
26 3 : setenv("HOME", tmp, 1);
27 3 : }
28 :
29 2 : static void seed_cached_session(int dc_id, uint64_t salt,
30 : const uint8_t key[256]) {
31 : MtProtoSession s;
32 2 : mtproto_session_init(&s);
33 2 : mtproto_session_set_auth_key(&s, key);
34 2 : mtproto_session_set_salt(&s, salt);
35 2 : s.session_id = 0xCCCCCCCCCCCCCCCCULL;
36 2 : (void)session_store_save_dc(dc_id, &s);
37 2 : }
38 :
39 1 : static void test_fast_path_reuses_cached_key(void) {
40 1 : with_tmp_home("fastpath");
41 1 : mock_socket_reset();
42 :
43 : uint8_t key[256];
44 257 : for (int i = 0; i < 256; i++) key[i] = (uint8_t)(i * 13 + 7);
45 1 : seed_cached_session(2, 0xF00DULL, key);
46 :
47 : DcSession out;
48 1 : ASSERT(dc_session_open(2, &out) == 0, "fast-path open succeeds");
49 1 : ASSERT(out.dc_id == 2, "dc_id recorded");
50 1 : ASSERT(out.transport.dc_id == 2, "transport tagged with dc_id");
51 1 : ASSERT(out.session.has_auth_key == 1, "session has auth_key");
52 1 : ASSERT(memcmp(out.session.auth_key, key, 256) == 0,
53 : "reused auth_key matches persisted copy");
54 1 : ASSERT(out.session.server_salt == 0xF00DULL, "salt restored");
55 1 : ASSERT(out.authorized == 1,
56 : "cached key is treated as already authorized");
57 1 : ASSERT(mock_socket_was_connected() == 1, "transport connected");
58 :
59 1 : size_t sent_len = 0;
60 1 : (void)mock_socket_get_sent(&sent_len);
61 1 : ASSERT(sent_len == 1, "fast path sends only the 0xef abridged prefix");
62 :
63 1 : dc_session_close(&out);
64 1 : ASSERT(mock_socket_was_closed() == 1, "close tears down transport");
65 :
66 1 : session_store_clear();
67 : }
68 :
69 1 : static void test_unknown_dc_fails(void) {
70 1 : with_tmp_home("unknown-dc");
71 1 : mock_socket_reset();
72 :
73 : DcSession out;
74 1 : ASSERT(dc_session_open(99, &out) == -1, "unknown DC rejected");
75 : }
76 :
77 1 : static void test_null_out_rejected(void) {
78 1 : ASSERT(dc_session_open(2, NULL) == -1, "null out param rejected");
79 1 : dc_session_close(NULL); /* must not crash */
80 : }
81 :
82 1 : static void test_cached_but_connect_fails_bails_out(void) {
83 1 : with_tmp_home("connect-fail");
84 1 : mock_socket_reset();
85 :
86 1 : uint8_t key[256] = {0};
87 1 : seed_cached_session(2, 0xBEEFULL, key);
88 :
89 1 : mock_socket_fail_connect();
90 :
91 : DcSession out;
92 : /* Fast path falls back to full handshake; handshake also fails because
93 : * the mock has no queued server response. So we expect -1 here. */
94 1 : ASSERT(dc_session_open(2, &out) == -1,
95 : "cached DC + connect failure bails (no stray success)");
96 :
97 1 : session_store_clear();
98 : }
99 :
100 1 : void run_dc_session_tests(void) {
101 1 : RUN_TEST(test_fast_path_reuses_cached_key);
102 1 : RUN_TEST(test_unknown_dc_fails);
103 1 : RUN_TEST(test_null_out_rejected);
104 1 : RUN_TEST(test_cached_but_connect_fails_bails_out);
105 1 : }
|