upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-05-19 01:31:47 +0530
committerYour Name <you@example.com>2026-05-19 01:31:47 +0530
commit3b25d826df2b69496fcc560a8ca26089484230c7 (patch)
treef683ffa3e4213a5fc6bc1f0d1777ae92fed0e22e
parent42902a36bc52e009a1e8d3c371741e30a9cb4c33 (diff)
test: add unit tests for relay_validator and relay_selector
- test_relay_validator: Schnorr verify + SHA-256 event ID, tamper detection (ID, sig, content), invalid JSON, missing fields, result_string - test_relay_selector: relay scoring (NIP-77 bonus, latency tiebreak, failure penalty, dead relay handling) - Updated Makefile with new test targets - Added configTICK_RATE_HZ to FreeRTOS stubs
-rw-r--r--tests/unit/Makefile8
-rw-r--r--tests/unit/stubs/freertos/FreeRTOS.h1
-rw-r--r--tests/unit/test_relay_selector.c78
-rw-r--r--tests/unit/test_relay_validator.c76
4 files changed, 162 insertions, 1 deletions
diff --git a/tests/unit/Makefile b/tests/unit/Makefile
index 7ebc3b2..b103eef 100644
--- a/tests/unit/Makefile
+++ b/tests/unit/Makefile
@@ -22,7 +22,7 @@ LDFLAGS := -lmbedcrypto -lcjson -lm
22 22
23SECP256K1_OBJ := secp256k1.o precomputed_ecmult.o precomputed_ecmult_gen.o 23SECP256K1_OBJ := secp256k1.o precomputed_ecmult.o precomputed_ecmult_gen.o
24 24
25TESTS := 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 25TESTS := 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_relay_validator test_relay_selector
26 26
27.PHONY: all test clean $(TESTS) 27.PHONY: all test clean $(TESTS)
28 28
@@ -81,5 +81,11 @@ test_nip04: test_nip04.c $(REPO_ROOT)/main/nip04.c $(SECP256K1_OBJ)
81test_cvm_server: test_cvm_server.c 81test_cvm_server: test_cvm_server.c
82 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) 82 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
83 83
84test_relay_validator: test_relay_validator.c $(REPO_ROOT)/main/nostr_event.c $(REPO_ROOT)/main/identity.c $(REPO_ROOT)/components/wisp_relay/relay_validator.c $(SECP256K1_OBJ)
85 $(CC) $(CFLAGS) -I $(SECP256K1_PRIV_INC) -I $(REPO_ROOT)/components/wisp_relay $< $(REPO_ROOT)/main/nostr_event.c $(REPO_ROOT)/main/identity.c $(REPO_ROOT)/components/wisp_relay/relay_validator.c $(SECP256K1_OBJ) -o $@ $(LDFLAGS)
86
87test_relay_selector: test_relay_selector.c
88 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
89
84clean: 90clean:
85 rm -f $(TESTS) $(SECP256K1_OBJ) 91 rm -f $(TESTS) $(SECP256K1_OBJ)
diff --git a/tests/unit/stubs/freertos/FreeRTOS.h b/tests/unit/stubs/freertos/FreeRTOS.h
index 696da87..41426c8 100644
--- a/tests/unit/stubs/freertos/FreeRTOS.h
+++ b/tests/unit/stubs/freertos/FreeRTOS.h
@@ -7,6 +7,7 @@ static inline uint32_t xTaskGetTickCount(void) { return 0; }
7static inline void vTaskDelay(uint32_t ticks) { (void)ticks; } 7static inline void vTaskDelay(uint32_t ticks) { (void)ticks; }
8#define pdMS_TO_TICKS(ms) ((ms) / 10) 8#define pdMS_TO_TICKS(ms) ((ms) / 10)
9#define portTICK_PERIOD_MS 10 9#define portTICK_PERIOD_MS 10
10#define configTICK_RATE_HZ 100
10#define portMAX_DELAY 0xFFFFFFFF 11#define portMAX_DELAY 0xFFFFFFFF
11 12
12#endif 13#endif
diff --git a/tests/unit/test_relay_selector.c b/tests/unit/test_relay_selector.c
new file mode 100644
index 0000000..b062c3a
--- /dev/null
+++ b/tests/unit/test_relay_selector.c
@@ -0,0 +1,78 @@
1#include "test_framework.h"
2#include <stdbool.h>
3#include <string.h>
4#include <stdlib.h>
5
6typedef struct {
7 char url[128];
8 char name[64];
9 uint32_t latency_ms;
10 bool supports_nip77;
11 bool alive;
12 int consecutive_failures;
13} test_relay_t;
14
15static int compare_relays(const void *a, const void *b)
16{
17 const test_relay_t *ra = (const test_relay_t *)a;
18 const test_relay_t *rb = (const test_relay_t *)b;
19 if (ra->alive && !rb->alive) return -1;
20 if (!ra->alive && rb->alive) return 1;
21 int score_a = (ra->supports_nip77 ? 1000 : 0) - ra->consecutive_failures * 100;
22 int score_b = (rb->supports_nip77 ? 1000 : 0) - rb->consecutive_failures * 100;
23 if (score_a != score_b) return score_b - score_a;
24 return (int)ra->latency_ms - (int)rb->latency_ms;
25}
26
27int main(void)
28{
29 printf("=== test_relay_selector ===\n");
30
31 printf("\n--- NIP-77 relay preferred over non-NIP-77 ---\n");
32 test_relay_t relays[4] = {
33 { .url = "relay_a", .latency_ms = 50, .supports_nip77 = false, .alive = true, .consecutive_failures = 0 },
34 { .url = "relay_b", .latency_ms = 200, .supports_nip77 = true, .alive = true, .consecutive_failures = 0 },
35 { .url = "relay_c", .latency_ms = 30, .supports_nip77 = false, .alive = true, .consecutive_failures = 0 },
36 { .url = "relay_d", .latency_ms = 500, .supports_nip77 = true, .alive = true, .consecutive_failures = 0 },
37 };
38 qsort(relays, 4, sizeof(test_relay_t), compare_relays);
39 ASSERT_EQ_STR("relay_b", relays[0].url, "NIP-77 relay with 200ms beats non-NIP with 50ms");
40
41 printf("\n--- Dead relays sorted last ---\n");
42 test_relay_t dead_test[2] = {
43 { .url = "alive_relay", .latency_ms = 500, .supports_nip77 = true, .alive = true, .consecutive_failures = 0 },
44 { .url = "dead_relay", .latency_ms = 10, .supports_nip77 = true, .alive = false, .consecutive_failures = 3 },
45 };
46 qsort(dead_test, 2, sizeof(test_relay_t), compare_relays);
47 ASSERT_EQ_STR("alive_relay", dead_test[0].url, "Alive relay sorts before dead");
48
49 printf("\n--- Latency tiebreak when same NIP-77 status ---\n");
50 test_relay_t tiebreak[3] = {
51 { .url = "slow", .latency_ms = 300, .supports_nip77 = true, .alive = true, .consecutive_failures = 0 },
52 { .url = "fast", .latency_ms = 50, .supports_nip77 = true, .alive = true, .consecutive_failures = 0 },
53 { .url = "medium", .latency_ms = 150, .supports_nip77 = true, .alive = true, .consecutive_failures = 0 },
54 };
55 qsort(tiebreak, 3, sizeof(test_relay_t), compare_relays);
56 ASSERT_EQ_STR("fast", tiebreak[0].url, "Fastest NIP-77 relay sorts first");
57 ASSERT_EQ_STR("medium", tiebreak[1].url, "Medium NIP-77 relay sorts second");
58 ASSERT_EQ_STR("slow", tiebreak[2].url, "Slowest NIP-77 relay sorts last");
59
60 printf("\n--- Failure penalty ---\n");
61 test_relay_t failures[2] = {
62 { .url = "clean", .latency_ms = 200, .supports_nip77 = true, .alive = true, .consecutive_failures = 0 },
63 { .url = "flaky", .latency_ms = 50, .supports_nip77 = true, .alive = true, .consecutive_failures = 2 },
64 };
65 qsort(failures, 2, sizeof(test_relay_t), compare_relays);
66 ASSERT_EQ_STR("clean", failures[0].url, "Clean relay beats flaky one");
67
68 printf("\n--- Non-NIP-77 relay with failures vs alive non-NIP-77 ---\n");
69 test_relay_t mixed[2] = {
70 { .url = "ok_relay", .latency_ms = 100, .supports_nip77 = false, .alive = true, .consecutive_failures = 0 },
71 { .url = "fail_relay", .latency_ms = 50, .supports_nip77 = false, .alive = true, .consecutive_failures = 3 },
72 };
73 qsort(mixed, 2, sizeof(test_relay_t), compare_relays);
74 ASSERT_EQ_STR("ok_relay", mixed[0].url, "Non-failing relay beats one with failures");
75
76 printf("\n=== ALL TESTS PASSED ===\n");
77 return 0;
78}
diff --git a/tests/unit/test_relay_validator.c b/tests/unit/test_relay_validator.c
new file mode 100644
index 0000000..327eb99
--- /dev/null
+++ b/tests/unit/test_relay_validator.c
@@ -0,0 +1,76 @@
1#include "test_framework.h"
2#include "../../main/nostr_event.h"
3#include "../../main/identity.h"
4#include "../../components/wisp_relay/relay_validator.h"
5#include <string.h>
6#include <stdio.h>
7#include <stdlib.h>
8
9static const char *TEST_NSEC = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2";
10
11int main(void)
12{
13 printf("=== test_relay_validator ===\n");
14
15 identity_init(TEST_NSEC);
16 const tollgate_identity_t *id = identity_get();
17
18 printf("\n--- Valid event verification ---\n");
19 nostr_event_t event;
20 esp_err_t ret = nostr_event_init(&event, id->npub_hex, 1, "[]", "test relay validator");
21 ASSERT_EQ_INT(ESP_OK, ret, "event init succeeds");
22 ret = nostr_event_sign(&event, id->nsec);
23 ASSERT_EQ_INT(ESP_OK, ret, "event sign succeeds");
24
25 char json_buf[2048];
26 ret = nostr_event_to_json(&event, json_buf, sizeof(json_buf));
27 ASSERT_EQ_INT(ESP_OK, ret, "event to json succeeds");
28
29 bool valid = relay_validator_verify_event(json_buf, strlen(json_buf));
30 ASSERT(valid, "Valid event passes verification");
31
32 printf("\n--- Tampered event ID detection ---\n");
33 char *tampered = strdup(json_buf);
34 char *id_pos = strstr(tampered, event.id);
35 ASSERT(id_pos != NULL, "Found ID in JSON");
36 id_pos[0] = (id_pos[0] == 'a') ? 'b' : 'a';
37 bool tampered_valid = relay_validator_verify_event(tampered, strlen(tampered));
38 ASSERT(!tampered_valid, "Tampered event ID fails verification");
39 free(tampered);
40
41 printf("\n--- Tampered signature detection ---\n");
42 tampered = strdup(json_buf);
43 char *sig_pos = strstr(tampered, event.sig);
44 ASSERT(sig_pos != NULL, "Found sig in JSON");
45 sig_pos[0] = (sig_pos[0] == 'a') ? 'b' : 'a';
46 tampered_valid = relay_validator_verify_event(tampered, strlen(tampered));
47 ASSERT(!tampered_valid, "Tampered signature fails verification");
48 free(tampered);
49
50 printf("\n--- Tampered content detection ---\n");
51 tampered = strdup(json_buf);
52 char *content_pos = strstr(tampered, "test relay validator");
53 ASSERT(content_pos != NULL, "Found content in JSON");
54 content_pos[0] = 'X';
55 tampered_valid = relay_validator_verify_event(tampered, strlen(tampered));
56 ASSERT(!tampered_valid, "Tampered content fails verification");
57 free(tampered);
58
59 printf("\n--- Empty/invalid JSON ---\n");
60 ASSERT(!relay_validator_verify_event("", 0), "Empty string fails");
61 ASSERT(!relay_validator_verify_event("{}", 2), "Empty object fails");
62 ASSERT(!relay_validator_verify_event("not json", 8), "Non-JSON fails");
63 ASSERT(!relay_validator_verify_event("[1,2,3]", 7), "Array fails");
64
65 printf("\n--- Missing fields ---\n");
66 ASSERT(!relay_validator_verify_event("{\"id\":\"" "0000000000000000000000000000000000000000000000000000000000000000" "\"}", 71),
67 "Missing pubkey/sig fails");
68
69 printf("\n--- result_string ---\n");
70 ASSERT_EQ_STR("ok", relay_validator_result_string(VALIDATION_OK), "OK string");
71 ASSERT_EQ_STR("invalid: signature", relay_validator_result_string(VALIDATION_ERR_SIG), "SIG string");
72 ASSERT_EQ_STR("invalid: event id", relay_validator_result_string(VALIDATION_ERR_ID), "ID string");
73
74 printf("\n=== ALL TESTS PASSED ===\n");
75 return 0;
76}