test_validators.c (9119B)
1 /* 2 This file is part of TALER 3 (C) 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 /** 18 * @file src/util/test_validators.c 19 * @brief Tests for validators. 20 * @author Christian Grothoff (ivan@avalos.me) 21 */ 22 #include "platform.h" 23 #include <gnunet/gnunet_util_lib.h> 24 #include "taler/taler_merchant_util.h" 25 26 static bool 27 check_email (void) 28 { 29 struct 30 { 31 const char *email; 32 bool expected; 33 } tests[] = { 34 /* Valid emails */ 35 {"user@example.com", true}, 36 {"john.doe@example.co.uk", true}, 37 {"alice+tag@domain.org", true}, 38 {"test_underscore@test.example", true}, 39 {"user!special@example.com", true}, 40 {"first.last@sub.domain.example.com", true}, 41 {"\"john doe\"@example.com", true}, 42 {"\"very.unusual.@.unusual.com\"@example.com", true}, 43 {"user@192.168.1.1", true}, 44 {"user@[192.168.1.1]", true}, 45 {"user@[IPv6:2001:db8::1]", true}, 46 {"user@[IPv6::1]", true}, 47 {"a@b.c", true}, 48 {"test+123@test-domain.org", true}, 49 {"_test@example.com", true}, 50 {"1234567890@example.com", true}, 51 52 /* Invalid emails */ 53 {"", false}, 54 {"plainaddress", false}, 55 {"@example.com", false}, 56 {"user@", false}, 57 {"user name@example.com", false}, 58 {"user@domain", false}, /* No TLD - debatable, but common validation */ 59 {"user..name@example.com", false}, 60 {".user@example.com", false}, 61 {"user.@example.com", false}, 62 {"user@.example.com", false}, 63 {"user@example.com.", false}, 64 {"user@-example.com", false}, 65 {"user@example-.com", false}, 66 {"user@@example.com", false}, 67 {"user@exam ple.com", false}, 68 {"user@[256.256.256.256]", false}, 69 {"user@[IPv6:zzzz::1]", false}, 70 /* The local part may legally start with '-', but mail(1) would 71 take such an address for an option */ 72 {"-user@example.com", false}, 73 {"-Xexec@example.com", false}, 74 }; 75 76 unsigned int num_tests = sizeof (tests) / sizeof (tests[0]); 77 unsigned int passed = 0; 78 unsigned int failed = 0; 79 80 for (unsigned int i = 0; i < num_tests; i++) 81 { 82 bool result = TALER_MERCHANT_email_valid (tests[i].email); 83 bool success = (result == tests[i].expected); 84 85 if (success) 86 { 87 passed++; 88 continue; 89 } 90 failed++; 91 fprintf (stderr, 92 "FAIL: \"%s\" -> %s (expected %s)\n", 93 tests[i].email ? tests[i].email : "(NULL)", 94 result ? "VALID" : "INVALID", 95 tests[i].expected ? "VALID" : "INVALID"); 96 } 97 return (failed > 0) ? 1 : 0; 98 } 99 100 101 static bool 102 check_phone (void) 103 { 104 struct 105 { 106 const char *phone; 107 const char *expected_normalized; 108 bool allow_letters; 109 } tests[] = { 110 /* Valid phone numbers with +CC - digits only */ 111 {"+1-202-555-0173", "+12025550173", false}, 112 {"+1 202 555 0173", "+12025550173", false}, 113 {"+1 (202) 555-0173", "+12025550173", false}, 114 {"+33142685300", "+33142685300", false}, 115 {"+33 1 42 68 53 00", "+33142685300", false}, 116 {"+44 20 7946 0958", "+442079460958", false}, 117 {"+44.20.7946.0958", "+442079460958", false}, 118 {"+49-30-12345678", "+493012345678", false}, 119 {"+886 2 2345 6789", "+886223456789", false}, 120 {"+1-800-555-0123", "+18005550123", false}, 121 {"+81-3-1234-5678", "+81312345678", false}, 122 {"+39 06 6994 0255", "+390669940255", false}, 123 {"+34-91-577-3200", "+34915773200", false}, 124 {"+46 8 123456", "+468123456", false}, 125 {"+1(555)1234567", "+15551234567", false}, 126 {"+1-555-1234567", "+15551234567", false}, 127 {"+358 9 1234567", "+35891234567", false}, 128 {"+1 202-555.0173", "+12025550173", false}, 129 {"+7 495 123-45-67", "+74951234567", false}, 130 131 /* Valid phone numbers with letters (allow_letters=true) */ 132 {"+1-800-CALL-NOW", "+18002255669", true}, 133 {"+1-800-FLOWERS", "+18003569377", true}, 134 {"+1-888-PIZZA", "+188874992", true}, 135 {"+1 800 CALL NOW", "+18002255669", true}, 136 {"+44-HELLO-WORLD", "+444355696753", true}, 137 {"+1-555-ABCDEFGH", "+155522233344", true}, 138 139 /* Letters should fail when allow_letters=false */ 140 {"+1-800-CALL-NOW", NULL, false}, 141 {"+1-800-FLOWERS", NULL, false}, 142 143 /* Invalid phone numbers */ 144 {"", NULL, false}, 145 {"202-555-0173", NULL, false}, /* Missing + */ 146 {"1-202-555-0173", NULL, false}, /* Missing + */ 147 {"+1", NULL, false}, /* Only country code */ 148 {"+1 abc def ghij", NULL, false}, /* Invalid format */ 149 {"1-800-555-0123", NULL, false}, /* No plus sign */ 150 {"++1-202-555-0173", NULL, false}, /* Double plus */ 151 {"+1--202-555-0173", NULL, false}, /* Double hyphen */ 152 {"+12025550173 ", NULL, false}, /* Trailing space */ 153 {" +12025550173", NULL, false}, /* Leading space */ 154 {"+1 (555", NULL, false}, /* Unclosed parenthesis */ 155 {"+1 555)", NULL, false}, /* Unmatched closing paren */ 156 {"+1-555-", NULL, false}, /* Trailing hyphen */ 157 {"+1-555-01a3", NULL, false}, /* Lowercase letter without allow_letters */ 158 {"+1 (202", NULL, false}, /* Unmatched paren */ 159 }; 160 161 unsigned int num_tests = sizeof (tests) / sizeof (tests[0]); 162 unsigned int passed = 0; 163 unsigned int failed = 0; 164 165 for (unsigned int i = 0; i < num_tests; i++) 166 { 167 char *result = TALER_MERCHANT_phone_validate_normalize (tests[i].phone, 168 tests[i].allow_letters); 169 bool success; 170 171 if (NULL == tests[i].expected_normalized) 172 { 173 success = (NULL == result); 174 } 175 else 176 { 177 success = (NULL != result && 178 0 == strcmp (result, 179 tests[i].expected_normalized)); 180 } 181 if (success) 182 { 183 passed++; 184 } 185 else 186 { 187 failed++; 188 fprintf (stderr, 189 "FAIL: (`%s' (letters=%s) -> `%s'", 190 tests[i].phone, 191 tests[i].allow_letters ? "true" : "false", 192 result); 193 } 194 GNUNET_free (result); 195 } 196 return (failed > 0) ? 1 : 0; 197 } 198 199 200 static bool 201 check_mime_type (void) 202 { 203 struct 204 { 205 const char *mime_type; 206 bool expected; 207 } tests[] = { 208 /* Valid MIME types */ 209 {"text/plain", true}, 210 {"application/pdf", true}, 211 {"text/csv; charset=utf-8", true}, 212 {"application/vnd.api+json", true}, 213 {"TEXT/HTML", true}, 214 215 /* Invalid MIME types */ 216 {"", false}, 217 {"text", false}, 218 {"text/", false}, 219 {"/plain", false}, 220 {"text/plain\r\nAccept: text/html", false}, 221 {"text/plain\n", false}, 222 {"-text/plain", false}, 223 {"text plain", false}, 224 }; 225 unsigned int num_tests = sizeof (tests) / sizeof (tests[0]); 226 unsigned int failed = 0; 227 228 for (unsigned int i = 0; i < num_tests; i++) 229 { 230 bool result = TALER_MERCHANT_mime_type_valid (tests[i].mime_type); 231 232 if (result == tests[i].expected) 233 continue; 234 failed++; 235 fprintf (stderr, 236 "FAIL: \"%s\" -> %s (expected %s)\n", 237 tests[i].mime_type, 238 result ? "VALID" : "INVALID", 239 tests[i].expected ? "VALID" : "INVALID"); 240 } 241 return (failed > 0) ? 1 : 0; 242 } 243 244 245 static bool 246 check_report_target (void) 247 { 248 struct 249 { 250 const char *target_address; 251 bool expected; 252 } tests[] = { 253 /* Valid target addresses */ 254 {"user@example.com", true}, 255 {"/var/spool/taler/report.pdf", true}, 256 {"a file with spaces", true}, 257 258 /* Invalid target addresses */ 259 {"", false}, 260 {"-Xexec@example.com", false}, 261 {"--exec=/bin/sh", false}, 262 {"user@example.com\r\nBcc: other@example.com", false}, 263 {"user@example.com\n", false}, 264 }; 265 unsigned int num_tests = sizeof (tests) / sizeof (tests[0]); 266 unsigned int failed = 0; 267 268 for (unsigned int i = 0; i < num_tests; i++) 269 { 270 bool result = TALER_MERCHANT_report_target_valid (tests[i].target_address); 271 272 if (result == tests[i].expected) 273 continue; 274 failed++; 275 fprintf (stderr, 276 "FAIL: \"%s\" -> %s (expected %s)\n", 277 tests[i].target_address, 278 result ? "VALID" : "INVALID", 279 tests[i].expected ? "VALID" : "INVALID"); 280 } 281 return (failed > 0) ? 1 : 0; 282 } 283 284 285 int 286 main (void) 287 { 288 if (check_email ()) 289 { 290 fprintf (stderr, 291 "email failed\n"); 292 return 1; 293 } 294 if (check_phone ()) 295 { 296 fprintf (stderr, 297 "phone failed\n"); 298 return 1; 299 } 300 if (check_mime_type ()) 301 { 302 fprintf (stderr, 303 "mime_type failed\n"); 304 return 1; 305 } 306 if (check_report_target ()) 307 { 308 fprintf (stderr, 309 "report target failed\n"); 310 return 1; 311 } 312 return 0; 313 }