From 78d0c3795e9a90a9f99178e38346a53d9b2ebe57 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 18 May 2026 19:14:45 +0530 Subject: Add test_mint_health: 14 host unit tests for mint health module Tests: init, get_all, initial state, is_reachable, mark_unreachable, overflow handling, empty init, callback registration, reinit, start/stop. Adds freertos/semphr.h stub and pdTRUE to FreeRTOS.h stub. --- tests/unit/Makefile | 5 +- tests/unit/stubs/freertos/FreeRTOS.h | 1 + tests/unit/stubs/freertos/semphr.h | 7 ++ tests/unit/test_mint_health.c | 194 +++++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 tests/unit/stubs/freertos/semphr.h create mode 100644 tests/unit/test_mint_health.c diff --git a/tests/unit/Makefile b/tests/unit/Makefile index f47ef23..fc84c4e 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -22,7 +22,7 @@ LDFLAGS := -lmbedcrypto -lcjson -lm SECP256K1_OBJ := secp256k1.o precomputed_ecmult.o precomputed_ecmult_gen.o -TESTS := test_geohash test_identity test_nostr_event test_cashu test_session test_tollgate_client test_lnurl_pay test_lightning_payout test_mcp_handler test_nip04 test_cvm_server +TESTS := test_geohash test_identity test_nostr_event test_cashu test_session test_tollgate_client test_lnurl_pay test_lightning_payout test_mcp_handler test_nip04 test_cvm_server test_mint_health .PHONY: all test clean $(TESTS) @@ -81,5 +81,8 @@ test_nip04: test_nip04.c $(REPO_ROOT)/main/nip04.c $(SECP256K1_OBJ) test_cvm_server: test_cvm_server.c $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) +test_mint_health: test_mint_health.c $(REPO_ROOT)/main/mint_health.c + $(CC) -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -Wno-sign-compare -std=gnu17 -g -O0 -DTEST_HOST -include stubs/esp_err.h -I $(REPO_ROOT)/main -I stubs -I $(SECP256K1_INC) -I $(SECP256K1_CFG) -I /usr/include/cjson $< $(REPO_ROOT)/main/mint_health.c -o $@ $(LDFLAGS) + clean: rm -f $(TESTS) $(SECP256K1_OBJ) diff --git a/tests/unit/stubs/freertos/FreeRTOS.h b/tests/unit/stubs/freertos/FreeRTOS.h index 696da87..2d2b967 100644 --- a/tests/unit/stubs/freertos/FreeRTOS.h +++ b/tests/unit/stubs/freertos/FreeRTOS.h @@ -8,5 +8,6 @@ static inline void vTaskDelay(uint32_t ticks) { (void)ticks; } #define pdMS_TO_TICKS(ms) ((ms) / 10) #define portTICK_PERIOD_MS 10 #define portMAX_DELAY 0xFFFFFFFF +#define pdTRUE 1 #endif diff --git a/tests/unit/stubs/freertos/semphr.h b/tests/unit/stubs/freertos/semphr.h new file mode 100644 index 0000000..0389b11 --- /dev/null +++ b/tests/unit/stubs/freertos/semphr.h @@ -0,0 +1,7 @@ +#ifndef STUBS_FREERTOS_SEMPHR_H +#define STUBS_FREERTOS_SEMPHR_H + +#include "FreeRTOS.h" +#include "task.h" + +#endif diff --git a/tests/unit/test_mint_health.c b/tests/unit/test_mint_health.c new file mode 100644 index 0000000..d170d55 --- /dev/null +++ b/tests/unit/test_mint_health.c @@ -0,0 +1,194 @@ +#include +#include +#include +#include "mint_health.h" + +static int test_count = 0; +static int pass_count = 0; + +#define TEST(name) do { \ + test_count++; \ + printf(" TEST: %s ... ", name); \ +} while(0) + +#define PASS() do { \ + pass_count++; \ + printf("PASS\n"); \ +} while(0) + +#define FAIL(msg) do { \ + printf("FAIL: %s\n", msg); \ +} while(0) + +#define ASSERT_EQ(a, b, msg) do { \ + if ((a) != (b)) { FAIL(msg); return; } \ +} while(0) + +#define ASSERT_TRUE(a, msg) do { \ + if (!(a)) { FAIL(msg); return; } \ +} while(0) + +#define ASSERT_FALSE(a, msg) do { \ + if ((a)) { FAIL(msg); return; } \ +} while(0) + +static void test_init_basic(void) { + TEST("init with 4 mints"); + const char urls[4][256] = { + "https://mint.minibits.cash/Bitcoin", + "https://mint.coinos.io", + "https://21mint.me", + "https://mint.lnvoltz.com" + }; + esp_err_t err = mint_health_init(urls, 4); + ASSERT_EQ(err, 0, "init should return ESP_OK"); + PASS(); +} + +static void test_get_all(void) { + TEST("get_all returns correct count"); + int count = 0; + const mint_status_t *mints = mint_health_get_all(&count); + ASSERT_EQ(count, 4, "should have 4 mints"); + ASSERT_TRUE(mints != NULL, "mints should not be NULL"); + PASS(); +} + +static void test_initial_state_unreachable(void) { + TEST("initial state: all mints unreachable (no probes run)"); + const char *expected_urls[] = { + "https://mint.minibits.cash/Bitcoin", + "https://mint.coinos.io", + "https://21mint.me", + "https://mint.lnvoltz.com" + }; + int count = 0; + const mint_status_t *mints = mint_health_get_all(&count); + ASSERT_EQ(count, 4, "should have 4 mints"); + for (int i = 0; i < count; i++) { + ASSERT_FALSE(mints[i].reachable, "initial mint should be unreachable"); + ASSERT_EQ(mints[i].consecutive_successes, 0, "initial successes should be 0"); + ASSERT_TRUE(strcmp(mints[i].url, expected_urls[i]) == 0, "URL mismatch"); + } + PASS(); +} + +static void test_is_reachable_before_probes(void) { + TEST("is_reachable returns false before probes"); + bool r = mint_health_is_reachable("https://mint.minibits.cash/Bitcoin"); + ASSERT_FALSE(r, "should be unreachable before probes"); + PASS(); +} + +static void test_is_reachable_null(void) { + TEST("is_reachable returns false for NULL"); + bool r = mint_health_is_reachable(NULL); + ASSERT_FALSE(r, "NULL should return false"); + PASS(); +} + +static void test_is_reachable_unknown_url(void) { + TEST("is_reachable returns false for unknown URL"); + bool r = mint_health_is_reachable("https://unknown.mint.example.com"); + ASSERT_FALSE(r, "unknown URL should return false"); + PASS(); +} + +static void test_mark_unreachable(void) { + TEST("mark_unreachable on already-unreachable mint"); + mint_health_mark_unreachable("https://mint.coinos.io"); + bool r = mint_health_is_reachable("https://mint.coinos.io"); + ASSERT_FALSE(r, "should still be unreachable"); + PASS(); +} + +static void test_mark_unreachable_null(void) { + TEST("mark_unreachable with NULL does not crash"); + mint_health_mark_unreachable(NULL); + PASS(); +} + +static void test_init_overflow(void) { + TEST("init with more than MAX mints truncates"); + const char urls[MINT_HEALTH_MAX + 2][256]; + for (int i = 0; i < MINT_HEALTH_MAX + 2; i++) { + snprintf((char *)urls[i], 256, "https://mint%d.example.com", i); + } + esp_err_t err = mint_health_init(urls, MINT_HEALTH_MAX + 2); + ASSERT_EQ(err, 0, "init should succeed"); + + int count = 0; + mint_health_get_all(&count); + ASSERT_EQ(count, MINT_HEALTH_MAX, "should be truncated to MAX"); + PASS(); +} + +static void test_init_empty(void) { + TEST("init with 0 mints"); + esp_err_t err = mint_health_init(NULL, 0); + ASSERT_EQ(err, 0, "init with 0 should succeed"); + + int count = -1; + mint_health_get_all(&count); + ASSERT_EQ(count, 0, "should have 0 mints"); + PASS(); +} + +static void dummy_cb(void) { } + +static void test_register_callback(void) { + TEST("register_callback does not crash"); + mint_health_register_callback(dummy_cb); + PASS(); +} + +static void test_register_callback_null(void) { + TEST("register_callback NULL does not crash"); + mint_health_register_callback(NULL); + PASS(); +} + +static void test_reinit_resets_state(void) { + TEST("re-init resets state"); + const char urls[2][256] = { + "https://mint-a.example.com", + "https://mint-b.example.com" + }; + mint_health_init(urls, 2); + + int count = 0; + const mint_status_t *mints = mint_health_get_all(&count); + ASSERT_EQ(count, 2, "should have 2 mints"); + ASSERT_TRUE(strcmp(mints[0].url, "https://mint-a.example.com") == 0, "first URL"); + ASSERT_TRUE(strcmp(mints[1].url, "https://mint-b.example.com") == 0, "second URL"); + PASS(); +} + +static void test_start_stop(void) { + TEST("start/stop do not crash (task stubbed)"); + mint_health_start(); + mint_health_stop(); + PASS(); +} + +int main(void) { + printf("\n=== Mint Health Unit Tests ===\n\n"); + + test_init_basic(); + test_get_all(); + test_initial_state_unreachable(); + test_is_reachable_before_probes(); + test_is_reachable_null(); + test_is_reachable_unknown_url(); + test_mark_unreachable(); + test_mark_unreachable_null(); + test_init_overflow(); + test_init_empty(); + test_register_callback(); + test_register_callback_null(); + test_reinit_resets_state(); + test_start_stop(); + + printf("\n=== Results: %d passed, %d failed ===\n\n", pass_count, test_count - pass_count); + return (pass_count == test_count) ? 0 : 1; +} -- cgit v1.2.3