upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests/unit
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit')
-rw-r--r--tests/unit/Makefile26
-rw-r--r--tests/unit/test_client_core.c189
-rw-r--r--tests/unit/test_mint_health_core.c130
-rw-r--r--tests/unit/test_portal.c153
-rw-r--r--tests/unit/test_stratum_client.c125
5 files changed, 616 insertions, 7 deletions
diff --git a/tests/unit/Makefile b/tests/unit/Makefile
index 9347516..591ec0f 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 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
30 30
31.PHONY: all test clean $(TESTS) 31.PHONY: all test clean $(TESTS)
32 32
@@ -69,8 +69,8 @@ TG_CORE_OBJS_CFLAGS := -include stubs/mint_health.h -include stubs/esp_netif.h -
69test_session: test_session.c $(TG_CORE_SRC)/tollgate_core_cashu.c $(TG_CORE_SRC)/tollgate_core_session.c $(TG_CORE_SRC)/tollgate_core_firewall.c $(TG_CORE_SRC)/tollgate_core.c $(TG_CORE_SRC)/tollgate_core_mining.c $(TG_CORE_SRC)/tollgate_core_dns.c $(TG_CORE_SRC)/tollgate_core_stratum_proxy.c 69test_session: test_session.c $(TG_CORE_SRC)/tollgate_core_cashu.c $(TG_CORE_SRC)/tollgate_core_session.c $(TG_CORE_SRC)/tollgate_core_firewall.c $(TG_CORE_SRC)/tollgate_core.c $(TG_CORE_SRC)/tollgate_core_mining.c $(TG_CORE_SRC)/tollgate_core_dns.c $(TG_CORE_SRC)/tollgate_core_stratum_proxy.c
70 $(CC) $(CFLAGS) $(TG_CORE_OBJS_CFLAGS) $^ -o $@ $(LDFLAGS) 70 $(CC) $(CFLAGS) $(TG_CORE_OBJS_CFLAGS) $^ -o $@ $(LDFLAGS)
71 71
72test_tollgate_client: test_tollgate_client.c $(REPO_ROOT)/main/market.c $(REPO_ROOT)/main/beacon_price.c $(TG_CORE_SRC)/tollgate_core_beacon.c $(TG_CORE_SRC)/tollgate_core_market.c 72test_tollgate_client: test_tollgate_client.c $(REPO_ROOT)/main/market.c $(REPO_ROOT)/main/beacon_price.c $(TG_CORE_SRC)/tollgate_core_beacon.c $(TG_CORE_SRC)/tollgate_core_market.c $(TG_CORE_SRC)/tollgate_core_client.c
73 $(CC) $(CFLAGS) -I $(REPO_ROOT)/main $< $(REPO_ROOT)/main/market.c $(REPO_ROOT)/main/beacon_price.c $(TG_CORE_SRC)/tollgate_core_beacon.c $(TG_CORE_SRC)/tollgate_core_market.c -o $@ $(LDFLAGS) 73 $(CC) $(CFLAGS) -I $(REPO_ROOT)/main $< $(REPO_ROOT)/main/market.c $(REPO_ROOT)/main/beacon_price.c $(TG_CORE_SRC)/tollgate_core_beacon.c $(TG_CORE_SRC)/tollgate_core_market.c $(TG_CORE_SRC)/tollgate_core_client.c -o $@ $(LDFLAGS)
74 74
75test_lnurl_pay: test_lnurl_pay.c 75test_lnurl_pay: test_lnurl_pay.c
76 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) 76 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
@@ -87,8 +87,8 @@ test_nip04: test_nip04.c $(REPO_ROOT)/main/nip04.c $(SECP256K1_OBJ)
87test_cvm_server: test_cvm_server.c 87test_cvm_server: test_cvm_server.c
88 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) 88 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
89 89
90test_mint_health: test_mint_health.c $(REPO_ROOT)/main/mint_health.c 90test_mint_health: test_mint_health.c $(REPO_ROOT)/main/mint_health.c $(TG_CORE_SRC)/tollgate_core_mint_health.c
91 $(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) 91 $(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 -I $(TG_CORE_SRC) -I $(TG_CORE_INC) $^ -o $@ $(LDFLAGS)
92 92
93test_beacon_price: test_beacon_price.c $(REPO_ROOT)/main/beacon_price.c $(TG_CORE_SRC)/tollgate_core_beacon.c 93test_beacon_price: test_beacon_price.c $(REPO_ROOT)/main/beacon_price.c $(TG_CORE_SRC)/tollgate_core_beacon.c
94 $(CC) $(CFLAGS) -I $(REPO_ROOT)/main $< $(REPO_ROOT)/main/beacon_price.c $(TG_CORE_SRC)/tollgate_core_beacon.c -o $@ $(LDFLAGS) 94 $(CC) $(CFLAGS) -I $(REPO_ROOT)/main $< $(REPO_ROOT)/main/beacon_price.c $(TG_CORE_SRC)/tollgate_core_beacon.c -o $@ $(LDFLAGS)
@@ -105,8 +105,8 @@ test_stratum_proxy: test_stratum_proxy.c $(REPO_ROOT)/main/stratum_proxy.c $(TG_
105test_session_payment_method: test_session_payment_method.c $(TG_CORE_SRC)/tollgate_core_cashu.c $(TG_CORE_SRC)/tollgate_core_session.c $(TG_CORE_SRC)/tollgate_core_firewall.c $(TG_CORE_SRC)/tollgate_core.c $(TG_CORE_SRC)/tollgate_core_mining.c $(TG_CORE_SRC)/tollgate_core_dns.c $(TG_CORE_SRC)/tollgate_core_stratum_proxy.c 105test_session_payment_method: test_session_payment_method.c $(TG_CORE_SRC)/tollgate_core_cashu.c $(TG_CORE_SRC)/tollgate_core_session.c $(TG_CORE_SRC)/tollgate_core_firewall.c $(TG_CORE_SRC)/tollgate_core.c $(TG_CORE_SRC)/tollgate_core_mining.c $(TG_CORE_SRC)/tollgate_core_dns.c $(TG_CORE_SRC)/tollgate_core_stratum_proxy.c
106 $(CC) $(CFLAGS) $(TG_CORE_OBJS_CFLAGS) $^ -o $@ $(LDFLAGS) 106 $(CC) $(CFLAGS) $(TG_CORE_OBJS_CFLAGS) $^ -o $@ $(LDFLAGS)
107 107
108test_tollgate_client_mining: test_tollgate_client_mining.c 108test_tollgate_client_mining: test_tollgate_client_mining.c $(TG_CORE_SRC)/tollgate_core_client.c
109 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) 109 $(CC) $(CFLAGS) -I $(REPO_ROOT)/main $< $(TG_CORE_SRC)/tollgate_core_client.c -o $@ $(LDFLAGS)
110 110
111test_firewall_sandbox: test_firewall_sandbox.c $(TG_CORE_SRC)/tollgate_core_firewall.c $(TG_CORE_SRC)/tollgate_core.c $(TG_CORE_SRC)/tollgate_core_session.c $(TG_CORE_SRC)/tollgate_core_cashu.c $(TG_CORE_SRC)/tollgate_core_mining.c $(TG_CORE_SRC)/tollgate_core_dns.c $(TG_CORE_SRC)/tollgate_core_stratum_proxy.c 111test_firewall_sandbox: test_firewall_sandbox.c $(TG_CORE_SRC)/tollgate_core_firewall.c $(TG_CORE_SRC)/tollgate_core.c $(TG_CORE_SRC)/tollgate_core_session.c $(TG_CORE_SRC)/tollgate_core_cashu.c $(TG_CORE_SRC)/tollgate_core_mining.c $(TG_CORE_SRC)/tollgate_core_dns.c $(TG_CORE_SRC)/tollgate_core_stratum_proxy.c
112 $(CC) $(CFLAGS) -include stubs/dns_server.h $(TG_CORE_OBJS_CFLAGS) $^ -o $@ $(LDFLAGS) 112 $(CC) $(CFLAGS) -include stubs/dns_server.h $(TG_CORE_OBJS_CFLAGS) $^ -o $@ $(LDFLAGS)
@@ -117,5 +117,17 @@ test_relay_selector: test_relay_selector.c
117test_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) 117test_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)
118 $(CC) $(CFLAGS) -I $(SECP256K1_PRIV_INC) -I $(REPO_ROOT)/main -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) 118 $(CC) $(CFLAGS) -I $(SECP256K1_PRIV_INC) -I $(REPO_ROOT)/main -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)
119 119
120test_portal: test_portal.c $(TG_CORE_SRC)/tollgate_core_portal.c
121 $(CC) $(CFLAGS) $(TG_CORE_OBJS_CFLAGS) $^ -o $@ $(LDFLAGS)
122
123test_stratum_client: test_stratum_client.c $(TG_CORE_SRC)/tollgate_core_stratum_client.c
124 $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
125
126test_mint_health_core: test_mint_health_core.c $(TG_CORE_SRC)/tollgate_core_mint_health.c
127 $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
128
129test_client_core: test_client_core.c $(TG_CORE_SRC)/tollgate_core_client.c
130 $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
131
120clean: 132clean:
121 rm -f $(TESTS) $(SECP256K1_OBJ) 133 rm -f $(TESTS) $(SECP256K1_OBJ)
diff --git a/tests/unit/test_client_core.c b/tests/unit/test_client_core.c
new file mode 100644
index 0000000..7cc6cc7
--- /dev/null
+++ b/tests/unit/test_client_core.c
@@ -0,0 +1,189 @@
1#include "test_framework.h"
2#include "../../components/tollgate_core/src/tollgate_core_client.h"
3#include <cjson/cJSON.h>
4#include <string.h>
5#include <stdio.h>
6
7int main(void)
8{
9 printf("=== test_client_core ===\n");
10
11 printf("\n--- parse_discovery valid ---\n");
12 {
13 const char *json = "{\"kind\":10021,\"content\":\"discovery\","
14 "\"tags\":["
15 "[\"metric\",\"milliseconds\"],"
16 "[\"step_size\",\"60000\"],"
17 "[\"price_per_step\",\"21\",\"21\",\"sha256\",\"https://test.mint\"]"
18 "]}";
19 tollgate_discovery_t disc = {0};
20 bool ok = tollgate_core_client_parse_discovery(json, &disc);
21 ASSERT(ok, "parse_discovery returns true");
22 ASSERT(disc.is_tollgate, "is_tollgate set");
23 ASSERT_EQ_INT(21, disc.price_per_step, "price_per_step");
24 ASSERT_EQ_INT(60000, disc.step_size_ms, "step_size_ms");
25 ASSERT(strcmp(disc.metric, "milliseconds") == 0, "metric");
26 ASSERT(strcmp(disc.mint_url, "https://test.mint") == 0, "mint_url");
27 }
28
29 printf("\n--- parse_discovery wrong kind ---\n");
30 {
31 const char *json = "{\"kind\":9999}";
32 tollgate_discovery_t disc = {0};
33 bool ok = tollgate_core_client_parse_discovery(json, &disc);
34 ASSERT(!ok, "wrong kind returns false");
35 }
36
37 printf("\n--- parse_discovery mining payment ---\n");
38 {
39 const char *json = "{\"kind\":10021,\"content\":\"\","
40 "\"tags\":["
41 "[\"metric\",\"milliseconds\"],"
42 "[\"step_size\",\"60000\"],"
43 "[\"price_per_step\",\"0\",\"mining\",\"3333\",\"extra\"]"
44 "]}";
45 tollgate_discovery_t disc = {0};
46 bool ok = tollgate_core_client_parse_discovery(json, &disc);
47 ASSERT(ok, "parse_discovery mining returns true");
48 ASSERT(disc.mining_available, "mining_available set");
49 ASSERT_EQ_INT(3333, (int)disc.mining_port, "mining_port");
50 }
51
52 printf("\n--- parse_discovery empty tags ---\n");
53 {
54 const char *json = "{\"kind\":10021,\"tags\":[]}";
55 tollgate_discovery_t disc = {0};
56 bool ok = tollgate_core_client_parse_discovery(json, &disc);
57 ASSERT(ok, "empty tags returns true");
58 ASSERT(disc.is_tollgate, "is_tollgate set");
59 }
60
61 printf("\n--- parse_discovery no tags ---\n");
62 {
63 const char *json = "{\"kind\":10021}";
64 tollgate_discovery_t disc = {0};
65 bool ok = tollgate_core_client_parse_discovery(json, &disc);
66 ASSERT(ok, "no tags returns true");
67 }
68
69 printf("\n--- parse_discovery invalid JSON ---\n");
70 {
71 tollgate_discovery_t disc = {0};
72 bool ok = tollgate_core_client_parse_discovery("not json", &disc);
73 ASSERT(!ok, "invalid JSON returns false");
74 }
75
76 printf("\n--- parse_session valid ---\n");
77 {
78 const char *json = "{\"kind\":1022,\"content\":\"session\","
79 "\"tags\":[[\"allotment\",\"120000\"]]}";
80 int64_t allotment = 0;
81 bool ok = tollgate_core_client_parse_session(json, &allotment);
82 ASSERT(ok, "parse_session returns true");
83 ASSERT_EQ_UINT64(120000, (unsigned long long)allotment, "allotment_ms");
84 }
85
86 printf("\n--- parse_session wrong kind ---\n");
87 {
88 const char *json = "{\"kind\":10021}";
89 int64_t allotment = 0;
90 bool ok = tollgate_core_client_parse_session(json, &allotment);
91 ASSERT(!ok, "wrong kind returns false");
92 }
93
94 printf("\n--- parse_session no allotment tag ---\n");
95 {
96 const char *json = "{\"kind\":1022,\"tags\":[]}";
97 int64_t allotment = 999;
98 bool ok = tollgate_core_client_parse_session(json, &allotment);
99 ASSERT(ok, "returns true");
100 ASSERT_EQ_UINT64(999, (unsigned long long)allotment, "allotment unchanged when no tag");
101 }
102
103 printf("\n--- parse_usage valid ---\n");
104 {
105 int64_t remaining = 0, total = 0;
106 bool ok = tollgate_core_client_parse_usage("45000/60000", &remaining, &total);
107 ASSERT(ok, "parse_usage returns true");
108 ASSERT_EQ_UINT64(45000, (unsigned long long)remaining, "remaining");
109 ASSERT_EQ_UINT64(60000, (unsigned long long)total, "total");
110 }
111
112 printf("\n--- parse_usage no slash ---\n");
113 {
114 int64_t remaining = 0, total = 0;
115 bool ok = tollgate_core_client_parse_usage("45000", &remaining, &total);
116 ASSERT(!ok, "no slash returns false");
117 }
118
119 printf("\n--- parse_usage zero values ---\n");
120 {
121 int64_t remaining = 0, total = 0;
122 bool ok = tollgate_core_client_parse_usage("0/0", &remaining, &total);
123 ASSERT(ok, "parse_usage zero values");
124 ASSERT_EQ_UINT64(0, (unsigned long long)remaining, "remaining is 0");
125 ASSERT_EQ_UINT64(0, (unsigned long long)total, "total is 0");
126 }
127
128 printf("\n--- should_renew ---\n");
129 {
130 ASSERT(tollgate_core_client_should_renew(1000, 60000, 20),
131 "1.6% remaining → should renew");
132 ASSERT(tollgate_core_client_should_renew(0, 60000, 20),
133 "0% remaining → should renew");
134 ASSERT(!tollgate_core_client_should_renew(30000, 60000, 20),
135 "50% remaining → should not renew");
136 ASSERT(!tollgate_core_client_should_renew(60000, 60000, 20),
137 "100% remaining → should not renew");
138 ASSERT(tollgate_core_client_should_renew(11999, 60000, 20),
139 "19.99% → should renew");
140 ASSERT(tollgate_core_client_should_renew(12000, 60000, 20),
141 "20% exactly at threshold → should renew");
142 }
143
144 printf("\n--- should_renew default threshold ---\n");
145 {
146 ASSERT(tollgate_core_client_should_renew(1000, 60000, 0),
147 "threshold=0 defaults to 20%");
148 ASSERT(tollgate_core_client_should_renew(1000, 60000, -1),
149 "negative threshold defaults to 20%");
150 }
151
152 printf("\n--- should_renew edge cases ---\n");
153 {
154 ASSERT(!tollgate_core_client_should_renew(-1, 60000, 20),
155 "negative remaining → false");
156 ASSERT(!tollgate_core_client_should_renew(1000, 0, 20),
157 "zero allotment → false");
158 }
159
160 printf("\n--- calc_price_per_min ---\n");
161 {
162 ASSERT_EQ_INT(21, tollgate_core_client_calc_price_per_min(21, 60000),
163 "21 sats/60s = 21 sats/min");
164 ASSERT_EQ_INT(42, tollgate_core_client_calc_price_per_min(21, 30000),
165 "21 sats/30s = 42 sats/min");
166 ASSERT_EQ_INT(21000, tollgate_core_client_calc_price_per_min(21, 60),
167 "21 sats/60ms effective");
168 }
169
170 printf("\n--- calc_price_per_min zero step ---\n");
171 {
172 int result = tollgate_core_client_calc_price_per_min(21, 0);
173 ASSERT(result > 0, "zero step_size_ms doesn't crash");
174 }
175
176 printf("\n--- calc_steps ---\n");
177 {
178 ASSERT_EQ_INT(21, tollgate_core_client_calc_steps(1, 21, 21),
179 "1 step * 21 = 21");
180 ASSERT_EQ_INT(42, tollgate_core_client_calc_steps(2, 21, 21),
181 "2 steps * 21 = 42");
182 ASSERT_EQ_INT(21, tollgate_core_client_calc_steps(0, 21, 21),
183 "0 steps defaults to 1");
184 ASSERT_EQ_INT(21, tollgate_core_client_calc_steps(-1, 21, 21),
185 "negative steps defaults to 1");
186 }
187
188 TEST_SUMMARY();
189}
diff --git a/tests/unit/test_mint_health_core.c b/tests/unit/test_mint_health_core.c
new file mode 100644
index 0000000..9727edb
--- /dev/null
+++ b/tests/unit/test_mint_health_core.c
@@ -0,0 +1,130 @@
1#include "test_framework.h"
2#include "../../components/tollgate_core/src/tollgate_core_mint_health.h"
3#include <string.h>
4#include <stdio.h>
5
6int main(void)
7{
8 printf("=== test_mint_health_core ===\n");
9
10 printf("\n--- update: probe success ---\n");
11 {
12 tollgate_mint_health_t state = {0};
13 strncpy(state.mints[0].url, "https://test.mint", sizeof(state.mints[0].url) - 1);
14 state.count = 1;
15
16 tollgate_core_mint_health_update(&state, 0, true, 200, 0, 1000);
17 ASSERT_EQ_INT(1, state.mints[0].consecutive_successes, "1st success");
18 ASSERT(!state.mints[0].reachable, "not yet reachable (threshold=3)");
19
20 tollgate_core_mint_health_update(&state, 0, true, 200, 0, 2000);
21 ASSERT_EQ_INT(2, state.mints[0].consecutive_successes, "2nd success");
22 ASSERT(!state.mints[0].reachable, "still not reachable");
23
24 tollgate_core_mint_health_update(&state, 0, true, 200, 0, 3000);
25 ASSERT_EQ_INT(3, state.mints[0].consecutive_successes, "3rd success");
26 ASSERT(state.mints[0].reachable, "now reachable after threshold");
27 }
28
29 printf("\n--- update: failure resets ---\n");
30 {
31 tollgate_mint_health_t state = {0};
32 strncpy(state.mints[0].url, "https://test.mint", sizeof(state.mints[0].url) - 1);
33 state.count = 1;
34 state.mints[0].reachable = true;
35 state.mints[0].consecutive_successes = 5;
36
37 tollgate_core_mint_health_update(&state, 0, false, 0, -1, 1000);
38 ASSERT(!state.mints[0].reachable, "failure marks unreachable");
39 ASSERT_EQ_INT(0, state.mints[0].consecutive_successes, "successes reset to 0");
40 ASSERT_EQ_INT(-1, state.mints[0].last_err, "error code recorded");
41 }
42
43 printf("\n--- update_initial: success ---\n");
44 {
45 tollgate_mint_health_t state = {0};
46 strncpy(state.mints[0].url, "https://test.mint", sizeof(state.mints[0].url) - 1);
47 state.count = 1;
48
49 tollgate_core_mint_health_update_initial(&state, 0, true, 200, 0, 1000);
50 ASSERT(state.mints[0].reachable, "immediately reachable on initial success");
51 ASSERT_EQ_INT(TG_MINT_HEALTH_RECOVERY_THRESHOLD,
52 state.mints[0].consecutive_successes, "successes set to threshold");
53 }
54
55 printf("\n--- update_initial: failure ---\n");
56 {
57 tollgate_mint_health_t state = {0};
58 strncpy(state.mints[0].url, "https://test.mint", sizeof(state.mints[0].url) - 1);
59 state.count = 1;
60
61 tollgate_core_mint_health_update_initial(&state, 0, false, 0, -2, 1000);
62 ASSERT(!state.mints[0].reachable, "not reachable on initial failure");
63 ASSERT_EQ_INT(0, state.mints[0].consecutive_successes, "successes is 0");
64 }
65
66 printf("\n--- is_reachable ---\n");
67 {
68 tollgate_mint_health_t state = {0};
69 strncpy(state.mints[0].url, "https://test.mint", sizeof(state.mints[0].url) - 1);
70 state.count = 1;
71 state.mints[0].reachable = true;
72
73 ASSERT(tollgate_core_mint_health_is_reachable(&state, "https://test.mint"),
74 "exact URL match");
75 ASSERT(tollgate_core_mint_health_is_reachable(&state, "https://test.mint/v1/info"),
76 "substring match");
77 ASSERT(!tollgate_core_mint_health_is_reachable(&state, "https://other.mint"),
78 "no match for other URL");
79 ASSERT(!tollgate_core_mint_health_is_reachable(NULL, "https://test.mint"),
80 "NULL state");
81 ASSERT(!tollgate_core_mint_health_is_reachable(&state, NULL),
82 "NULL url");
83 }
84
85 printf("\n--- mark_unreachable ---\n");
86 {
87 tollgate_mint_health_t state = {0};
88 strncpy(state.mints[0].url, "https://test.mint", sizeof(state.mints[0].url) - 1);
89 state.count = 1;
90 state.mints[0].reachable = true;
91 state.mints[0].consecutive_successes = 5;
92
93 tollgate_core_mint_health_mark_unreachable(&state, "https://test.mint");
94 ASSERT(!state.mints[0].reachable, "marked unreachable");
95 ASSERT_EQ_INT(0, state.mints[0].consecutive_successes, "successes cleared");
96
97 tollgate_core_mint_health_mark_unreachable(&state, "https://test.mint");
98 ASSERT(!state.mints[0].reachable, "idempotent");
99 }
100
101 printf("\n--- count_reachable ---\n");
102 {
103 tollgate_mint_health_t state = {0};
104 state.count = 3;
105 strncpy(state.mints[0].url, "https://a.mint", sizeof(state.mints[0].url) - 1);
106 strncpy(state.mints[1].url, "https://b.mint", sizeof(state.mints[1].url) - 1);
107 strncpy(state.mints[2].url, "https://c.mint", sizeof(state.mints[2].url) - 1);
108 state.mints[0].reachable = true;
109 state.mints[1].reachable = false;
110 state.mints[2].reachable = true;
111
112 ASSERT_EQ_INT(2, tollgate_core_mint_health_count_reachable(&state),
113 "2 out of 3 reachable");
114 ASSERT_EQ_INT(0, tollgate_core_mint_health_count_reachable(NULL),
115 "NULL returns 0");
116 }
117
118 printf("\n--- update with invalid index ---\n");
119 {
120 tollgate_mint_health_t state = {0};
121 state.count = 1;
122
123 tollgate_core_mint_health_update(&state, -1, true, 200, 0, 1000);
124 tollgate_core_mint_health_update(&state, 5, true, 200, 0, 1000);
125 tollgate_core_mint_health_update(NULL, 0, true, 200, 0, 1000);
126 ASSERT(true, "no crash on invalid inputs");
127 }
128
129 TEST_SUMMARY();
130}
diff --git a/tests/unit/test_portal.c b/tests/unit/test_portal.c
new file mode 100644
index 0000000..575921e
--- /dev/null
+++ b/tests/unit/test_portal.c
@@ -0,0 +1,153 @@
1#include "test_framework.h"
2#include "../../components/tollgate_core/src/tollgate_core_portal.h"
3#include "../../components/tollgate_core/src/tollgate_core_session.h"
4#include <string.h>
5#include <stdio.h>
6#include <stdlib.h>
7
8int main(void)
9{
10 printf("=== test_portal ===\n");
11
12 printf("\n--- template_replace single key ---\n");
13 {
14 char *result = tollgate_core_portal_template_replace(
15 "Hello __NAME__!", "__NAME__", "World");
16 ASSERT(result != NULL, "result not NULL");
17 ASSERT(strcmp(result, "Hello World!") == 0, "single substitution works");
18 free(result);
19 }
20
21 printf("\n--- template_replace no match ---\n");
22 {
23 char *result = tollgate_core_portal_template_replace(
24 "Hello World!", "__NAME__", "Test");
25 ASSERT(result != NULL, "result not NULL");
26 ASSERT(strcmp(result, "Hello World!") == 0, "no match returns original");
27 free(result);
28 }
29
30 printf("\n--- template_replace multiple occurrences ---\n");
31 {
32 char *result = tollgate_core_portal_template_replace(
33 "__IP__:2121 and __IP__:80", "__IP__", "10.0.0.1");
34 ASSERT(result != NULL, "result not NULL");
35 ASSERT(strcmp(result, "10.0.0.1:2121 and 10.0.0.1:80") == 0,
36 "multiple occurrences replaced");
37 free(result);
38 }
39
40 printf("\n--- template_replace key longer than value ---\n");
41 {
42 char *result = tollgate_core_portal_template_replace(
43 "A __LONGKEY__ B", "__LONGKEY__", "X");
44 ASSERT(result != NULL, "result not NULL");
45 ASSERT(strcmp(result, "A X B") == 0, "shrinks correctly");
46 free(result);
47 }
48
49 printf("\n--- render multi-key substitution ---\n");
50 {
51 tollgate_portal_sub_t subs[] = {
52 { "__AP_IP__", "10.0.0.1" },
53 { "__PRICE__", "21" },
54 { "__MINT_URL__", "https://test.mint" },
55 };
56 char *result = tollgate_core_portal_render(
57 "IP=__AP_IP__ price=__PRICE__ mint=__MINT_URL__", subs, 3);
58 ASSERT(result != NULL, "result not NULL");
59 ASSERT(strcmp(result, "IP=10.0.0.1 price=21 mint=https://test.mint") == 0,
60 "multi-key render works");
61 free(result);
62 }
63
64 printf("\n--- render with no matches ---\n");
65 {
66 tollgate_portal_sub_t subs[] = {
67 { "__AP_IP__", "10.0.0.1" },
68 };
69 char *result = tollgate_core_portal_render("no placeholders", subs, 1);
70 ASSERT(result != NULL, "result not NULL");
71 ASSERT(strcmp(result, "no placeholders") == 0, "no matches returns original");
72 free(result);
73 }
74
75 printf("\n--- calc_usage time metric ---\n");
76 {
77 tg_session_t session = {0};
78 session.active = true;
79 session.allotment_ms = 60000;
80 session.start_time_ms = 10000;
81
82 int64_t remaining = 0, total = 0;
83 bool ok = tollgate_core_portal_calc_usage(&session, "milliseconds", 30000, &remaining, &total);
84 ASSERT(ok, "calc_usage returns true for active session");
85 ASSERT_EQ_UINT64(40000, (unsigned long long)remaining, "remaining = 60000 - 20000");
86 ASSERT_EQ_UINT64(60000, (unsigned long long)total, "total = allotment_ms");
87 }
88
89 printf("\n--- calc_usage bytes metric ---\n");
90 {
91 tg_session_t session = {0};
92 session.active = true;
93 session.allotment_bytes = 1000000;
94 session.bytes_consumed = 300000;
95
96 int64_t remaining = 0, total = 0;
97 bool ok = tollgate_core_portal_calc_usage(&session, "bytes", 0, &remaining, &total);
98 ASSERT(ok, "calc_usage returns true for bytes");
99 ASSERT_EQ_UINT64(700000, (unsigned long long)remaining, "remaining = 1000000 - 300000");
100 ASSERT_EQ_UINT64(1000000, (unsigned long long)total, "total = allotment_bytes");
101 }
102
103 printf("\n--- calc_usage expired time ---\n");
104 {
105 tg_session_t session = {0};
106 session.active = true;
107 session.allotment_ms = 60000;
108 session.start_time_ms = 0;
109
110 int64_t remaining = 0, total = 0;
111 tollgate_core_portal_calc_usage(&session, "milliseconds", 120000, &remaining, &total);
112 ASSERT_EQ_UINT64(0, (unsigned long long)remaining, "remaining clamped to 0 when expired");
113 }
114
115 printf("\n--- calc_usage inactive session ---\n");
116 {
117 tg_session_t session = {0};
118 session.active = false;
119
120 int64_t remaining = 0, total = 0;
121 bool ok = tollgate_core_portal_calc_usage(&session, "milliseconds", 0, &remaining, &total);
122 ASSERT(!ok, "inactive session returns false");
123 }
124
125 printf("\n--- format_usage ---\n");
126 {
127 tg_session_t session = {0};
128 session.active = true;
129 session.allotment_ms = 60000;
130 session.start_time_ms = 10000;
131
132 char buf[64];
133 int len = tollgate_core_portal_format_usage(&session, "milliseconds", 30000, buf, sizeof(buf));
134 ASSERT(len > 0, "format_usage returns positive length");
135 ASSERT(strstr(buf, "/") != NULL, "contains separator");
136 ASSERT(strstr(buf, "60000") != NULL, "contains total");
137 }
138
139 printf("\n--- is_captive_uri ---\n");
140 {
141 ASSERT(tollgate_core_portal_is_captive_uri("/generate_204"), "generate_204 is captive");
142 ASSERT(tollgate_core_portal_is_captive_uri("/hotspot-detect.html"), "hotspot-detect is captive");
143 ASSERT(tollgate_core_portal_is_captive_uri("/success.txt"), "success.txt is captive");
144 ASSERT(tollgate_core_portal_is_captive_uri("/ncsi.txt"), "ncsi.txt is captive");
145 ASSERT(tollgate_core_portal_is_captive_uri("/connecttest.txt"), "connecttest is captive");
146 ASSERT(tollgate_core_portal_is_captive_uri("/wpad.dat"), "wpad.dat is captive");
147 ASSERT(!tollgate_core_portal_is_captive_uri("/"), "root is not captive");
148 ASSERT(!tollgate_core_portal_is_captive_uri("/setup"), "setup is not captive");
149 ASSERT(!tollgate_core_portal_is_captive_uri(NULL), "NULL is not captive");
150 }
151
152 TEST_SUMMARY();
153}
diff --git a/tests/unit/test_stratum_client.c b/tests/unit/test_stratum_client.c
new file mode 100644
index 0000000..302fcba
--- /dev/null
+++ b/tests/unit/test_stratum_client.c
@@ -0,0 +1,125 @@
1#include "test_framework.h"
2#include "../../components/tollgate_core/src/tollgate_core_stratum_client.h"
3#include <cjson/cJSON.h>
4#include <string.h>
5#include <stdio.h>
6#include <stdlib.h>
7
8int main(void)
9{
10 printf("=== test_stratum_client ===\n");
11
12 printf("\n--- hex_to_bytes ---\n");
13 {
14 const char *hex = "0a1b2c3d";
15 uint8_t out[4] = {0};
16 tollgate_core_stratum_hex_to_bytes(hex, out, 4);
17 ASSERT_EQ_INT(0x0a, (int)out[0], "byte 0");
18 ASSERT_EQ_INT(0x1b, (int)out[1], "byte 1");
19 ASSERT_EQ_INT(0x2c, (int)out[2], "byte 2");
20 ASSERT_EQ_INT(0x3d, (int)out[3], "byte 3");
21 }
22
23 printf("\n--- hex_to_bytes zeros ---\n");
24 {
25 const char *hex = "00000000";
26 uint8_t out[4] = {0xff, 0xff, 0xff, 0xff};
27 tollgate_core_stratum_hex_to_bytes(hex, out, 4);
28 ASSERT_EQ_INT(0, (int)out[0], "zero byte 0");
29 ASSERT_EQ_INT(0, (int)out[1], "zero byte 1");
30 }
31
32 printf("\n--- parse_notify valid ---\n");
33 {
34 cJSON *params = cJSON_Parse("[\"123\",\"0000000000000000000000000000000000000000000000000000000000000000\","
35 "\"\""
36 ",\"\""
37 ",\"\""
38 ",\"20000000\""
39 ",\"1705e3c4\""
40 ",\"65a3b2c1\"]");
41 tollgate_stratum_job_t job = {0};
42 uint32_t nbits = 0;
43 bool ok = tollgate_core_stratum_parse_notify(params, &job, &nbits);
44 ASSERT(ok, "parse_notify returns true");
45 ASSERT_EQ_INT(123, (int)job.job_id, "job_id parsed");
46 ASSERT(job.valid, "job is valid");
47 ASSERT_EQ_INT(0x20000000, (int)job.version, "version parsed");
48 ASSERT_EQ_INT(0x1705e3c4, (int)job.nbits, "nbits parsed");
49 ASSERT_EQ_INT(0x65a3b2c1, (int)job.ntime, "ntime parsed");
50 ASSERT_EQ_INT(0x1705e3c4, (int)nbits, "out_nbits set");
51 ASSERT_EQ_INT(32, job.target_len, "target_len is 32");
52 cJSON_Delete(params);
53 }
54
55 printf("\n--- parse_notify too few params ---\n");
56 {
57 cJSON *params = cJSON_Parse("[\"123\",\"abcd\"]");
58 tollgate_stratum_job_t job = {0};
59 bool ok = tollgate_core_stratum_parse_notify(params, &job, NULL);
60 ASSERT(!ok, "parse_notify returns false for insufficient params");
61 cJSON_Delete(params);
62 }
63
64 printf("\n--- parse_notify NULL ---\n");
65 {
66 tollgate_stratum_job_t job = {0};
67 bool ok = tollgate_core_stratum_parse_notify(NULL, &job, NULL);
68 ASSERT(!ok, "parse_notify returns false for NULL");
69 }
70
71 printf("\n--- parse_difficulty valid ---\n");
72 {
73 cJSON *params = cJSON_Parse("[512]");
74 uint64_t diff = 0;
75 bool ok = tollgate_core_stratum_parse_difficulty(params, &diff);
76 ASSERT(ok, "parse_difficulty returns true");
77 ASSERT_EQ_UINT64(512, (unsigned long long)diff, "difficulty parsed");
78 cJSON_Delete(params);
79 }
80
81 printf("\n--- parse_difficulty invalid ---\n");
82 {
83 cJSON *params = cJSON_Parse("[]");
84 uint64_t diff = 0;
85 bool ok = tollgate_core_stratum_parse_difficulty(params, &diff);
86 ASSERT(!ok, "parse_difficulty returns false for empty array");
87 cJSON_Delete(params);
88 }
89
90 printf("\n--- build_subscribe ---\n");
91 {
92 char buf[256];
93 int len = tollgate_core_stratum_build_subscribe(buf, sizeof(buf), 1);
94 ASSERT(len > 0, "build_subscribe returns positive length");
95 ASSERT(strstr(buf, "mining.subscribe") != NULL, "contains method");
96 ASSERT(strstr(buf, "\"id\":1") != NULL, "contains id");
97 ASSERT(strstr(buf, "TollGate/1.0") != NULL, "contains user agent");
98 ASSERT(buf[len - 1] == '\n', "ends with newline");
99 }
100
101 printf("\n--- build_authorize ---\n");
102 {
103 char buf[512];
104 int len = tollgate_core_stratum_build_authorize(buf, sizeof(buf), 2, "user1", "pass1");
105 ASSERT(len > 0, "build_authorize returns positive length");
106 ASSERT(strstr(buf, "mining.authorize") != NULL, "contains method");
107 ASSERT(strstr(buf, "user1") != NULL, "contains user");
108 ASSERT(strstr(buf, "pass1") != NULL, "contains pass");
109 }
110
111 printf("\n--- build_submit ---\n");
112 {
113 char buf[512];
114 int len = tollgate_core_stratum_build_submit(buf, sizeof(buf), 3, "worker1",
115 100, 0x65a3b2c1, 0x12345678, 0x20000000);
116 ASSERT(len > 0, "build_submit returns positive length");
117 ASSERT(strstr(buf, "mining.submit") != NULL, "contains method");
118 ASSERT(strstr(buf, "worker1") != NULL, "contains worker");
119 ASSERT(strstr(buf, "100") != NULL, "contains job_id");
120 ASSERT(strstr(buf, "65a3b2c1") != NULL, "contains ntime");
121 ASSERT(strstr(buf, "12345678") != NULL, "contains nonce");
122 }
123
124 TEST_SUMMARY();
125}