upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-05-23 23:24:07 +0530
committerYour Name <you@example.com>2026-05-23 23:24:07 +0530
commite40645e07e4076610abc8270fba421dbdd8fc217 (patch)
tree81dc395d5983461d44e032da50dece540831e8f0 /tests
parentbf8e095b94fe500a9c35d7f2f616eb85569dbe5e (diff)
test: add relay_types, sub_manager, deletion unit tests
- test_relay_types: hex_to_bytes/bytes_to_hex with known vectors, roundtrip, edge cases (17 assertions) - test_sub_manager: hex_prefix_match, filter_matches_event for NIP-01 kind/author/since/until filtering (23 assertions) - test_deletion: extract_event_id_field, extract_pubkey_hex with valid/invalid JSON vectors (14 assertions) - All 28 test files pass, 0 failures
Diffstat (limited to 'tests')
-rw-r--r--tests/unit/Makefile11
-rw-r--r--tests/unit/test_deletion.c116
-rw-r--r--tests/unit/test_relay_types.c114
-rw-r--r--tests/unit/test_sub_manager.c162
4 files changed, 402 insertions, 1 deletions
diff --git a/tests/unit/Makefile b/tests/unit/Makefile
index 591ec0f..2d89859 100644
--- a/tests/unit/Makefile
+++ b/tests/unit/Makefile
@@ -26,7 +26,7 @@ LDFLAGS := -lmbedcrypto -lcjson -lm
26 26
27SECP256K1_OBJ := secp256k1.o precomputed_ecmult.o precomputed_ecmult_gen.o 27SECP256K1_OBJ := secp256k1.o precomputed_ecmult.o precomputed_ecmult_gen.o
28 28
29TESTS := 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_beacon_price test_market test_mint_health test_mining_payment test_stratum_proxy test_session_payment_method test_tollgate_client_mining test_firewall_sandbox test_relay_selector test_relay_validator test_portal test_stratum_client test_mint_health_core test_client_core 29TESTS := 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_beacon_price test_market test_mint_health test_mining_payment test_stratum_proxy test_session_payment_method test_tollgate_client_mining test_firewall_sandbox test_relay_selector test_relay_validator test_portal test_stratum_client test_mint_health_core test_client_core test_relay_types test_sub_manager test_deletion
30 30
31.PHONY: all test clean $(TESTS) 31.PHONY: all test clean $(TESTS)
32 32
@@ -129,5 +129,14 @@ test_mint_health_core: test_mint_health_core.c $(TG_CORE_SRC)/tollgate_core_mint
129test_client_core: test_client_core.c $(TG_CORE_SRC)/tollgate_core_client.c 129test_client_core: test_client_core.c $(TG_CORE_SRC)/tollgate_core_client.c
130 $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) 130 $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
131 131
132test_relay_types: test_relay_types.c $(REPO_ROOT)/components/wisp_relay/relay_types.c
133 $(CC) $(CFLAGS) -I $(REPO_ROOT)/components/wisp_relay $^ -o $@ $(LDFLAGS)
134
135test_sub_manager: test_sub_manager.c
136 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
137
138test_deletion: test_deletion.c $(REPO_ROOT)/components/wisp_relay/relay_types.c
139 $(CC) $(CFLAGS) -I $(REPO_ROOT)/components/wisp_relay $^ -o $@ $(LDFLAGS)
140
132clean: 141clean:
133 rm -f $(TESTS) $(SECP256K1_OBJ) 142 rm -f $(TESTS) $(SECP256K1_OBJ)
diff --git a/tests/unit/test_deletion.c b/tests/unit/test_deletion.c
new file mode 100644
index 0000000..c5a7ad3
--- /dev/null
+++ b/tests/unit/test_deletion.c
@@ -0,0 +1,116 @@
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <stdint.h>
5
6#define ASSERT(cond, msg) do { if (!(cond)) { printf("FAIL: %s (%s:%d)\n", msg, __FILE__, __LINE__); return 1; } } while(0)
7
8#include "relay_types.h"
9#include <cJSON.h>
10
11static int extract_event_id_field(const char *event_json, size_t len, uint8_t id_out[32])
12{
13 cJSON *obj = cJSON_ParseWithLength(event_json, len);
14 if (!obj) return -1;
15 cJSON *id_item = cJSON_GetObjectItem(obj, "id");
16 if (!id_item || !cJSON_IsString(id_item) || strlen(id_item->valuestring) != 64) {
17 cJSON_Delete(obj);
18 return -1;
19 }
20 int ret = relay_hex_to_bytes(id_item->valuestring, 64, id_out, 32);
21 cJSON_Delete(obj);
22 return ret;
23}
24
25static char *extract_pubkey_hex(const char *event_json, size_t len)
26{
27 cJSON *obj = cJSON_ParseWithLength(event_json, len);
28 if (!obj) return NULL;
29 cJSON *pk = cJSON_GetObjectItem(obj, "pubkey");
30 char *result = NULL;
31 if (pk && cJSON_IsString(pk)) result = strdup(pk->valuestring);
32 cJSON_Delete(obj);
33 return result;
34}
35
36int main(void)
37{
38 int passed = 0;
39
40 printf("--- extract_event_id_field valid ---\n");
41 {
42 const char *json = "{\"id\":\"0000000000000000000000000000000000000000000000000000000000000001\"}";
43 uint8_t id[32] = {0};
44 int ret = extract_event_id_field(json, strlen(json), id);
45 ASSERT(ret == 0, "extract should succeed");
46 ASSERT(id[31] == 0x01, "last byte should be 1");
47 passed += 2;
48 }
49
50 printf("--- extract_event_id_field missing id ---\n");
51 {
52 const char *json = "{\"kind\":1}";
53 uint8_t id[32] = {0};
54 int ret = extract_event_id_field(json, strlen(json), id);
55 ASSERT(ret == -1, "missing id should fail");
56 passed++;
57 }
58
59 printf("--- extract_event_id_field short id ---\n");
60 {
61 const char *json = "{\"id\":\"abcd\"}";
62 uint8_t id[32] = {0};
63 int ret = extract_event_id_field(json, strlen(json), id);
64 ASSERT(ret == -1, "short id should fail");
65 passed++;
66 }
67
68 printf("--- extract_event_id_field invalid json ---\n");
69 {
70 uint8_t id[32] = {0};
71 int ret = extract_event_id_field("not json", 8, id);
72 ASSERT(ret == -1, "invalid json should fail");
73 passed++;
74 }
75
76 printf("--- extract_event_id_field known vector ---\n");
77 {
78 const char *json = "{\"id\":\"deadbeef00000000000000000000000000000000000000000000000000000000\"}";
79 uint8_t id[32] = {0};
80 int ret = extract_event_id_field(json, strlen(json), id);
81 ASSERT(ret == 0, "known vector should succeed");
82 ASSERT(id[0] == 0xde, "byte 0");
83 ASSERT(id[1] == 0xad, "byte 1");
84 ASSERT(id[2] == 0xbe, "byte 2");
85 ASSERT(id[3] == 0xef, "byte 3");
86 passed += 5;
87 }
88
89 printf("--- extract_pubkey_hex valid ---\n");
90 {
91 const char *json = "{\"pubkey\":\"abcdef1234567890\"}";
92 char *pk = extract_pubkey_hex(json, strlen(json));
93 ASSERT(pk != NULL, "pubkey should not be null");
94 ASSERT(strcmp(pk, "abcdef1234567890") == 0, "pubkey value");
95 free(pk);
96 passed += 2;
97 }
98
99 printf("--- extract_pubkey_hex missing ---\n");
100 {
101 const char *json = "{\"kind\":1}";
102 char *pk = extract_pubkey_hex(json, strlen(json));
103 ASSERT(pk == NULL, "missing pubkey should return null");
104 passed++;
105 }
106
107 printf("--- extract_pubkey_hex invalid json ---\n");
108 {
109 char *pk = extract_pubkey_hex("not json", 8);
110 ASSERT(pk == NULL, "invalid json should return null");
111 passed++;
112 }
113
114 printf("\n=== Results: %d passed, 0 failed ===\n", passed);
115 return 0;
116}
diff --git a/tests/unit/test_relay_types.c b/tests/unit/test_relay_types.c
new file mode 100644
index 0000000..a7aa590
--- /dev/null
+++ b/tests/unit/test_relay_types.c
@@ -0,0 +1,114 @@
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <stdint.h>
5
6#define ASSERT(cond, msg) do { if (!(cond)) { printf("FAIL: %s (%s:%d)\n", msg, __FILE__, __LINE__); return 1; } } while(0)
7
8#include "relay_types.h"
9
10int main(void)
11{
12 int passed = 0, failed = 0;
13
14 printf("--- relay_hex_to_bytes valid ---\n");
15 {
16 const char *hex = "deadbeef01020304050607080910111213141516171819202122232425262728";
17 uint8_t out[32];
18 int ret = relay_hex_to_bytes(hex, 64, out, 32);
19 ASSERT(ret == 0, "hex_to_bytes should return 0");
20 ASSERT(out[0] == 0xde, "first byte");
21 ASSERT(out[1] == 0xad, "second byte");
22 ASSERT(out[2] == 0xbe, "third byte");
23 ASSERT(out[3] == 0xef, "fourth byte");
24 ASSERT(out[31] == 0x28, "last byte");
25 passed += 6;
26 printf(" PASS: relay_hex_to_bytes valid\n");
27 }
28
29 printf("--- relay_hex_to_bytes length mismatch ---\n");
30 {
31 uint8_t out[4];
32 int ret = relay_hex_to_bytes("dead", 4, out, 3);
33 ASSERT(ret == -1, "wrong output length should fail");
34 ret = relay_hex_to_bytes("dead", 2, out, 4);
35 ASSERT(ret == -1, "short hex should fail");
36 passed += 2;
37 printf(" PASS: length mismatch\n");
38 }
39
40 printf("--- relay_hex_to_bytes invalid chars ---\n");
41 {
42 uint8_t out[2];
43 int ret = relay_hex_to_bytes("zz", 2, out, 1);
44 ASSERT(ret == -1, "invalid hex chars should fail");
45 passed += 1;
46 printf(" PASS: invalid chars\n");
47 }
48
49 printf("--- relay_hex_to_bytes zeros ---\n");
50 {
51 uint8_t out[4];
52 int ret = relay_hex_to_bytes("00000000", 8, out, 4);
53 ASSERT(ret == 0, "zeros should succeed");
54 ASSERT(out[0] == 0 && out[1] == 0 && out[2] == 0 && out[3] == 0, "all zeros");
55 passed += 2;
56 printf(" PASS: zeros\n");
57 }
58
59 printf("--- relay_hex_to_bytes ff ---\n");
60 {
61 uint8_t out[2];
62 int ret = relay_hex_to_bytes("ffff", 4, out, 2);
63 ASSERT(ret == 0, "ff should succeed");
64 ASSERT(out[0] == 0xff && out[1] == 0xff, "all ff");
65 passed += 2;
66 printf(" PASS: ff\n");
67 }
68
69 printf("--- relay_bytes_to_hex basic ---\n");
70 {
71 uint8_t bytes[] = {0xde, 0xad, 0xbe, 0xef};
72 char hex[9];
73 relay_bytes_to_hex(bytes, 4, hex);
74 ASSERT(strcmp(hex, "deadbeef") == 0, "bytes_to_hex result");
75 ASSERT(hex[8] == '\0', "null terminator");
76 passed += 2;
77 printf(" PASS: relay_bytes_to_hex basic\n");
78 }
79
80 printf("--- relay_bytes_to_hex zeros ---\n");
81 {
82 uint8_t bytes[] = {0x00, 0x00};
83 char hex[5];
84 relay_bytes_to_hex(bytes, 2, hex);
85 ASSERT(strcmp(hex, "0000") == 0, "zeros hex");
86 passed += 1;
87 printf(" PASS: zeros hex\n");
88 }
89
90 printf("--- relay_bytes_to_hex single byte ---\n");
91 {
92 uint8_t bytes[] = {0x0a};
93 char hex[3];
94 relay_bytes_to_hex(bytes, 1, hex);
95 ASSERT(strcmp(hex, "0a") == 0, "single byte hex");
96 passed += 1;
97 printf(" PASS: single byte\n");
98 }
99
100 printf("--- roundtrip hex <-> bytes ---\n");
101 {
102 const char *original = "0102030405060708091011121314151617181920212223242526272829303132";
103 uint8_t buf[32];
104 char hex[65];
105 relay_hex_to_bytes(original, 64, buf, 32);
106 relay_bytes_to_hex(buf, 32, hex);
107 ASSERT(strcmp(hex, original) == 0, "roundtrip matches");
108 passed += 1;
109 printf(" PASS: roundtrip\n");
110 }
111
112 printf("\n=== Results: %d passed, %d failed ===\n", passed, failed);
113 return failed > 0 ? 1 : 0;
114}
diff --git a/tests/unit/test_sub_manager.c b/tests/unit/test_sub_manager.c
new file mode 100644
index 0000000..1a39a67
--- /dev/null
+++ b/tests/unit/test_sub_manager.c
@@ -0,0 +1,162 @@
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <stdint.h>
5#include <stdbool.h>
6
7#define ASSERT(cond, msg) do { if (!(cond)) { printf("FAIL: %s (%s:%d)\n", msg, __FILE__, __LINE__); return 1; } } while(0)
8
9static bool hex_prefix_match(const char *prefix, size_t prefix_len,
10 const char *full, size_t full_len)
11{
12 if (prefix_len == 0) return true;
13 if (prefix_len > full_len) return false;
14 return memcmp(prefix, full, prefix_len) == 0;
15}
16
17typedef struct {
18 char *authors[4];
19 size_t authors_count;
20 int32_t kinds[4];
21 size_t kinds_count;
22 int64_t since;
23 int64_t until;
24} test_filter_t;
25
26static bool filter_matches_event(const test_filter_t *f, int event_kind,
27 const char *pubkey_hex, uint64_t created_at)
28{
29 if (f->kinds_count > 0) {
30 bool found = false;
31 for (size_t i = 0; i < f->kinds_count; i++) {
32 if (f->kinds[i] == event_kind) { found = true; break; }
33 }
34 if (!found) return false;
35 }
36 if (f->authors_count > 0) {
37 bool found = false;
38 for (size_t i = 0; i < f->authors_count; i++) {
39 if (hex_prefix_match(f->authors[i], strlen(f->authors[i]),
40 pubkey_hex, strlen(pubkey_hex))) {
41 found = true; break;
42 }
43 }
44 if (!found) return false;
45 }
46 if (f->since > 0 && (int64_t)created_at < f->since) return false;
47 if (f->until > 0 && (int64_t)created_at > f->until) return false;
48 return true;
49}
50
51int main(void)
52{
53 int passed = 0;
54
55 printf("--- hex_prefix_match empty prefix ---\n");
56 {
57 ASSERT(hex_prefix_match("", 0, "abcdef", 6) == true, "empty prefix matches");
58 passed++;
59 }
60
61 printf("--- hex_prefix_match exact match ---\n");
62 {
63 ASSERT(hex_prefix_match("abc", 3, "abcdef", 6) == true, "prefix match");
64 passed++;
65 }
66
67 printf("--- hex_prefix_match no match ---\n");
68 {
69 ASSERT(hex_prefix_match("xyz", 3, "abcdef", 6) == false, "no match");
70 passed++;
71 }
72
73 printf("--- hex_prefix_match prefix longer than full ---\n");
74 {
75 ASSERT(hex_prefix_match("abcdefgh", 8, "abc", 3) == false, "prefix too long");
76 passed++;
77 }
78
79 printf("--- hex_prefix_match exact full ---\n");
80 {
81 ASSERT(hex_prefix_match("abcdef", 6, "abcdef", 6) == true, "exact match");
82 passed++;
83 }
84
85 printf("--- filter_matches_event empty filter ---\n");
86 {
87 test_filter_t f = {0};
88 ASSERT(filter_matches_event(&f, 1, "abc", 100) == true, "empty filter matches all");
89 passed++;
90 }
91
92 printf("--- filter_matches_event kind match ---\n");
93 {
94 test_filter_t f = {0};
95 f.kinds[0] = 1;
96 f.kinds_count = 1;
97 ASSERT(filter_matches_event(&f, 1, "abc", 100) == true, "kind 1 matches");
98 ASSERT(filter_matches_event(&f, 2, "abc", 100) == false, "kind 2 no match");
99 passed += 2;
100 }
101
102 printf("--- filter_matches_event multiple kinds ---\n");
103 {
104 test_filter_t f = {0};
105 f.kinds[0] = 1;
106 f.kinds[1] = 5;
107 f.kinds_count = 2;
108 ASSERT(filter_matches_event(&f, 5, "abc", 100) == true, "kind 5 in list");
109 ASSERT(filter_matches_event(&f, 3, "abc", 100) == false, "kind 3 not in list");
110 passed += 2;
111 }
112
113 printf("--- filter_matches_event author match ---\n");
114 {
115 test_filter_t f = {0};
116 f.authors[0] = "abcd";
117 f.authors_count = 1;
118 ASSERT(filter_matches_event(&f, 1, "abcdef", 100) == true, "author prefix match");
119 ASSERT(filter_matches_event(&f, 1, "xyz123", 100) == false, "author no match");
120 passed += 2;
121 }
122
123 printf("--- filter_matches_event since ---\n");
124 {
125 test_filter_t f = {0};
126 f.since = 50;
127 ASSERT(filter_matches_event(&f, 1, "abc", 100) == true, "created_at >= since");
128 ASSERT(filter_matches_event(&f, 1, "abc", 49) == false, "created_at < since");
129 ASSERT(filter_matches_event(&f, 1, "abc", 50) == true, "created_at == since");
130 passed += 3;
131 }
132
133 printf("--- filter_matches_event until ---\n");
134 {
135 test_filter_t f = {0};
136 f.until = 100;
137 ASSERT(filter_matches_event(&f, 1, "abc", 99) == true, "created_at <= until");
138 ASSERT(filter_matches_event(&f, 1, "abc", 101) == false, "created_at > until");
139 ASSERT(filter_matches_event(&f, 1, "abc", 100) == true, "created_at == until");
140 passed += 3;
141 }
142
143 printf("--- filter_matches_event combined ---\n");
144 {
145 test_filter_t f = {0};
146 f.kinds[0] = 1;
147 f.kinds_count = 1;
148 f.since = 50;
149 f.until = 150;
150 f.authors[0] = "abc";
151 f.authors_count = 1;
152 ASSERT(filter_matches_event(&f, 1, "abcdef", 100) == true, "all match");
153 ASSERT(filter_matches_event(&f, 2, "abcdef", 100) == false, "wrong kind");
154 ASSERT(filter_matches_event(&f, 1, "xyz", 100) == false, "wrong author");
155 ASSERT(filter_matches_event(&f, 1, "abcdef", 40) == false, "before since");
156 ASSERT(filter_matches_event(&f, 1, "abcdef", 160) == false, "after until");
157 passed += 5;
158 }
159
160 printf("\n=== Results: %d passed, 0 failed ===\n", passed);
161 return 0;
162}