LCOV - code coverage report
Current view: top level - tests/unit - test_compose_service.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 138 138
Test Date: 2026-05-07 15:53:07 Functions: 100.0 % 16 16

            Line data    Source code
       1              : #include "test_helpers.h"
       2              : #include "compose_service.h"
       3              : #include <stdlib.h>
       4              : #include <string.h>
       5              : 
       6              : /* ── compose_build_message ───────────────────────────────────────────── */
       7              : 
       8            1 : static void test_build_null_params(void) {
       9            1 :     char *out = NULL;
      10            1 :     size_t len = 0;
      11            1 :     ASSERT(compose_build_message(NULL, &out, &len) == -1,
      12              :            "build: NULL params → -1");
      13              : }
      14              : 
      15            1 : static void test_build_missing_fields(void) {
      16            1 :     char *out = NULL;
      17            1 :     size_t len = 0;
      18            1 :     ComposeParams p = {0};
      19            1 :     ASSERT(compose_build_message(&p, &out, &len) == -1,
      20              :            "build: missing from/to/subject → -1");
      21              : 
      22            1 :     p.from = "a@b.com";
      23            1 :     ASSERT(compose_build_message(&p, &out, &len) == -1,
      24              :            "build: missing to/subject → -1");
      25              : 
      26            1 :     p.to = "c@d.com";
      27            1 :     ASSERT(compose_build_message(&p, &out, &len) == -1,
      28              :            "build: missing subject → -1");
      29              : }
      30              : 
      31            1 : static void test_build_null_out(void) {
      32            1 :     ComposeParams p = {.from = "a@b.com", .to = "c@d.com", .subject = "Hi"};
      33            1 :     size_t len = 0;
      34            1 :     ASSERT(compose_build_message(&p, NULL, &len) == -1,
      35              :            "build: NULL out → -1");
      36              : }
      37              : 
      38            1 : static void test_build_basic(void) {
      39            1 :     ComposeParams p = {
      40              :         .from = "alice@example.com",
      41              :         .to = "bob@example.com",
      42              :         .subject = "Test subject",
      43              :         .body = "Hello, world!\n"
      44              :     };
      45            1 :     char *out = NULL;
      46            1 :     size_t len = 0;
      47            1 :     int rc = compose_build_message(&p, &out, &len);
      48            1 :     ASSERT(rc == 0, "build basic: success");
      49            1 :     ASSERT(out != NULL, "build basic: out not NULL");
      50            1 :     ASSERT(len > 0, "build basic: len > 0");
      51              : 
      52              :     /* Verify required headers */
      53            1 :     ASSERT(strstr(out, "From: alice@example.com\r\n") != NULL,
      54              :            "build basic: From header");
      55            1 :     ASSERT(strstr(out, "To: bob@example.com\r\n") != NULL,
      56              :            "build basic: To header");
      57            1 :     ASSERT(strstr(out, "Subject: Test subject\r\n") != NULL,
      58              :            "build basic: Subject header");
      59            1 :     ASSERT(strstr(out, "Date: ") != NULL,
      60              :            "build basic: Date header");
      61            1 :     ASSERT(strstr(out, "Message-ID: <") != NULL,
      62              :            "build basic: Message-ID header");
      63            1 :     ASSERT(strstr(out, "MIME-Version: 1.0\r\n") != NULL,
      64              :            "build basic: MIME-Version header");
      65            1 :     ASSERT(strstr(out, "Content-Type: text/plain; charset=UTF-8\r\n") != NULL,
      66              :            "build basic: Content-Type header");
      67              : 
      68              :     /* Verify header/body separator */
      69            1 :     ASSERT(strstr(out, "\r\n\r\n") != NULL,
      70              :            "build basic: CRLF separator");
      71              : 
      72              :     /* Verify body is present after separator */
      73            1 :     const char *body_start = strstr(out, "\r\n\r\n");
      74            1 :     ASSERT(body_start && strstr(body_start, "Hello, world!") != NULL,
      75              :            "build basic: body present");
      76              : 
      77              :     /* No In-Reply-To for non-reply */
      78            1 :     ASSERT(strstr(out, "In-Reply-To") == NULL,
      79              :            "build basic: no In-Reply-To");
      80              : 
      81            1 :     free(out);
      82              : }
      83              : 
      84            1 : static void test_build_reply(void) {
      85            1 :     ComposeParams p = {
      86              :         .from = "alice@example.com",
      87              :         .to = "bob@example.com",
      88              :         .subject = "Re: Original",
      89              :         .body = "Thanks!",
      90              :         .reply_to_msg_id = "<orig123@example.com>"
      91              :     };
      92            1 :     char *out = NULL;
      93            1 :     size_t len = 0;
      94            1 :     int rc = compose_build_message(&p, &out, &len);
      95            1 :     ASSERT(rc == 0, "build reply: success");
      96            1 :     ASSERT(strstr(out, "In-Reply-To: <orig123@example.com>\r\n") != NULL,
      97              :            "build reply: In-Reply-To header");
      98            1 :     ASSERT(strstr(out, "References: <orig123@example.com>\r\n") != NULL,
      99              :            "build reply: References header");
     100            1 :     free(out);
     101              : }
     102              : 
     103            1 : static void test_build_empty_body(void) {
     104            1 :     ComposeParams p = {
     105              :         .from = "a@b.com",
     106              :         .to = "c@d.com",
     107              :         .subject = "Empty",
     108              :         .body = NULL
     109              :     };
     110            1 :     char *out = NULL;
     111            1 :     size_t len = 0;
     112            1 :     int rc = compose_build_message(&p, &out, &len);
     113            1 :     ASSERT(rc == 0, "build empty body: success");
     114            1 :     ASSERT(out != NULL, "build empty body: not NULL");
     115            1 :     free(out);
     116              : }
     117              : 
     118            1 : static void test_build_lf_to_crlf(void) {
     119            1 :     ComposeParams p = {
     120              :         .from = "a@b.com",
     121              :         .to = "c@d.com",
     122              :         .subject = "Lines",
     123              :         .body = "line1\nline2\nline3"
     124              :     };
     125            1 :     char *out = NULL;
     126            1 :     size_t len = 0;
     127            1 :     int rc = compose_build_message(&p, &out, &len);
     128            1 :     ASSERT(rc == 0, "build LF→CRLF: success");
     129              :     /* Body should have CRLF line endings */
     130            1 :     const char *body = strstr(out, "\r\n\r\n");
     131            1 :     ASSERT(body && strstr(body, "line1\r\nline2\r\nline3") != NULL,
     132              :            "build LF→CRLF: body has CRLF");
     133            1 :     free(out);
     134              : }
     135              : 
     136              : /* ── compose_extract_reply_meta ──────────────────────────────────────── */
     137              : 
     138            1 : static void test_extract_null(void) {
     139            1 :     char *rt = NULL, *subj = NULL, *msgid = NULL;
     140            1 :     ASSERT(compose_extract_reply_meta(NULL, &rt, &subj, &msgid) == -1,
     141              :            "extract: NULL raw_msg → -1");
     142            1 :     ASSERT(compose_extract_reply_meta("From: a\r\n\r\n", NULL, &subj, &msgid) == -1,
     143              :            "extract: NULL reply_to_out → -1");
     144              : }
     145              : 
     146            1 : static void test_extract_basic(void) {
     147            1 :     const char *raw =
     148              :         "From: Alice <alice@example.com>\r\n"
     149              :         "Subject: Hello\r\n"
     150              :         "Message-ID: <abc123@example.com>\r\n"
     151              :         "\r\n"
     152              :         "Body.\r\n";
     153            1 :     char *rt = NULL, *subj = NULL, *msgid = NULL;
     154            1 :     int rc = compose_extract_reply_meta(raw, &rt, &subj, &msgid);
     155            1 :     ASSERT(rc == 0, "extract basic: success");
     156            1 :     ASSERT(rt != NULL, "extract basic: reply_to not NULL");
     157            1 :     ASSERT(strstr(rt, "alice@example.com") != NULL,
     158              :            "extract basic: reply_to has address");
     159            1 :     ASSERT(subj != NULL && strncmp(subj, "Re: ", 4) == 0,
     160              :            "extract basic: subject starts with Re:");
     161            1 :     ASSERT(strstr(subj, "Hello") != NULL,
     162              :            "extract basic: subject has original");
     163            1 :     ASSERT(msgid != NULL && strcmp(msgid, "<abc123@example.com>") == 0,
     164              :            "extract basic: message-id correct");
     165            1 :     free(rt); free(subj); free(msgid);
     166              : }
     167              : 
     168            1 : static void test_extract_re_dedup(void) {
     169            1 :     const char *raw =
     170              :         "From: Bob\r\n"
     171              :         "Subject: Re: Re: Re: Original\r\n"
     172              :         "Message-ID: <x@y>\r\n"
     173              :         "\r\n";
     174            1 :     char *rt = NULL, *subj = NULL, *msgid = NULL;
     175            1 :     int rc = compose_extract_reply_meta(raw, &rt, &subj, &msgid);
     176            1 :     ASSERT(rc == 0, "extract re dedup: success");
     177            1 :     ASSERT(strcmp(subj, "Re: Original") == 0,
     178              :            "extract re dedup: single Re: prefix");
     179            1 :     free(rt); free(subj); free(msgid);
     180              : }
     181              : 
     182            1 : static void test_extract_reply_to_header(void) {
     183            1 :     const char *raw =
     184              :         "From: Alice\r\n"
     185              :         "Reply-To: noreply@example.com\r\n"
     186              :         "Subject: Test\r\n"
     187              :         "Message-ID: <z@w>\r\n"
     188              :         "\r\n";
     189            1 :     char *rt = NULL, *subj = NULL, *msgid = NULL;
     190            1 :     int rc = compose_extract_reply_meta(raw, &rt, &subj, &msgid);
     191            1 :     ASSERT(rc == 0, "extract Reply-To: success");
     192            1 :     ASSERT(strstr(rt, "noreply@example.com") != NULL,
     193              :            "extract Reply-To: prefers Reply-To over From");
     194            1 :     free(rt); free(subj); free(msgid);
     195              : }
     196              : 
     197              : /* ── Registration ────────────────────────────────────────────────────── */
     198              : 
     199            1 : static void test_build_cc(void) {
     200            1 :     ComposeParams p = {
     201              :         .from = "alice@example.com",
     202              :         .to   = "bob@example.com",
     203              :         .cc   = "carol@example.com",
     204              :         .subject = "Hello",
     205              :         .body = "Hi"
     206              :     };
     207            1 :     char *out = NULL; size_t len = 0;
     208            1 :     ASSERT(compose_build_message(&p, &out, &len) == 0, "build cc: success");
     209            1 :     ASSERT(strstr(out, "Cc: carol@example.com\r\n") != NULL,
     210              :            "build cc: Cc header present");
     211            1 :     ASSERT(strstr(out, "Bcc:") == NULL, "build cc: no Bcc when bcc NULL");
     212            1 :     free(out);
     213              : }
     214              : 
     215            1 : static void test_build_bcc(void) {
     216            1 :     ComposeParams p = {
     217              :         .from = "alice@example.com",
     218              :         .to   = "bob@example.com",
     219              :         .bcc  = "secret@example.com",
     220              :         .subject = "Hello",
     221              :         .body = "Hi"
     222              :     };
     223            1 :     char *out = NULL; size_t len = 0;
     224            1 :     ASSERT(compose_build_message(&p, &out, &len) == 0, "build bcc: success");
     225            1 :     ASSERT(strstr(out, "Bcc: secret@example.com\r\n") != NULL,
     226              :            "build bcc: Bcc header present");
     227            1 :     ASSERT(strstr(out, "Cc:") == NULL, "build bcc: no Cc when cc NULL");
     228            1 :     free(out);
     229              : }
     230              : 
     231            1 : static void test_build_cc_and_bcc(void) {
     232            1 :     ComposeParams p = {
     233              :         .from = "alice@example.com",
     234              :         .to   = "bob@example.com",
     235              :         .cc   = "carol@example.com, dave@example.com",
     236              :         .bcc  = "eve@example.com",
     237              :         .subject = "Meeting",
     238              :         .body = "See you there"
     239              :     };
     240            1 :     char *out = NULL; size_t len = 0;
     241            1 :     ASSERT(compose_build_message(&p, &out, &len) == 0, "build cc+bcc: success");
     242            1 :     ASSERT(strstr(out, "Cc: carol@example.com, dave@example.com\r\n") != NULL,
     243              :            "build cc+bcc: Cc header");
     244            1 :     ASSERT(strstr(out, "Bcc: eve@example.com\r\n") != NULL,
     245              :            "build cc+bcc: Bcc header");
     246            1 :     ASSERT(strstr(out, "To: bob@example.com\r\n") != NULL,
     247              :            "build cc+bcc: To header still present");
     248            1 :     free(out);
     249              : }
     250              : 
     251            1 : static void test_build_empty_cc_omitted(void) {
     252            1 :     ComposeParams p = {
     253              :         .from = "a@b.com", .to = "c@d.com",
     254              :         .cc = "", .bcc = "",
     255              :         .subject = "X", .body = "Y"
     256              :     };
     257            1 :     char *out = NULL; size_t len = 0;
     258            1 :     ASSERT(compose_build_message(&p, &out, &len) == 0, "build empty cc: success");
     259            1 :     ASSERT(strstr(out, "Cc:") == NULL,  "build empty cc: no Cc header");
     260            1 :     ASSERT(strstr(out, "Bcc:") == NULL, "build empty cc: no Bcc header");
     261            1 :     free(out);
     262              : }
     263              : 
     264            1 : void test_compose_service(void) {
     265            1 :     RUN_TEST(test_build_null_params);
     266            1 :     RUN_TEST(test_build_missing_fields);
     267            1 :     RUN_TEST(test_build_null_out);
     268            1 :     RUN_TEST(test_build_basic);
     269            1 :     RUN_TEST(test_build_reply);
     270            1 :     RUN_TEST(test_build_empty_body);
     271            1 :     RUN_TEST(test_build_lf_to_crlf);
     272            1 :     RUN_TEST(test_build_cc);
     273            1 :     RUN_TEST(test_build_bcc);
     274            1 :     RUN_TEST(test_build_cc_and_bcc);
     275            1 :     RUN_TEST(test_build_empty_cc_omitted);
     276            1 :     RUN_TEST(test_extract_null);
     277            1 :     RUN_TEST(test_extract_basic);
     278            1 :     RUN_TEST(test_extract_re_dedup);
     279            1 :     RUN_TEST(test_extract_reply_to_header);
     280            1 : }
        

Generated by: LCOV version 2.0-1