diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-26 12:56:51 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-26 12:56:59 -0300 |
| commit | b7b08a7d29d84de187dd8a55e0c94de66b07a572 (patch) | |
| tree | 7e55661d3eca3cf3b16aada6e87eabee68802fb9 /tests/test_cepis.c | |
| download | asm-b7b08a7d29d84de187dd8a55e0c94de66b07a572.tar.gz | |
Initial importv0.1.0
Diffstat (limited to 'tests/test_cepis.c')
| -rw-r--r-- | tests/test_cepis.c | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/tests/test_cepis.c b/tests/test_cepis.c new file mode 100644 index 0000000..3dd6dc6 --- /dev/null +++ b/tests/test_cepis.c @@ -0,0 +1,721 @@ +/* + * Test driver. Every case runs against the local server in tests/server.py, + * reached through base_url; nothing here mocks the HTTP layer. + * + * Usage: test_cepis http://127.0.0.1:<port> + */ + +#define _POSIX_C_SOURCE 200809L + +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "cepis.h" + +_Static_assert(sizeof(cepis_geo) == 32, "cepis_geo layout"); +_Static_assert(offsetof(cepis_geo, precisao) == 24, "cepis_geo layout"); +_Static_assert(sizeof(cepis_address) == 120, "cepis_address layout"); +_Static_assert(offsetof(cepis_address, siafi) == 96, "cepis_address layout"); +_Static_assert(offsetof(cepis_address, geo) == 104, "cepis_address layout"); +_Static_assert(offsetof(cepis_address, raw) == 112, "cepis_address layout"); +_Static_assert(sizeof(struct cepis_health) == 48, "cepis_health layout"); +_Static_assert(offsetof(struct cepis_health, base_gerada_em) == 32, + "cepis_health layout"); +_Static_assert(sizeof(cepis_error) == 272, "cepis_error layout"); +_Static_assert(offsetof(cepis_error, retry_after) == 8, "cepis_error layout"); +_Static_assert(offsetof(cepis_error, message) == 16, "cepis_error layout"); +_Static_assert(sizeof(cepis_config) == 56, "cepis_config layout"); +_Static_assert(offsetof(cepis_config, max_attempts) == 16, + "cepis_config layout"); +_Static_assert(offsetof(cepis_config, user_agent) == 48, "cepis_config layout"); + +static const char *base; +static int failures; +static int checks; +static const char *current; + +#define CHECK(cond) \ + do { \ + checks++; \ + if (!(cond)) { \ + failures++; \ + fprintf(stderr, " FAIL %s:%d [%s]: %s\n", __FILE__, __LINE__, \ + current, #cond); \ + } \ + } while (0) + +#define CHECK_STR(got, want) \ + do { \ + checks++; \ + const char *g_ = (got); \ + if (g_ == NULL || strcmp(g_, (want)) != 0) { \ + failures++; \ + fprintf(stderr, " FAIL %s:%d [%s]: want \"%s\" got \"%s\"\n", \ + __FILE__, __LINE__, current, (want), \ + g_ ? g_ : "(null)"); \ + } \ + } while (0) + +static void begin(const char *name) +{ + current = name; + printf("- %s\n", name); +} + +static char *urlf(const char *fmt, ...) +{ + static char buf[8][512]; + static int slot; + char *out = buf[slot++ % 8]; + va_list ap; + va_start(ap, fmt); + vsnprintf(out, 512, fmt, ap); + va_end(ap); + return out; +} + +static cepis_client *client_at(const char *url, int attempts, double backoff) +{ + cepis_config cfg; + cepis_client *client = NULL; + + memset(&cfg, 0, sizeof cfg); + cfg.base_url = url; + cfg.max_attempts = attempts; + cfg.backoff_base = backoff; + cfg.backoff_max = backoff * 4.0; + cfg.timeout = 5.0; + if (cepis_client_new(&cfg, &client) != CEPIS_OK) + return NULL; + return client; +} + +static long hits(const char *prefix) +{ + cepis_client *client; + + client = client_at(urlf("%s/_stats/%s", base, prefix), 1, 0.001); + struct cepis_health *health = NULL; + long count = -1; + + if (client == NULL) + return -1; + if (cepis_health(client, &health, NULL) == CEPIS_OK && health != NULL) + count = (long)health->ceps; + cepis_health_free(health); + cepis_client_free(client); + return count; +} + +static double now_seconds(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9; +} + +static void test_normalize(void) +{ + char out[16]; + + begin("normalize_cep"); + CHECK(cepis_normalize_cep("01001-000", out) == CEPIS_OK); + CHECK_STR(out, "01001000"); + CHECK(cepis_normalize_cep("01001000", out) == CEPIS_OK); + CHECK_STR(out, "01001000"); + CHECK(cepis_normalize_cep(" 01001-000 ", out) == CEPIS_OK); + CHECK_STR(out, "01001000"); + CHECK(cepis_normalize_cep("01.001-000", out) == CEPIS_OK); + CHECK_STR(out, "01001000"); + CHECK(cepis_normalize_cep("0100100", out) == CEPIS_ERR_INVALID_CEP); + CHECK_STR(out, ""); + CHECK(cepis_normalize_cep("010010000", out) == CEPIS_ERR_INVALID_CEP); + CHECK(cepis_normalize_cep("0100100a", out) == CEPIS_ERR_INVALID_CEP); + CHECK(cepis_normalize_cep("", out) == CEPIS_ERR_INVALID_CEP); + CHECK(cepis_normalize_cep(NULL, out) == CEPIS_ERR_INVALID_CEP); + CHECK(cepis_normalize_cep("01001000", NULL) == CEPIS_ERR_USAGE); +} + +static void test_lookup_geo(void) +{ + cepis_client *client = client_at(base, 3, 0.001); + cepis_address *address = NULL; + cepis_error err; + char masked[16]; + + begin("lookup with geo"); + CHECK(cepis_lookup(client, "01001-000", &address, &err) == CEPIS_OK); + if (address != NULL) { + CHECK_STR(address->cep, "01001000"); + CHECK_STR(address->logradouro, "Praça da Sé"); + CHECK_STR(address->complemento, "lado ímpar"); + CHECK_STR(address->unidade, ""); + CHECK_STR(address->bairro, "Sé"); + CHECK_STR(address->localidade, "São Paulo"); + CHECK_STR(address->uf, "SP"); + CHECK_STR(address->estado, "São Paulo"); + CHECK_STR(address->regiao, "Sudeste"); + CHECK_STR(address->ibge, "3550308"); + CHECK_STR(address->gia, "1004"); + CHECK_STR(address->ddd, "11"); + CHECK_STR(address->siafi, "7107"); + CHECK(address->raw != NULL); + CHECK(strstr(address->raw, "logradouro") != NULL); + CHECK(address->geo != NULL); + if (address->geo != NULL) { + CHECK(address->geo->lat < -23.5 && address->geo->lat > -23.6); + CHECK(address->geo->lon < -46.6 && address->geo->lon > -46.7); + CHECK(address->geo->raio_m == 1084); + CHECK_STR(address->geo->precisao, "logradouro"); + CHECK(cepis_geo_is_precise(address->geo) == 1); + } + cepis_address_formatted_cep(address, masked); + CHECK_STR(masked, "01001-000"); + } + cepis_address_free(address); + + address = NULL; + CHECK(cepis_lookup(client, "69900000", &address, &err) == CEPIS_OK); + if (address != NULL) { + CHECK(address->geo != NULL); + if (address->geo != NULL) { + CHECK_STR(address->geo->precisao, "localidade"); + CHECK(cepis_geo_is_precise(address->geo) == 0); + CHECK(address->geo->raio_m == 18420); + } + } + cepis_address_free(address); + cepis_client_free(client); +} + +static void test_lookup_no_geo(void) +{ + cepis_client *client = client_at(base, 3, 0.001); + cepis_address *address = NULL; + + begin("lookup without geo"); + CHECK(cepis_lookup(client, "20040020", &address, NULL) == CEPIS_OK); + if (address != NULL) { + CHECK(address->geo == NULL); + CHECK_STR(address->localidade, "Rio de Janeiro"); + CHECK_STR(address->gia, ""); + } + cepis_address_free(address); + cepis_client_free(client); +} + +static void test_escaped_json(void) +{ + cepis_client *client = client_at(urlf("%s/escaped", base), 1, 0.001); + cepis_address *address = NULL; + + begin("\\u escapes decoded to UTF-8"); + CHECK(cepis_lookup(client, "01001000", &address, NULL) == CEPIS_OK); + if (address != NULL) { + CHECK_STR(address->logradouro, "Praça da Sé"); + CHECK_STR(address->localidade, "São Paulo"); + } + cepis_address_free(address); + cepis_client_free(client); +} + +static void test_surrogate_pair(void) +{ + cepis_client *client = client_at(urlf("%s/surrogate", base), 1, 0.001); + cepis_address *address = NULL; + + begin("a surrogate pair decodes to one UTF-8 sequence"); + CHECK(cepis_lookup(client, "01001000", &address, NULL) == CEPIS_OK); + if (address != NULL) + CHECK_STR(address->logradouro, "Rua \xf0\x9f\x98\x80 Feliz"); + cepis_address_free(address); + cepis_client_free(client); +} + +static void test_csv_bom(void) +{ + const char *ceps[] = {"01001000", "20040020"}; + cepis_client *client = client_at(urlf("%s/bom", base), 1, 0.001); + cepis_address **out = NULL; + + begin("a UTF-8 BOM on the CSV is stripped"); + CHECK(cepis_lookup_many(client, ceps, 2, &out, NULL) == CEPIS_OK); + if (out != NULL) { + CHECK(out[0] != NULL); + CHECK(out[1] != NULL); + if (out[0] != NULL) + CHECK_STR(out[0]->cep, "01001000"); + } + cepis_address_list_free(out, 2); + cepis_client_free(client); +} + +static void test_not_found_not_retried(void) +{ + cepis_client *client = client_at(urlf("%s/nf", base), 3, 0.001); + cepis_address *address = NULL; + cepis_error err; + + begin("404 is not retried"); + CHECK(cepis_lookup(client, "99999999", &address, &err) == + CEPIS_ERR_NOT_FOUND); + CHECK(address == NULL); + CHECK(err.status == CEPIS_ERR_NOT_FOUND); + CHECK(strstr(err.message, "99999999") != NULL); + CHECK(hits("nf") == 1); + cepis_client_free(client); +} + +static void test_bad_cep_from_api(void) +{ + cepis_client *client = client_at(urlf("%s/always400", base), 3, 0.001); + cepis_address *address = NULL; + cepis_error err; + + begin("400 is not retried and carries the API message"); + CHECK(cepis_lookup(client, "00000000", &address, &err) == + CEPIS_ERR_INVALID_CEP); + CHECK(address == NULL); + CHECK(strstr(err.message, "8 d") != NULL); + CHECK(hits("always400") == 1); + cepis_address_free(address); + cepis_client_free(client); +} + +static void test_local_validation_makes_no_request(void) +{ + cepis_client *client = client_at(urlf("%s/novalid", base), 3, 0.001); + cepis_address *address = NULL; + cepis_error err; + + begin("local validation makes no request"); + CHECK(cepis_lookup(client, "123", &address, &err) == CEPIS_ERR_INVALID_CEP); + CHECK(address == NULL); + CHECK(strstr(err.message, "8 digits") != NULL); + CHECK(hits("novalid") == 0); + cepis_client_free(client); +} + +static void test_retry_then_success(void) +{ + cepis_client *client = client_at(urlf("%s/fail2", base), 3, 0.001); + cepis_address *address = NULL; + cepis_error err; + + begin("retry then success"); + CHECK(cepis_lookup(client, "01001000", &address, &err) == CEPIS_OK); + CHECK(address != NULL); + CHECK(hits("fail2") == 3); + cepis_address_free(address); + cepis_client_free(client); +} + +static void test_retry_exhausted(void) +{ + cepis_client *client = client_at(urlf("%s/always503", base), 2, 0.001); + cepis_address *address = NULL; + cepis_error err; + + begin("retry exhausted on 5xx"); + CHECK(cepis_lookup(client, "01001000", &address, &err) == CEPIS_ERR_SERVER); + CHECK(address == NULL); + CHECK(err.status == CEPIS_ERR_SERVER); + CHECK(err.http_status == 503); + CHECK(strstr(err.message, "503") != NULL); + CHECK(hits("always503") == 2); + cepis_client_free(client); +} + +static void test_rate_limited(void) +{ + cepis_client *client = client_at(urlf("%s/always429", base), 2, 0.001); + cepis_address *address = NULL; + cepis_error err; + + begin("429 exposes Retry-After in seconds"); + CHECK(cepis_lookup(client, "01001000", &address, &err) == + CEPIS_ERR_RATE_LIMITED); + CHECK(address == NULL); + CHECK(err.http_status == 429); + CHECK(err.retry_after > 0.5 && err.retry_after < 1.5); + CHECK(hits("always429") == 2); + cepis_client_free(client); +} + +static void test_rate_limited_http_date(void) +{ + cepis_client *client = client_at(urlf("%s/date429", base), 2, 0.001); + cepis_address *address = NULL; + cepis_error err; + + begin("429 with an HTTP-date Retry-After"); + CHECK(cepis_lookup(client, "01001000", &address, &err) == + CEPIS_ERR_RATE_LIMITED); + CHECK(err.retry_after > 0.0 && err.retry_after <= 3.0); + CHECK(hits("date429") == 2); + cepis_client_free(client); +} + +static void test_retry_after_is_capped(void) +{ + cepis_client *client = client_at(urlf("%s/hugeretry", base), 2, 0.01); + cepis_address *address = NULL; + cepis_error err; + double started = now_seconds(); + + begin("a huge Retry-After is capped at backoff_max"); + CHECK(cepis_lookup(client, "01001000", &address, &err) == + CEPIS_ERR_RATE_LIMITED); + CHECK(now_seconds() - started < 5.0); + CHECK(err.retry_after > 3000.0); + cepis_client_free(client); +} + +static void test_total_timeout(void) +{ + cepis_config cfg; + cepis_client *client = NULL; + cepis_address *address = NULL; + cepis_error err; + double started; + + begin("total_timeout bounds the whole call"); + memset(&cfg, 0, sizeof cfg); + cfg.base_url = urlf("%s/always503", base); + cfg.max_attempts = 50; + cfg.backoff_base = 0.05; + cfg.backoff_max = 0.05; + cfg.total_timeout = 0.4; + CHECK(cepis_client_new(&cfg, &client) == CEPIS_OK); + started = now_seconds(); + CHECK(cepis_lookup(client, "01001000", &address, &err) == CEPIS_ERR_SERVER); + CHECK(now_seconds() - started < 4.0); + cepis_client_free(client); +} + +static void test_invalid_response(void) +{ + cepis_client *client = client_at(urlf("%s/badjson", base), 1, 0.001); + cepis_address *address = NULL; + cepis_error err; + + begin("a 200 with an unparseable body"); + CHECK(cepis_lookup(client, "01001000", &address, &err) == + CEPIS_ERR_INVALID_RESPONSE); + CHECK(address == NULL); + cepis_client_free(client); +} + +static void test_bad_geo_kept_address(void) +{ + cepis_client *client = client_at(urlf("%s/badgeo", base), 1, 0.001); + cepis_address *address = NULL; + + begin("a malformed geo drops the geo, not the address"); + CHECK(cepis_lookup(client, "01001000", &address, NULL) == CEPIS_OK); + if (address != NULL) { + CHECK(address->geo == NULL); + CHECK_STR(address->localidade, "São Paulo"); + CHECK(strstr(address->raw, "campo_novo") != NULL); + } + cepis_address_free(address); + cepis_client_free(client); +} + +static void test_network_error(void) +{ + cepis_client *client = client_at("http://127.0.0.1:1", 2, 0.001); + cepis_address *address = NULL; + cepis_error err; + + begin("a refused connection is a network error"); + CHECK(cepis_lookup(client, "01001000", &address, &err) == + CEPIS_ERR_NETWORK); + CHECK(address == NULL); + CHECK(err.message[0] != '\0'); + cepis_client_free(client); +} + +static void test_health(void) +{ + cepis_client *client = client_at(base, 2, 0.001); + struct cepis_health *health = NULL; + + begin("health"); + CHECK(cepis_health(client, &health, NULL) == CEPIS_OK); + if (health != NULL) { + CHECK_STR(health->status, "ok"); + CHECK_STR(health->dne, "26071"); + CHECK(health->ceps == 1596168); + CHECK(health->geo == 894489); + CHECK_STR(health->base_gerada_em, "2026-07-24T03:48:28Z"); + CHECK(health->raw != NULL); + } + cepis_health_free(health); + cepis_client_free(client); +} + +static void test_health_degraded(void) +{ + cepis_client *client = client_at(urlf("%s/degraded", base), 2, 0.001); + struct cepis_health *health = NULL; + + begin("a degraded API is not an error"); + CHECK(cepis_health(client, &health, NULL) == CEPIS_OK); + if (health != NULL) + CHECK_STR(health->status, "degraded"); + CHECK(hits("degraded") == 1); + cepis_health_free(health); + cepis_client_free(client); +} + +static void test_batch_order(void) +{ + const char *ceps[] = {"01001000", "99999999", "20040020"}; + cepis_client *client = client_at(urlf("%s/batch", base), 2, 0.001); + cepis_address **out = NULL; + + begin("batch keeps order and nulls the missing entry"); + CHECK(cepis_lookup_many(client, ceps, 3, &out, NULL) == CEPIS_OK); + if (out != NULL) { + CHECK(out[0] != NULL); + CHECK(out[1] == NULL); + CHECK(out[2] != NULL); + if (out[0] != NULL) { + CHECK_STR(out[0]->localidade, "São Paulo"); + CHECK(out[0]->geo == NULL); + CHECK(out[0]->raw == NULL); + } + if (out[2] != NULL) + CHECK_STR(out[2]->localidade, "Rio de Janeiro"); + } + CHECK(hits("batch") == 1); + cepis_address_list_free(out, 3); + cepis_client_free(client); +} + +static void test_batch_quoting(void) +{ + const char *ceps[] = {"12345678"}; + cepis_client *client = client_at(base, 2, 0.001); + cepis_address **out = NULL; + + begin("batch parses quoted CSV fields"); + CHECK(cepis_lookup_many(client, ceps, 1, &out, NULL) == CEPIS_OK); + if (out != NULL && out[0] != NULL) { + CHECK_STR(out[0]->logradouro, "Rua A, B \"quoted\""); + CHECK_STR(out[0]->bairro, "Vila, Nova"); + CHECK_STR(out[0]->complemento, "até 100"); + } + cepis_address_list_free(out, 1); + cepis_client_free(client); +} + +static void test_batch_malformed_entry(void) +{ + const char *ceps[] = {"01001000", "abc", "20040020"}; + cepis_client *client = client_at(base, 2, 0.001); + cepis_address **out = NULL; + + begin("a malformed CEP in a batch is null, not an error"); + CHECK(cepis_lookup_many(client, ceps, 3, &out, NULL) == CEPIS_OK); + if (out != NULL) { + CHECK(out[0] != NULL); + CHECK(out[1] == NULL); + CHECK(out[2] != NULL); + } + cepis_address_list_free(out, 3); + cepis_client_free(client); +} + +static void test_batch_chunking(void) +{ + cepis_client *client = client_at(urlf("%s/chunk", base), 2, 0.001); + cepis_address **out = NULL; + const char **ceps; + char *storage; + size_t i; + size_t n = 501; + int ok = 1; + + begin("batches above 500 are chunked"); + ceps = malloc(n * sizeof *ceps); + storage = malloc(n * 9); + for (i = 0; i < n; i++) { + snprintf(storage + i * 9, 9, "%08zu", (size_t)70000000 + i); + ceps[i] = storage + i * 9; + } + CHECK(cepis_lookup_many(client, ceps, n, &out, NULL) == CEPIS_OK); + if (out != NULL) { + for (i = 0; i < n; i++) { + if (out[i] == NULL || strcmp(out[i]->cep, ceps[i]) != 0) { + ok = 0; + break; + } + } + CHECK(ok); + } + CHECK(hits("chunk") == 2); + cepis_address_list_free(out, n); + free(storage); + free(ceps); + cepis_client_free(client); +} + +static void test_batch_short_response(void) +{ + const char *ceps[] = {"01001000", "20040020", "12345678"}; + cepis_client *client = client_at(urlf("%s/short", base), 2, 0.001); + cepis_address **out = NULL; + + begin("a short batch response is padded, not shifted"); + CHECK(cepis_lookup_many(client, ceps, 3, &out, NULL) == CEPIS_OK); + if (out != NULL) { + CHECK(out[0] != NULL); + CHECK(out[1] == NULL); + CHECK(out[2] == NULL); + if (out[0] != NULL) + CHECK_STR(out[0]->cep, "01001000"); + } + cepis_address_list_free(out, 3); + cepis_client_free(client); +} + +static void test_batch_empty(void) +{ + cepis_client *client = client_at(base, 2, 0.001); + cepis_address **out = NULL; + + begin("an empty batch is a valid empty result"); + CHECK(cepis_lookup_many(client, NULL, 0, &out, NULL) == CEPIS_OK); + CHECK(out != NULL); + cepis_address_list_free(out, 0); + cepis_client_free(client); +} + +static void test_trailing_slash(void) +{ + cepis_client *client = client_at(urlf("%s/", base), 2, 0.001); + cepis_address *address = NULL; + + begin("a trailing slash in base_url is stripped"); + CHECK(cepis_lookup(client, "01001000", &address, NULL) == CEPIS_OK); + CHECK(address != NULL); + cepis_address_free(address); + cepis_client_free(client); +} + +int cepis_abi_probe(void *fn, void *a0, void *a1, void *a2, void *a3); + +static void probe(const char *name, void *fn, void *a0, void *a1, void *a2, + void *a3) +{ + int mask = cepis_abi_probe(fn, a0, a1, a2, a3); + + checks++; + if (mask != 0) { + failures++; + fprintf(stderr, " FAIL [%s]: %s violated the ABI, mask 0x%02x\n", + current, name, mask); + } +} + +static void test_abi(void) +{ + cepis_client *client = client_at(base, 2, 0.001); + cepis_address *address = NULL; + struct cepis_health *health = NULL; + cepis_address **many = NULL; + cepis_error err; + char out[16]; + + begin("callee-saved registers, stack pointer and DF survive every call"); + probe("cepis_normalize_cep", (void *)cepis_normalize_cep, + (void *)"01001-000", out, NULL, NULL); + probe("cepis_strerror", (void *)cepis_strerror, (void *)(long)4, NULL, + NULL, NULL); + probe("cepis_lookup(invalid)", (void *)cepis_lookup, client, (void *)"1", + &address, &err); + probe("cepis_lookup", (void *)cepis_lookup, client, (void *)"01001000", + &address, &err); + probe("cepis_address_free", (void *)cepis_address_free, address, NULL, + NULL, NULL); + probe("cepis_health", (void *)cepis_health, client, &health, &err, NULL); + probe("cepis_health_free", (void *)cepis_health_free, health, NULL, NULL, + NULL); + probe("cepis_lookup_many", (void *)cepis_lookup_many, client, NULL, NULL, + &many); + cepis_address_list_free(many, 0); + probe("cepis_default_client", (void *)cepis_default_client, NULL, NULL, + NULL, NULL); + probe("cepis_client_free", (void *)cepis_client_free, client, NULL, NULL, + NULL); +} + +static void test_usage(void) +{ + cepis_client *client = client_at(base, 2, 0.001); + cepis_config cfg; + cepis_client *bad = NULL; + + begin("argument errors"); + CHECK(cepis_lookup(client, "01001000", NULL, NULL) == CEPIS_ERR_USAGE); + CHECK(cepis_health(client, NULL, NULL) == CEPIS_ERR_USAGE); + CHECK(cepis_lookup_many(client, NULL, 0, NULL, NULL) == CEPIS_ERR_USAGE); + memset(&cfg, 0, sizeof cfg); + cfg.max_attempts = -1; + CHECK(cepis_client_new(&cfg, &bad) == CEPIS_ERR_USAGE); + CHECK(bad == NULL); + CHECK(strcmp(cepis_version(), CEPIS_VERSION) == 0); + CHECK(strcmp(cepis_strerror(CEPIS_ERR_NOT_FOUND), "CEP not found") == 0); + cepis_client_free(client); +} + +int main(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, "usage: %s <base-url>\n", argv[0]); + return 2; + } + base = argv[1]; + cepis_global_init(); + + test_normalize(); + test_lookup_geo(); + test_lookup_no_geo(); + test_escaped_json(); + test_surrogate_pair(); + test_csv_bom(); + test_not_found_not_retried(); + test_bad_cep_from_api(); + test_local_validation_makes_no_request(); + test_retry_then_success(); + test_retry_exhausted(); + test_rate_limited(); + test_rate_limited_http_date(); + test_retry_after_is_capped(); + test_total_timeout(); + test_invalid_response(); + test_bad_geo_kept_address(); + test_network_error(); + test_health(); + test_health_degraded(); + test_batch_order(); + test_batch_quoting(); + test_batch_malformed_entry(); + test_batch_chunking(); + test_batch_short_response(); + test_batch_empty(); + test_trailing_slash(); + test_abi(); + test_usage(); + + cepis_global_cleanup(); + + printf("\n%d checks, %d failures\n", checks, failures); + return failures == 0 ? 0 : 1; +} |