Line data Source code
1 : #include "test_helpers.h"
2 : #include "config_store.h"
3 : #include "fs_util.h"
4 : #include "logger.h"
5 : #include "raii.h"
6 : #include <string.h>
7 : #include <stdlib.h>
8 : #include <unistd.h>
9 : #include <sys/stat.h>
10 :
11 10 : static void config_cleanup(void *ptr) {
12 10 : Config **cfg = (Config **)ptr;
13 10 : if (cfg && *cfg) {
14 5 : config_free(*cfg);
15 5 : *cfg = NULL;
16 : }
17 10 : }
18 :
19 1 : void test_config_store(void) {
20 1 : char *old_home = getenv("HOME");
21 1 : char *old_xdg = getenv("XDG_CONFIG_HOME");
22 1 : setenv("HOME", "/tmp/email-cli-test-home", 1);
23 1 : unsetenv("XDG_CONFIG_HOME");
24 : /* Pre-clean: remove any leftover account from previous test runs */
25 1 : unlink("/tmp/email-cli-test-home/.config/email-cli/accounts/test@test.com/config.ini");
26 1 : rmdir("/tmp/email-cli-test-home/.config/email-cli/accounts/test@test.com");
27 1 : rmdir("/tmp/email-cli-test-home/.config/email-cli/accounts");
28 :
29 : // 1. Test Save
30 : {
31 1 : Config cfg = {0};
32 1 : cfg.host = strdup("imaps://imap.test.com");
33 1 : cfg.user = strdup("test@test.com");
34 1 : cfg.pass = strdup("password123");
35 1 : cfg.folder = strdup("INBOX");
36 :
37 1 : int res = config_save_to_store(&cfg);
38 1 : ASSERT(res == 0, "config_save_to_store should return 0");
39 :
40 1 : free(cfg.host);
41 1 : free(cfg.user);
42 1 : free(cfg.pass);
43 1 : free(cfg.folder);
44 : }
45 :
46 : // 2. Test Load - full config
47 : {
48 2 : RAII_WITH_CLEANUP(config_cleanup) Config *loaded = config_load_from_store();
49 1 : ASSERT(loaded != NULL, "config_load_from_store should not return NULL");
50 1 : ASSERT(strcmp(loaded->host, "imaps://imap.test.com") == 0, "Host should match");
51 1 : ASSERT(strcmp(loaded->user, "test@test.com") == 0, "User should match");
52 1 : ASSERT(strcmp(loaded->pass, "password123") == 0, "Pass should match");
53 : }
54 :
55 : // 3. Test Load - incomplete config (missing host) → should return NULL
56 : {
57 1 : const char *acct_path =
58 : "/tmp/email-cli-test-home/.config/email-cli/accounts/test@test.com/config.ini";
59 1 : FILE *fp = fopen(acct_path, "w");
60 1 : if (fp) {
61 1 : fprintf(fp, "EMAIL_USER=test@test.com\n");
62 1 : fprintf(fp, "EMAIL_PASS=password123\n");
63 1 : fclose(fp);
64 : }
65 2 : RAII_WITH_CLEANUP(config_cleanup) Config *loaded = config_load_from_store();
66 1 : ASSERT(loaded == NULL, "config_load_from_store should return NULL for incomplete config");
67 : }
68 :
69 : // 4. Test Load - no config file → should return NULL
70 : {
71 1 : unlink("/tmp/email-cli-test-home/.config/email-cli/accounts/test@test.com/config.ini");
72 2 : RAII_WITH_CLEANUP(config_cleanup) Config *loaded = config_load_from_store();
73 1 : ASSERT(loaded == NULL, "config_load_from_store should return NULL when file is missing");
74 : }
75 :
76 : // 5. Test Save/Load with ssl_no_verify=1
77 : {
78 1 : Config cfg2 = {0};
79 1 : cfg2.host = strdup("imaps://imap.test.com");
80 1 : cfg2.user = strdup("test@test.com");
81 1 : cfg2.pass = strdup("password123");
82 1 : cfg2.folder = strdup("INBOX");
83 1 : cfg2.ssl_no_verify = 1;
84 :
85 1 : int res = config_save_to_store(&cfg2);
86 1 : ASSERT(res == 0, "config_save_to_store with ssl_no_verify=1 should return 0");
87 :
88 2 : RAII_WITH_CLEANUP(config_cleanup) Config *loaded2 = config_load_from_store();
89 1 : ASSERT(loaded2 != NULL, "config_load_from_store should not return NULL");
90 1 : ASSERT(loaded2->ssl_no_verify == 1, "ssl_no_verify should be 1 after load");
91 :
92 1 : free(cfg2.host);
93 1 : free(cfg2.user);
94 1 : free(cfg2.pass);
95 1 : free(cfg2.folder);
96 : }
97 :
98 : // 6. Test Load - host without protocol prefix → should return NULL with error
99 : {
100 1 : const char *acct_path =
101 : "/tmp/email-cli-test-home/.config/email-cli/accounts/test@test.com/config.ini";
102 1 : FILE *fp = fopen(acct_path, "w");
103 1 : if (fp) {
104 1 : fprintf(fp, "EMAIL_HOST=box.example.com\n");
105 1 : fprintf(fp, "EMAIL_USER=test@test.com\n");
106 1 : fprintf(fp, "EMAIL_PASS=password123\n");
107 1 : fclose(fp);
108 : }
109 2 : RAII_WITH_CLEANUP(config_cleanup) Config *loaded = config_load_from_store();
110 1 : ASSERT(loaded == NULL, "config_load_from_store should return NULL for host without protocol");
111 1 : unlink(acct_path);
112 : }
113 :
114 : // 7. Test config_free with NULL (should not crash)
115 1 : config_free(NULL);
116 :
117 : // 8. config_save_to_store fails: fs_mkdir_p fails
118 : // Make .config directory non-writable so subdirectory creation fails
119 : {
120 1 : setenv("HOME", "/tmp/email-cli-test-home-fail", 1);
121 1 : fs_mkdir_p("/tmp/email-cli-test-home-fail/.config", 0700);
122 1 : chmod("/tmp/email-cli-test-home-fail/.config", 0000);
123 :
124 1 : Config cfg_fail = {0};
125 1 : cfg_fail.host = strdup("imaps://imap.test.com");
126 1 : cfg_fail.user = strdup("test@test.com");
127 1 : cfg_fail.pass = strdup("password123");
128 1 : cfg_fail.folder = strdup("INBOX");
129 :
130 1 : int res = config_save_to_store(&cfg_fail);
131 1 : ASSERT(res == -1, "config_save_to_store: mkdir fail should return -1");
132 :
133 1 : chmod("/tmp/email-cli-test-home-fail/.config", 0700);
134 1 : free(cfg_fail.host);
135 1 : free(cfg_fail.user);
136 1 : free(cfg_fail.pass);
137 1 : free(cfg_fail.folder);
138 : }
139 :
140 : // 9. config_save_to_store fails: fopen fails
141 : // Create account dir, then place a directory at the config.ini path
142 : {
143 1 : setenv("HOME", "/tmp/email-cli-test-home-fopen", 1);
144 1 : fs_mkdir_p("/tmp/email-cli-test-home-fopen/.config/email-cli/accounts/test@test.com",
145 : 0700);
146 : // Create a directory at the config file path to make fopen("w") fail
147 1 : mkdir("/tmp/email-cli-test-home-fopen/.config/email-cli/accounts/test@test.com/config.ini",
148 : 0700);
149 :
150 1 : Config cfg_fopen = {0};
151 1 : cfg_fopen.host = strdup("imaps://imap.test.com");
152 1 : cfg_fopen.user = strdup("test@test.com");
153 1 : cfg_fopen.pass = strdup("password123");
154 1 : cfg_fopen.folder = strdup("INBOX");
155 :
156 1 : int res = config_save_to_store(&cfg_fopen);
157 1 : ASSERT(res == -1, "config_save_to_store: fopen on dir should return -1");
158 :
159 1 : rmdir("/tmp/email-cli-test-home-fopen/.config/email-cli/accounts/test@test.com/config.ini");
160 1 : free(cfg_fopen.host);
161 1 : free(cfg_fopen.user);
162 1 : free(cfg_fopen.pass);
163 1 : free(cfg_fopen.folder);
164 : }
165 :
166 : // 10. SMTP fields round-trip (covers lines 79-82 in config_store.c)
167 : {
168 1 : setenv("HOME", "/tmp/email-cli-test-home", 1);
169 1 : const char *acct_path =
170 : "/tmp/email-cli-test-home/.config/email-cli/accounts/test@test.com/config.ini";
171 1 : FILE *fp = fopen(acct_path, "w");
172 1 : if (fp) {
173 1 : fprintf(fp, "EMAIL_HOST=imaps://imap.test.com\n");
174 1 : fprintf(fp, "EMAIL_USER=test@test.com\n");
175 1 : fprintf(fp, "EMAIL_PASS=password123\n");
176 1 : fprintf(fp, "SMTP_HOST=smtps://smtp.test.com\n");
177 1 : fprintf(fp, "SMTP_PORT=465\n");
178 1 : fprintf(fp, "SMTP_USER=test@test.com\n");
179 1 : fprintf(fp, "SMTP_PASS=smtppass\n");
180 1 : fclose(fp);
181 : }
182 2 : RAII_WITH_CLEANUP(config_cleanup) Config *loaded = config_load_from_store();
183 1 : ASSERT(loaded != NULL, "smtp fields: should load");
184 1 : if (loaded) {
185 1 : ASSERT(loaded->smtp_host && strcmp(loaded->smtp_host, "smtps://smtp.test.com") == 0,
186 : "smtp fields: smtp_host matches");
187 1 : ASSERT(loaded->smtp_port == 465, "smtp fields: smtp_port matches");
188 1 : ASSERT(loaded->smtp_user && strcmp(loaded->smtp_user, "test@test.com") == 0,
189 : "smtp fields: smtp_user matches");
190 1 : ASSERT(loaded->smtp_pass && strcmp(loaded->smtp_pass, "smtppass") == 0,
191 : "smtp fields: smtp_pass matches");
192 : }
193 1 : unlink(acct_path);
194 : }
195 :
196 : // 11. Gmail mode config load (covers lines 83-86 in config_store.c)
197 : {
198 1 : setenv("HOME", "/tmp/email-cli-test-home", 1);
199 1 : const char *acct_path =
200 : "/tmp/email-cli-test-home/.config/email-cli/accounts/test@test.com/config.ini";
201 1 : FILE *fp = fopen(acct_path, "w");
202 1 : if (fp) {
203 1 : fprintf(fp, "EMAIL_USER=gmailuser@gmail.com\n");
204 1 : fprintf(fp, "GMAIL_MODE=1\n");
205 1 : fprintf(fp, "GMAIL_REFRESH_TOKEN=myrefreshtoken\n");
206 1 : fprintf(fp, "GMAIL_CLIENT_ID=myclientid\n");
207 1 : fprintf(fp, "GMAIL_CLIENT_SECRET=myclientsecret\n");
208 1 : fclose(fp);
209 : }
210 2 : RAII_WITH_CLEANUP(config_cleanup) Config *loaded = config_load_from_store();
211 1 : ASSERT(loaded != NULL, "gmail mode: should load");
212 1 : if (loaded) {
213 1 : ASSERT(loaded->gmail_mode == 1, "gmail mode: gmail_mode=1");
214 1 : ASSERT(loaded->gmail_refresh_token &&
215 : strcmp(loaded->gmail_refresh_token, "myrefreshtoken") == 0,
216 : "gmail mode: refresh token matches");
217 1 : ASSERT(loaded->gmail_client_id &&
218 : strcmp(loaded->gmail_client_id, "myclientid") == 0,
219 : "gmail mode: client_id matches");
220 1 : ASSERT(loaded->gmail_client_secret &&
221 : strcmp(loaded->gmail_client_secret, "myclientsecret") == 0,
222 : "gmail mode: client_secret matches");
223 : }
224 1 : unlink(acct_path);
225 : }
226 :
227 : // 12. Gmail mode: missing refresh token → NULL (covers line 92)
228 : {
229 1 : setenv("HOME", "/tmp/email-cli-test-home", 1);
230 1 : const char *acct_path =
231 : "/tmp/email-cli-test-home/.config/email-cli/accounts/test@test.com/config.ini";
232 1 : FILE *fp = fopen(acct_path, "w");
233 1 : if (fp) {
234 1 : fprintf(fp, "EMAIL_USER=gmailuser@gmail.com\n");
235 1 : fprintf(fp, "GMAIL_MODE=1\n");
236 : /* no GMAIL_REFRESH_TOKEN */
237 1 : fclose(fp);
238 : }
239 2 : RAII_WITH_CLEANUP(config_cleanup) Config *loaded = config_load_from_store();
240 1 : ASSERT(loaded == NULL,
241 : "gmail missing token: config_load should return NULL");
242 1 : unlink(acct_path);
243 : }
244 :
245 : // 13. SMTP host without smtps:// prefix → rejected (covers lines 110-118)
246 : {
247 1 : setenv("HOME", "/tmp/email-cli-test-home", 1);
248 1 : const char *acct_path =
249 : "/tmp/email-cli-test-home/.config/email-cli/accounts/test@test.com/config.ini";
250 1 : FILE *fp = fopen(acct_path, "w");
251 1 : if (fp) {
252 1 : fprintf(fp, "EMAIL_HOST=imaps://imap.test.com\n");
253 1 : fprintf(fp, "EMAIL_USER=test@test.com\n");
254 1 : fprintf(fp, "EMAIL_PASS=password123\n");
255 1 : fprintf(fp, "SMTP_HOST=smtp.test.com\n"); /* missing smtps:// */
256 1 : fclose(fp);
257 : }
258 2 : RAII_WITH_CLEANUP(config_cleanup) Config *loaded = config_load_from_store();
259 1 : ASSERT(loaded == NULL,
260 : "insecure smtp host: config_load should return NULL");
261 1 : unlink(acct_path);
262 : }
263 :
264 : // 14. ssl_no_verify=1 with non-TLS host and SMTP → warnings logged
265 : // (covers lines 124 and 128-129)
266 : {
267 1 : setenv("HOME", "/tmp/email-cli-test-home", 1);
268 1 : const char *acct_path =
269 : "/tmp/email-cli-test-home/.config/email-cli/accounts/test@test.com/config.ini";
270 1 : FILE *fp = fopen(acct_path, "w");
271 1 : if (fp) {
272 1 : fprintf(fp, "EMAIL_HOST=imap.insecure.com\n");
273 1 : fprintf(fp, "EMAIL_USER=test@test.com\n");
274 1 : fprintf(fp, "EMAIL_PASS=password123\n");
275 1 : fprintf(fp, "SSL_NO_VERIFY=1\n");
276 1 : fprintf(fp, "SMTP_HOST=smtp.insecure.com\n");
277 1 : fclose(fp);
278 : }
279 2 : RAII_WITH_CLEANUP(config_cleanup) Config *loaded = config_load_from_store();
280 1 : ASSERT(loaded != NULL,
281 : "ssl_no_verify non-tls: should still load (warnings only)");
282 1 : if (loaded) {
283 1 : ASSERT(loaded->ssl_no_verify == 1, "ssl_no_verify non-tls: flag set");
284 : }
285 1 : unlink(acct_path);
286 : }
287 :
288 : // 15. config_delete_account: non-empty dir → rmdir fails, returns -1
289 : // (covers lines 194-195)
290 : {
291 1 : setenv("HOME", "/tmp/email-cli-test-home", 1);
292 1 : const char *dir =
293 : "/tmp/email-cli-test-home/.config/email-cli/accounts/nodelete@test.com";
294 1 : const char *extra_file =
295 : "/tmp/email-cli-test-home/.config/email-cli/accounts/nodelete@test.com/extra.txt";
296 1 : fs_mkdir_p(dir, 0700);
297 1 : FILE *fp = fopen(extra_file, "w");
298 1 : if (fp) { fprintf(fp, "extra\n"); fclose(fp); }
299 :
300 1 : int rc = config_delete_account("nodelete@test.com");
301 1 : ASSERT(rc == -1, "delete non-empty dir: should return -1");
302 :
303 : /* cleanup */
304 1 : unlink(extra_file);
305 1 : rmdir(dir);
306 : }
307 :
308 : // 16. Multiple accounts → sorting path exercised (covers lines 243-264)
309 : {
310 1 : setenv("HOME", "/tmp/email-cli-test-multiacct", 1);
311 1 : unsetenv("XDG_CONFIG_HOME");
312 1 : const char *base =
313 : "/tmp/email-cli-test-multiacct/.config/email-cli/accounts";
314 :
315 : /* Account 1: bob@beta.com — different domain */
316 : char path[256];
317 1 : snprintf(path, sizeof(path), "%s/bob@beta.com", base);
318 1 : fs_mkdir_p(path, 0700);
319 1 : snprintf(path, sizeof(path), "%s/bob@beta.com/config.ini", base);
320 : {
321 1 : FILE *fp = fopen(path, "w");
322 1 : if (fp) {
323 1 : fprintf(fp, "EMAIL_HOST=imaps://imap.beta.com\n");
324 1 : fprintf(fp, "EMAIL_USER=bob@beta.com\n");
325 1 : fprintf(fp, "EMAIL_PASS=pass1\n");
326 1 : fclose(fp);
327 : }
328 : }
329 : /* Account 2: alice@alpha.com — different domain */
330 1 : snprintf(path, sizeof(path), "%s/alice@alpha.com", base);
331 1 : fs_mkdir_p(path, 0700);
332 1 : snprintf(path, sizeof(path), "%s/alice@alpha.com/config.ini", base);
333 : {
334 1 : FILE *fp = fopen(path, "w");
335 1 : if (fp) {
336 1 : fprintf(fp, "EMAIL_HOST=imaps://imap.alpha.com\n");
337 1 : fprintf(fp, "EMAIL_USER=alice@alpha.com\n");
338 1 : fprintf(fp, "EMAIL_PASS=pass2\n");
339 1 : fclose(fp);
340 : }
341 : }
342 : /* Account 3: zara@alpha.com — same domain as alice → hits same-domain
343 : * sort branch (lines 256-259). zara > alice alphabetically, so alice
344 : * should sort before zara. */
345 1 : snprintf(path, sizeof(path), "%s/zara@alpha.com", base);
346 1 : fs_mkdir_p(path, 0700);
347 1 : snprintf(path, sizeof(path), "%s/zara@alpha.com/config.ini", base);
348 : {
349 1 : FILE *fp = fopen(path, "w");
350 1 : if (fp) {
351 1 : fprintf(fp, "EMAIL_HOST=imaps://imap.alpha.com\n");
352 1 : fprintf(fp, "EMAIL_USER=zara@alpha.com\n");
353 1 : fprintf(fp, "EMAIL_PASS=pass3\n");
354 1 : fclose(fp);
355 : }
356 : }
357 :
358 1 : int count = 0;
359 1 : AccountEntry *list = config_list_accounts(&count);
360 1 : ASSERT(count == 3, "multi-acct sort: count=3");
361 1 : if (list && count >= 3) {
362 : /* After sorting: alpha.com accounts first (alice, zara), then beta.com */
363 1 : ASSERT(strstr(list[0].name, "alpha.com") != NULL,
364 : "multi-acct sort: first account is alpha.com");
365 1 : ASSERT(strstr(list[1].name, "alpha.com") != NULL,
366 : "multi-acct sort: second account is alpha.com");
367 : /* Within alpha.com: alice < zara */
368 1 : ASSERT(strncmp(list[0].name, "alice", 5) == 0,
369 : "multi-acct sort: alice before zara");
370 1 : ASSERT(strstr(list[2].name, "beta.com") != NULL,
371 : "multi-acct sort: beta.com last");
372 : }
373 1 : config_free_account_list(list, count);
374 :
375 : /* cleanup */
376 1 : unlink("/tmp/email-cli-test-multiacct/.config/email-cli/accounts/bob@beta.com/config.ini");
377 1 : rmdir("/tmp/email-cli-test-multiacct/.config/email-cli/accounts/bob@beta.com");
378 1 : unlink("/tmp/email-cli-test-multiacct/.config/email-cli/accounts/alice@alpha.com/config.ini");
379 1 : rmdir("/tmp/email-cli-test-multiacct/.config/email-cli/accounts/alice@alpha.com");
380 1 : unlink("/tmp/email-cli-test-multiacct/.config/email-cli/accounts/zara@alpha.com/config.ini");
381 1 : rmdir("/tmp/email-cli-test-multiacct/.config/email-cli/accounts/zara@alpha.com");
382 : }
383 :
384 : // 17. config_list_accounts realloc path: create 9 accounts (cap starts at
385 : // 8, so the 9th triggers realloc) — covers lines 227-230
386 : {
387 1 : setenv("HOME", "/tmp/email-cli-test-manyacct", 1);
388 1 : unsetenv("XDG_CONFIG_HOME");
389 1 : const char *base =
390 : "/tmp/email-cli-test-manyacct/.config/email-cli/accounts";
391 :
392 : /* usernames chosen to produce a valid IMAP host pattern */
393 1 : const char *users[] = {
394 : "u1@svc.io", "u2@svc.io", "u3@svc.io", "u4@svc.io",
395 : "u5@svc.io", "u6@svc.io", "u7@svc.io", "u8@svc.io",
396 : "u9@svc.io"
397 : };
398 1 : int nacct = (int)(sizeof(users) / sizeof(users[0]));
399 : char path[256];
400 10 : for (int i = 0; i < nacct; i++) {
401 9 : snprintf(path, sizeof(path), "%s/%s", base, users[i]);
402 9 : fs_mkdir_p(path, 0700);
403 9 : snprintf(path, sizeof(path), "%s/%s/config.ini", base, users[i]);
404 9 : FILE *fp = fopen(path, "w");
405 9 : if (fp) {
406 9 : fprintf(fp, "EMAIL_HOST=imaps://imap.svc.io\n");
407 9 : fprintf(fp, "EMAIL_USER=%s\n", users[i]);
408 9 : fprintf(fp, "EMAIL_PASS=pass\n");
409 9 : fclose(fp);
410 : }
411 : }
412 :
413 1 : int count = 0;
414 1 : AccountEntry *list = config_list_accounts(&count);
415 1 : ASSERT(count == nacct, "many accounts: count=9");
416 1 : config_free_account_list(list, count);
417 :
418 : /* cleanup */
419 10 : for (int i = 0; i < nacct; i++) {
420 9 : snprintf(path, sizeof(path),
421 : "/tmp/email-cli-test-manyacct/.config/email-cli/accounts/%s/config.ini",
422 : users[i]);
423 9 : unlink(path);
424 9 : snprintf(path, sizeof(path),
425 : "/tmp/email-cli-test-manyacct/.config/email-cli/accounts/%s",
426 : users[i]);
427 9 : rmdir(path);
428 : }
429 : }
430 :
431 1 : if (old_home) setenv("HOME", old_home, 1);
432 0 : else unsetenv("HOME");
433 1 : if (old_xdg) setenv("XDG_CONFIG_HOME", old_xdg, 1);
434 : }
435 :
436 : /* ── app_settings_set_obfuscation ────────────────────────────────────── */
437 :
438 1 : void test_config_settings(void) {
439 1 : char *old_home = getenv("HOME");
440 1 : char *old_xdg = getenv("XDG_CONFIG_HOME");
441 1 : setenv("HOME", "/tmp/email-cli-settings-test", 1);
442 1 : unsetenv("XDG_CONFIG_HOME");
443 :
444 1 : int rc = app_settings_set_obfuscation(0);
445 1 : ASSERT(rc == 0, "settings: set_obfuscation(0) returns 0");
446 :
447 1 : int val = app_settings_get_obfuscation();
448 1 : ASSERT(val == 0, "settings: get_obfuscation returns 0 after set");
449 :
450 1 : rc = app_settings_set_obfuscation(1);
451 1 : ASSERT(rc == 0, "settings: set_obfuscation(1) returns 0");
452 :
453 1 : if (old_home) setenv("HOME", old_home, 1); else unsetenv("HOME");
454 1 : if (old_xdg) setenv("XDG_CONFIG_HOME", old_xdg, 1); else unsetenv("XDG_CONFIG_HOME");
455 : }
456 :
457 : /* ── config_load_account ─────────────────────────────────────────────── */
458 :
459 1 : void test_config_load_account(void) {
460 1 : char *old_home = getenv("HOME");
461 1 : char *old_xdg = getenv("XDG_CONFIG_HOME");
462 1 : setenv("HOME", "/tmp/email-cli-load-acct-test", 1);
463 1 : unsetenv("XDG_CONFIG_HOME");
464 :
465 1 : Config cfg = {0};
466 1 : cfg.host = strdup("imaps://imap.load-test.com");
467 1 : cfg.user = strdup("load-acct@load-test.com");
468 1 : cfg.pass = strdup("testpass");
469 1 : cfg.folder = strdup("INBOX");
470 1 : config_save_account(&cfg);
471 1 : free(cfg.host); free(cfg.user); free(cfg.pass); free(cfg.folder);
472 :
473 1 : Config *loaded = config_load_account("load-acct@load-test.com");
474 1 : ASSERT(loaded != NULL, "config_load_account: found by user");
475 1 : if (loaded) {
476 1 : ASSERT(strcmp(loaded->user, "load-acct@load-test.com") == 0,
477 : "config_load_account: user matches");
478 1 : config_free(loaded);
479 : }
480 :
481 1 : Config *miss = config_load_account("nobody@nowhere.invalid");
482 1 : ASSERT(miss == NULL, "config_load_account: missing returns NULL");
483 :
484 1 : Config *null_r = config_load_account(NULL);
485 1 : ASSERT(null_r == NULL, "config_load_account: NULL arg returns NULL");
486 :
487 1 : Config *empty_r = config_load_account("");
488 1 : ASSERT(empty_r == NULL, "config_load_account: empty arg returns NULL");
489 :
490 1 : if (old_home) setenv("HOME", old_home, 1); else unsetenv("HOME");
491 1 : if (old_xdg) setenv("XDG_CONFIG_HOME", old_xdg, 1); else unsetenv("XDG_CONFIG_HOME");
492 : }
493 :
494 : /* ── config_migrate_credentials ──────────────────────────────────────── */
495 :
496 1 : void test_config_migrate(void) {
497 1 : char *old_home = getenv("HOME");
498 1 : char *old_xdg = getenv("XDG_CONFIG_HOME");
499 1 : setenv("HOME", "/tmp/email-cli-migrate-test", 1);
500 1 : unsetenv("XDG_CONFIG_HOME");
501 :
502 1 : Config cfg = {0};
503 1 : cfg.host = strdup("imaps://imap.mig.com");
504 1 : cfg.user = strdup("migrate@mig.com");
505 1 : cfg.pass = strdup("pass");
506 1 : cfg.folder = strdup("INBOX");
507 1 : config_save_account(&cfg);
508 1 : free(cfg.host); free(cfg.user); free(cfg.pass); free(cfg.folder);
509 :
510 1 : int rc = config_migrate_credentials();
511 1 : ASSERT(rc == 0, "config_migrate_credentials: returns 0");
512 :
513 : /* No accounts → also returns 0 */
514 1 : setenv("HOME", "/tmp/email-cli-migrate-empty-test", 1);
515 1 : rc = config_migrate_credentials();
516 1 : ASSERT(rc == 0, "config_migrate_credentials: empty home returns 0");
517 :
518 1 : if (old_home) setenv("HOME", old_home, 1); else unsetenv("HOME");
519 1 : if (old_xdg) setenv("XDG_CONFIG_HOME", old_xdg, 1); else unsetenv("XDG_CONFIG_HOME");
520 : }
|