LCOV - code coverage report
Current view: top level - tests/unit - test_config.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.0 % 100 99
Test Date: 2026-04-15 21:12:52 Functions: 100.0 % 2 2

            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            5 : static void config_cleanup(void *ptr) {
      12            5 :     Config **cfg = (Config **)ptr;
      13            5 :     if (cfg && *cfg) {
      14            2 :         config_free(*cfg);
      15            2 :         *cfg = NULL;
      16              :     }
      17            5 : }
      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            1 :     if (old_home) setenv("HOME", old_home, 1);
     167            0 :     else unsetenv("HOME");
     168            1 :     if (old_xdg) setenv("XDG_CONFIG_HOME", old_xdg, 1);
     169              : }
        

Generated by: LCOV version 2.0-1