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 : }
|