diff --git a/service.go b/service.go index c3ed5e6..19c5f77 100644 --- a/service.go +++ b/service.go @@ -89,12 +89,19 @@ type Service struct { // Records: map[string]dns.NameRecords{ // "gateway.charon.lthn": {A: []string{"10.10.10.10"}}, // }, -// HSDClient: dns.NewHSDClient(dns.HSDClientOptions{URL: "http://127.0.0.1:14037"}), +// HSDURL: "http://127.0.0.1:14037", +// MainchainURL: "http://127.0.0.1:14037", // }) type ServiceOptions struct { Records map[string]NameRecords RecordDiscoverer func() (map[string]NameRecords, error) FallbackRecordDiscoverer func() (map[string]NameRecords, error) + HSDURL string + HSDUsername string + HSDPassword string + MainchainURL string + MainchainUsername string + MainchainPassword string MainchainAliasClient *MainchainAliasClient HSDClient *HSDClient ChainAliasActionCaller ActionCaller @@ -126,6 +133,24 @@ func NewService(options ServiceOptions) *Service { checkInterval = DefaultTreeRootCheckInterval } + hsdClient := options.HSDClient + if hsdClient == nil && strings.TrimSpace(options.HSDURL) != "" { + hsdClient = NewHSDClient(HSDClientOptions{ + URL: options.HSDURL, + Username: options.HSDUsername, + Password: options.HSDPassword, + }) + } + + mainchainClient := options.MainchainAliasClient + if mainchainClient == nil && strings.TrimSpace(options.MainchainURL) != "" { + mainchainClient = NewMainchainAliasClient(MainchainClientOptions{ + URL: options.MainchainURL, + Username: options.MainchainUsername, + Password: options.MainchainPassword, + }) + } + cached := make(map[string]NameRecords, len(options.Records)) for name, record := range options.Records { cached[normalizeName(name)] = record @@ -136,8 +161,8 @@ func NewService(options ServiceOptions) *Service { reverseIndex: buildReverseIndexCache(cached), treeRoot: treeRoot, zoneApex: computeZoneApex(cached), - hsdClient: options.HSDClient, - mainchainAliasClient: options.MainchainAliasClient, + hsdClient: hsdClient, + mainchainAliasClient: mainchainClient, chainAliasActionCaller: options.ChainAliasActionCaller, chainAliasAction: options.ChainAliasAction, recordDiscoverer: options.RecordDiscoverer, diff --git a/service_test.go b/service_test.go index 9ed3ea5..47aeed6 100644 --- a/service_test.go +++ b/service_test.go @@ -677,6 +677,85 @@ func TestServiceDiscoverAliasesUsesConfiguredChainAliasAction(t *testing.T) { } } +func TestNewServiceBuildsRPCClientsFromOptions(t *testing.T) { + var chainCalls int32 + var treeRootCalls int32 + var nameResourceCalls int32 + + server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) { + var payload struct { + Method string `json:"method"` + Params []any `json:"params"` + } + if err := json.NewDecoder(request.Body).Decode(&payload); err != nil { + t.Fatalf("unexpected request payload: %v", err) + } + + switch payload.Method { + case "get_all_alias_details": + atomic.AddInt32(&chainCalls, 1) + responseWriter.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(responseWriter).Encode(map[string]any{ + "result": []any{ + map[string]any{ + "hns": "gateway.charon.lthn", + }, + }, + }) + case "getblockchaininfo": + atomic.AddInt32(&treeRootCalls, 1) + responseWriter.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(responseWriter).Encode(map[string]any{ + "result": map[string]any{ + "tree_root": "options-root-1", + }, + }) + case "getnameresource": + atomic.AddInt32(&nameResourceCalls, 1) + if len(payload.Params) != 1 || payload.Params[0] != "gateway.charon.lthn" { + t.Fatalf("unexpected alias lookup: %#v", payload.Params) + } + responseWriter.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(responseWriter).Encode(map[string]any{ + "result": map[string]any{ + "a": []string{"10.10.10.10"}, + }, + }) + default: + t.Fatalf("unexpected method: %s", payload.Method) + } + })) + defer server.Close() + + service := NewService(ServiceOptions{ + MainchainURL: server.URL, + HSDURL: server.URL, + }) + + if err := service.DiscoverAliases(context.Background()); err != nil { + t.Fatalf("expected configured RPC clients to drive discovery: %v", err) + } + + record, ok := service.Resolve("gateway.charon.lthn") + if !ok || len(record.A) != 1 || record.A[0] != "10.10.10.10" { + t.Fatalf("expected discovered record from configured clients, got %#v (ok=%t)", record, ok) + } + + health := service.Health() + if health.TreeRoot != "options-root-1" { + t.Fatalf("expected health to reflect configured HSD client tree root, got %#v", health.TreeRoot) + } + + if atomic.LoadInt32(&chainCalls) != 1 || atomic.LoadInt32(&treeRootCalls) != 1 || atomic.LoadInt32(&nameResourceCalls) != 1 { + t.Fatalf( + "expected chain=1 tree-root=1 name-resource=1, got %d %d %d", + atomic.LoadInt32(&chainCalls), + atomic.LoadInt32(&treeRootCalls), + atomic.LoadInt32(&nameResourceCalls), + ) + } +} + func TestServiceDiscoverAliasesClearsCacheWhenAliasListBecomesEmpty(t *testing.T) { var hsdCalls int32