[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp... #45
2 changed files with 142 additions and 4 deletions
58
service.go
58
service.go
|
|
@ -229,16 +229,20 @@ func parseActionAliasList(value any) ([]string, error) {
|
|||
switch aliases := value.(type) {
|
||||
case nil:
|
||||
return nil, fmt.Errorf("blockchain.chain.aliases action returned no value")
|
||||
case string:
|
||||
return normalizeAliasList([]string{aliases}), nil
|
||||
case []string:
|
||||
return normalizeAliasList(aliases), nil
|
||||
case []any:
|
||||
parsed := make([]string, 0, len(aliases))
|
||||
for _, item := range aliases {
|
||||
name, ok := item.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("blockchain.chain.aliases action returned non-string alias")
|
||||
name, err := parseActionAliasValue(item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if name != "" {
|
||||
parsed = append(parsed, name)
|
||||
}
|
||||
parsed = append(parsed, name)
|
||||
}
|
||||
return normalizeAliasList(parsed), nil
|
||||
case map[string]any:
|
||||
|
|
@ -248,11 +252,57 @@ func parseActionAliasList(value any) ([]string, error) {
|
|||
if rawResult, ok := aliases["result"]; ok {
|
||||
return parseActionAliasList(rawResult)
|
||||
}
|
||||
name, err := parseActionAliasRecord(aliases)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if name != "" {
|
||||
return []string{name}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("blockchain.chain.aliases action returned unsupported result type %T", value)
|
||||
}
|
||||
|
||||
func parseActionAliasValue(value any) (string, error) {
|
||||
switch alias := value.(type) {
|
||||
case string:
|
||||
return normalizeName(alias), nil
|
||||
case map[string]any:
|
||||
return parseActionAliasRecord(alias)
|
||||
default:
|
||||
return "", fmt.Errorf("blockchain.chain.aliases action returned unsupported alias item type %T", value)
|
||||
}
|
||||
}
|
||||
|
||||
func parseActionAliasRecord(record map[string]any) (string, error) {
|
||||
if hns, ok := record["hns"]; ok {
|
||||
if name, ok := hns.(string); ok {
|
||||
return normalizeName(name), nil
|
||||
}
|
||||
return "", fmt.Errorf("blockchain.chain.aliases action returned non-string hns value")
|
||||
}
|
||||
if comment, ok := record["comment"]; ok {
|
||||
if text, ok := comment.(string); ok {
|
||||
return extractAliasFromComment(text), nil
|
||||
}
|
||||
return "", fmt.Errorf("blockchain.chain.aliases action returned non-string comment value")
|
||||
}
|
||||
if alias, ok := record["alias"]; ok {
|
||||
if name, ok := alias.(string); ok {
|
||||
return normalizeName(name), nil
|
||||
}
|
||||
return "", fmt.Errorf("blockchain.chain.aliases action returned non-string alias value")
|
||||
}
|
||||
if name, ok := record["name"]; ok {
|
||||
if text, ok := name.(string); ok {
|
||||
return normalizeName(text), nil
|
||||
}
|
||||
return "", fmt.Errorf("blockchain.chain.aliases action returned non-string name value")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// DiscoverFromMainchainAliases updates records from main-chain aliases resolved through HSD.
|
||||
//
|
||||
// service.DiscoverFromMainchainAliases(context.Background(), dns.NewMainchainAliasClient(dns.MainchainClientOptions{
|
||||
|
|
|
|||
|
|
@ -677,6 +677,94 @@ func TestServiceDiscoverAliasesUsesConfiguredChainAliasAction(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestServiceDiscoverAliasesParsesAliasDetailRecordsFromActionCaller(t *testing.T) {
|
||||
var treeRootCalls int32
|
||||
var nameResourceCalls int32
|
||||
actionCalled := false
|
||||
|
||||
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 "getblockchaininfo":
|
||||
atomic.AddInt32(&treeRootCalls, 1)
|
||||
_ = json.NewEncoder(responseWriter).Encode(map[string]any{
|
||||
"result": map[string]any{
|
||||
"tree_root": "record-root-1",
|
||||
},
|
||||
})
|
||||
case "getnameresource":
|
||||
atomic.AddInt32(&nameResourceCalls, 1)
|
||||
switch payload.Params[0] {
|
||||
case "gateway.charon.lthn":
|
||||
_ = json.NewEncoder(responseWriter).Encode(map[string]any{
|
||||
"result": map[string]any{
|
||||
"a": []string{"10.10.10.10"},
|
||||
},
|
||||
})
|
||||
case "node.charon.lthn":
|
||||
_ = json.NewEncoder(responseWriter).Encode(map[string]any{
|
||||
"result": map[string]any{
|
||||
"aaaa": []string{"2600:1f1c:7f0:4f01::2"},
|
||||
},
|
||||
})
|
||||
default:
|
||||
t.Fatalf("unexpected alias lookup: %#v", payload.Params)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unexpected method: %s", payload.Method)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
service := NewService(ServiceOptions{
|
||||
ChainAliasActionCaller: actionCallerFunc(func(ctx context.Context, name string, values map[string]any) (any, bool, error) {
|
||||
actionCalled = true
|
||||
if name != "blockchain.chain.aliases" {
|
||||
t.Fatalf("unexpected action name: %s", name)
|
||||
}
|
||||
return map[string]any{
|
||||
"aliases": []any{
|
||||
map[string]any{
|
||||
"name": "gateway",
|
||||
"comment": "gateway alias hns=gateway.charon.lthn",
|
||||
},
|
||||
map[string]any{
|
||||
"name": "node",
|
||||
"hns": "node.charon.lthn",
|
||||
},
|
||||
},
|
||||
}, true, nil
|
||||
}),
|
||||
HSDClient: NewHSDClient(HSDClientOptions{URL: server.URL}),
|
||||
})
|
||||
|
||||
if err := service.DiscoverAliases(context.Background()); err != nil {
|
||||
t.Fatalf("expected DiscoverAliases to parse alias detail records: %v", err)
|
||||
}
|
||||
if !actionCalled {
|
||||
t.Fatal("expected action caller to be invoked")
|
||||
}
|
||||
if atomic.LoadInt32(&treeRootCalls) != 1 || atomic.LoadInt32(&nameResourceCalls) != 2 {
|
||||
t.Fatalf("expected one tree-root and two name-resource RPC calls, got treeRoot=%d nameResource=%d", atomic.LoadInt32(&treeRootCalls), atomic.LoadInt32(&nameResourceCalls))
|
||||
}
|
||||
|
||||
gateway, ok := service.Resolve("gateway.charon.lthn")
|
||||
if !ok || len(gateway.A) != 1 || gateway.A[0] != "10.10.10.10" {
|
||||
t.Fatalf("expected gateway A record, got %#v (ok=%t)", gateway, ok)
|
||||
}
|
||||
node, ok := service.Resolve("node.charon.lthn")
|
||||
if !ok || len(node.AAAA) != 1 || node.AAAA[0] != "2600:1f1c:7f0:4f01::2" {
|
||||
t.Fatalf("expected node AAAA record, got %#v (ok=%t)", node, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceDiscoverFallsBackWhenPrimaryDiscovererFails(t *testing.T) {
|
||||
primaryCalled := false
|
||||
fallbackCalled := false
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue