Mining/pkg/node/lethean_test.go
Claude aa69d9c933
ax(node): add Good and Ugly test variants for GetChainInfo, DiscoverPools, DiscoverGateways
AX principle 10 requires all three test categories (Good, Bad, Ugly).
lethean_test.go only had Bad variants for the three discovery functions;
added Good (happy-path via httptest.Server) and Ugly (edge-case filtering
and malformed JSON) for each, using a local test server to avoid live daemon dependency.

Co-Authored-By: Charon <charon@lethean.io>
2026-04-02 16:54:59 +01:00

191 lines
6.1 KiB
Go

// Copyright (c) 2017-2026 Lethean (https://lt.hn)
// SPDX-License-Identifier: EUPL-1.2
package node
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestParseComment_Good(t *testing.T) {
tests := []struct {
input string
key string
want string
}{
{"v=lthn1;type=gateway;cap=vpn,dns", "type", "gateway"},
{"v=lthn1;cap=pool", "cap", "pool"},
{"v=lthn1", "v", "lthn1"},
}
for _, testCase := range tests {
result := parseComment(testCase.input)
if result[testCase.key] != testCase.want {
t.Errorf("parseComment(%q)[%q] = %q, want %q", testCase.input, testCase.key, result[testCase.key], testCase.want)
}
}
}
func TestParseComment_Bad(t *testing.T) {
tests := []struct {
input string
key string
want string
}{
{"noequals", "noequals", ""},
{"k=v", "missing", ""},
{"=v", "", ""},
}
for _, testCase := range tests {
result := parseComment(testCase.input)
if result[testCase.key] != testCase.want {
t.Errorf("parseComment(%q)[%q] = %q, want %q", testCase.input, testCase.key, result[testCase.key], testCase.want)
}
}
}
func TestParseComment_Ugly(t *testing.T) {
tests := []struct {
name string
input string
key string
want string
}{
{"empty string", "", "k", ""},
{"semicolons only", ";;;", "k", ""},
{"duplicate keys last wins", "k=first;k=second", "k", "second"},
{"value with equals", "k=v=extra", "k", "v=extra"},
}
for _, testCase := range tests {
result := parseComment(testCase.input)
if result[testCase.key] != testCase.want {
t.Errorf("%s: parseComment(%q)[%q] = %q, want %q", testCase.name, testCase.input, testCase.key, result[testCase.key], testCase.want)
}
}
}
func TestGetChainInfo_Good(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"result":{"height":6292,"alias_count":5,"tx_pool_size":0,"difficulty":12345}}`))
}))
defer server.Close()
// info, err := node.GetChainInfo(server.URL)
// if info.Synced { log("height:", info.Height) }
info, err := GetChainInfo(server.URL)
if err != nil {
t.Fatalf("GetChainInfo returned unexpected error: %v", err)
}
if info.Height != 6292 {
t.Errorf("expected height 6292, got %d", info.Height)
}
if !info.Synced {
t.Error("expected Synced=true when height > 0")
}
if info.AliasCount != 5 {
t.Errorf("expected alias_count 5, got %d", info.AliasCount)
}
}
func TestGetChainInfo_Bad(t *testing.T) {
_, err := GetChainInfo("http://127.0.0.1:19999")
if err == nil {
t.Error("expected error for unreachable daemon")
}
}
func TestGetChainInfo_Ugly(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`not-valid-json`))
}))
defer server.Close()
// info, err := node.GetChainInfo(server.URL) — malformed response triggers unmarshal error
_, err := GetChainInfo(server.URL)
if err == nil {
t.Error("expected error when daemon returns malformed JSON")
}
}
func TestDiscoverPools_Good(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"result":{"aliases":[{"alias":"pool.lthn","address":"LTHNabc","comment":"v=lthn1;type=pool;cap=pool"}]}}`))
}))
defer server.Close()
// pools := node.DiscoverPools(server.URL)
// for _, pool := range pools { log(pool.Name, pool.Endpoint) }
pools := DiscoverPools(server.URL)
if len(pools) != 1 {
t.Fatalf("expected 1 pool, got %d", len(pools))
}
if pools[0].Name != "pool.lthn" {
t.Errorf("expected pool name 'pool.lthn', got '%s'", pools[0].Name)
}
}
func TestDiscoverPools_Bad(t *testing.T) {
pools := DiscoverPools("http://127.0.0.1:19999")
if len(pools) != 0 {
t.Errorf("expected 0 pools for unreachable daemon, got %d", len(pools))
}
}
func TestDiscoverPools_Ugly(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
// Aliases present but none contain "pool" in comment — result must be empty
w.Write([]byte(`{"result":{"aliases":[{"alias":"gw.lthn","address":"LTHNxyz","comment":"v=lthn1;type=gateway;cap=vpn"}]}}`))
}))
defer server.Close()
// DiscoverPools filters by comment containing "pool"; gateway alias must be skipped
pools := DiscoverPools(server.URL)
if len(pools) != 0 {
t.Errorf("expected 0 pools when no alias comment contains 'pool', got %d", len(pools))
}
}
func TestDiscoverGateways_Good(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"result":{"aliases":[{"alias":"gw.lthn","address":"LTHNxyz","comment":"v=lthn1;type=gateway;cap=vpn,dns"}]}}`))
}))
defer server.Close()
// gateways := node.DiscoverGateways(server.URL)
// for _, gw := range gateways { log(gw.Name, gw.Endpoint) }
gateways := DiscoverGateways(server.URL)
if len(gateways) != 1 {
t.Fatalf("expected 1 gateway, got %d", len(gateways))
}
if gateways[0].Name != "gw.lthn" {
t.Errorf("expected gateway name 'gw.lthn', got '%s'", gateways[0].Name)
}
}
func TestDiscoverGateways_Bad(t *testing.T) {
gateways := DiscoverGateways("http://127.0.0.1:19999")
if len(gateways) != 0 {
t.Errorf("expected 0 gateways for unreachable daemon, got %d", len(gateways))
}
}
func TestDiscoverGateways_Ugly(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
// Alias present but comment lacks "type=gateway" — must be filtered out
w.Write([]byte(`{"result":{"aliases":[{"alias":"pool.lthn","address":"LTHNabc","comment":"v=lthn1;type=pool;cap=pool"}]}}`))
}))
defer server.Close()
// DiscoverGateways filters by comment containing "type=gateway"; pool alias must be skipped
gateways := DiscoverGateways(server.URL)
if len(gateways) != 0 {
t.Errorf("expected 0 gateways when no alias comment contains 'type=gateway', got %d", len(gateways))
}
}