merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

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 }