Line data Source code
1 : /* SPDX-License-Identifier: GPL-3.0-or-later */
2 : /* Copyright 2026 Peter Csaszar */
3 :
4 : /**
5 : * @file test_config_wizard_pty.c
6 : * @brief PTY functional test for the interactive config wizard (FEAT-37).
7 : *
8 : * Launches `tg-cli login` under a PTY and verifies:
9 : * 1. The welcome text appears.
10 : * 2. The api_hash prompt is present.
11 : * 3. Characters typed at the api_hash prompt do NOT echo back to the master.
12 : * 4. After submitting valid values the binary prints "Saved to …/config.ini"
13 : * and exits with code 0.
14 : * 5. The generated config.ini has mode 0600.
15 : */
16 :
17 : #include "ptytest.h"
18 : #include "pty_assert.h"
19 :
20 : #include <stdio.h>
21 : #include <stdlib.h>
22 : #include <string.h>
23 : #include <sys/stat.h>
24 : #include <unistd.h>
25 :
26 : /* Defined by CMake: path to the tg-cli binary under test. */
27 : #ifndef TG_CLI_BINARY
28 : # error "TG_CLI_BINARY must be defined by CMake"
29 : #endif
30 :
31 : /* ---- helpers ---- */
32 :
33 : /** Unique temp dir per process for XDG_CONFIG_HOME. */
34 : static char g_tmp_dir[256];
35 :
36 2 : static void setup_tmp_dir(void) {
37 2 : const char *base = getenv("TMPDIR");
38 2 : if (!base) base = "/tmp";
39 2 : snprintf(g_tmp_dir, sizeof(g_tmp_dir),
40 2 : "%s/tg-cli-pty-wiz-%d", base, (int)getpid());
41 : /* mkdir -p */
42 : char cmd[512];
43 2 : snprintf(cmd, sizeof(cmd), "mkdir -p %s", g_tmp_dir);
44 2 : int rc = system(cmd); (void)rc;
45 2 : }
46 :
47 1 : static void cleanup_tmp_dir(void) {
48 : char cmd[512];
49 1 : snprintf(cmd, sizeof(cmd), "rm -rf %s", g_tmp_dir);
50 1 : int rc = system(cmd); (void)rc;
51 1 : }
52 :
53 : static int g_tests_run = 0;
54 : static int g_tests_failed = 0;
55 :
56 : /* ── Tests ─────────────────────────────────────────────────────────── */
57 :
58 : /**
59 : * @brief Happy-path PTY test.
60 : *
61 : * Launches `tg-cli login` with XDG_CONFIG_HOME pointed at a temp dir,
62 : * feeds valid api_id (without echo suppression) then api_hash (with echo
63 : * suppression), and checks:
64 : * - Welcome text visible.
65 : * - api_hash typed chars NOT echoed (the hash string must NOT appear
66 : * anywhere on screen after typing it).
67 : * - "Saved to" line appears.
68 : * - Exit code 0.
69 : * - config.ini has mode 0600.
70 : */
71 2 : static void test_wizard_pty_happy(void) {
72 2 : setup_tmp_dir();
73 :
74 : /* Point XDG_CONFIG_HOME to our temp dir so we don't touch the real one. */
75 2 : setenv("XDG_CONFIG_HOME", g_tmp_dir, 1);
76 :
77 2 : PtySession *s = pty_open(100, 30);
78 2 : ASSERT(s != NULL, "pty_open must succeed");
79 :
80 2 : const char *argv[] = { TG_CLI_BINARY, "login", NULL };
81 2 : int rc = pty_run(s, argv);
82 1 : ASSERT(rc == 0, "pty_run(tg-cli login) must succeed");
83 :
84 : /* Wait for welcome text. */
85 1 : int found = pty_wait_for(s, "Welcome to tg-cli", 5000);
86 1 : ASSERT(found == 0, "welcome text must appear on screen");
87 :
88 : /* Wait for the api_id prompt. */
89 1 : found = pty_wait_for(s, "Enter your api_id", 5000);
90 1 : ASSERT(found == 0, "api_id prompt must appear");
91 :
92 : /* Type a valid api_id followed by Enter. */
93 1 : pty_send(s, "99999\r", 6);
94 :
95 : /* Wait for the api_hash prompt. */
96 1 : found = pty_wait_for(s, "Enter your api_hash", 5000);
97 1 : ASSERT(found == 0, "api_hash prompt must appear");
98 :
99 : /* Type a valid api_hash (32 lowercase hex chars). */
100 1 : const char *hash = "abcdef0123456789abcdef0123456789";
101 1 : pty_send(s, hash, 32);
102 : /* Give the terminal a moment to process then check no echo. */
103 1 : pty_wait_for(s, "Saved to", 200); /* short timeout — probably not there yet */
104 :
105 : /* The hash should NOT appear in the screen buffer. */
106 1 : int hash_visible = pty_screen_contains(s, hash);
107 1 : ASSERT(hash_visible == 0,
108 : "api_hash must NOT echo: secret string must not appear on screen");
109 :
110 : /* Send Enter to submit. */
111 1 : pty_send(s, "\r", 1);
112 :
113 : /* Wait for "Saved to" confirmation. */
114 1 : found = pty_wait_for(s, "Saved to", 5000);
115 1 : ASSERT(found == 0, "\"Saved to\" must appear after valid input");
116 :
117 : /* Wait for the process to exit. */
118 1 : int exit_code = pty_wait_exit(s, 5000);
119 1 : ASSERT(exit_code == 0, "tg-cli login must exit with code 0");
120 :
121 : /* Verify config.ini mode 0600. */
122 : char cfg_path[512];
123 1 : snprintf(cfg_path, sizeof(cfg_path), "%s/tg-cli/config.ini", g_tmp_dir);
124 : struct stat st;
125 1 : ASSERT(stat(cfg_path, &st) == 0, "config.ini must exist after wizard");
126 1 : ASSERT((st.st_mode & 0777) == 0600, "config.ini must have mode 0600");
127 :
128 1 : pty_close(s);
129 1 : cleanup_tmp_dir();
130 1 : unsetenv("XDG_CONFIG_HOME");
131 : }
132 :
133 : /* ── Entry point ─────────────────────────────────────────────────────── */
134 :
135 2 : int main(void) {
136 2 : printf("PTY config wizard tests (%s)\n", TG_CLI_BINARY);
137 :
138 2 : test_wizard_pty_happy();
139 :
140 1 : printf("\n%d tests run, %d failed\n", g_tests_run, g_tests_failed);
141 1 : return g_tests_failed > 0 ? 1 : 0;
142 : }
|