Files
explorer-monorepo/backend/api/rest/config_test.go
2026-03-28 15:56:42 -07:00

228 lines
7.0 KiB
Go

package rest
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
)
type testNetworksCatalog struct {
Name string `json:"name"`
DefaultChainID int `json:"defaultChainId"`
ExplorerURL string `json:"explorerUrl"`
TokenListURL string `json:"tokenListUrl"`
GeneratedBy string `json:"generatedBy"`
Chains []struct {
ChainID string `json:"chainId"`
ChainIDDecimal int `json:"chainIdDecimal"`
ChainName string `json:"chainName"`
ShortName string `json:"shortName"`
RPCURLs []string `json:"rpcUrls"`
BlockExplorerURL []string `json:"blockExplorerUrls"`
InfoURL string `json:"infoURL"`
ExplorerAPIURL string `json:"explorerApiUrl"`
Testnet bool `json:"testnet"`
} `json:"chains"`
}
type testTokenList struct {
Name string `json:"name"`
Keywords []string
Extensions struct {
DefaultChainID int `json:"defaultChainId"`
ExplorerURL string `json:"explorerUrl"`
NetworksConfigURL string `json:"networksConfigUrl"`
} `json:"extensions"`
Tokens []struct {
ChainID int `json:"chainId"`
Address string `json:"address"`
Symbol string `json:"symbol"`
Decimals int `json:"decimals"`
Extensions struct {
UnitOfAccount string `json:"unitOfAccount"`
UnitDescription string `json:"unitDescription"`
} `json:"extensions"`
} `json:"tokens"`
}
type testCapabilitiesCatalog struct {
Name string `json:"name"`
GeneratedBy string `json:"generatedBy"`
ChainID int `json:"chainId"`
ChainName string `json:"chainName"`
RPCURL string `json:"rpcUrl"`
HTTP struct {
SupportedMethods []string `json:"supportedMethods"`
UnsupportedMethods []string `json:"unsupportedMethods"`
Notes []string `json:"notes"`
} `json:"http"`
Tracing struct {
SupportedMethods []string `json:"supportedMethods"`
UnsupportedMethods []string `json:"unsupportedMethods"`
Notes []string `json:"notes"`
} `json:"tracing"`
WalletSupport struct {
WalletAddEthereumChain bool `json:"walletAddEthereumChain"`
WalletWatchAsset bool `json:"walletWatchAsset"`
Notes []string `json:"notes"`
} `json:"walletSupport"`
}
func setupConfigHandler() http.Handler {
server := NewServer(nil, 138)
mux := http.NewServeMux()
server.SetupRoutes(mux)
return server.addMiddleware(mux)
}
func containsString(items []string, want string) bool {
for _, item := range items {
if item == want {
return true
}
}
return false
}
func TestConfigNetworksEndpointProvidesWalletMetadata(t *testing.T) {
handler := setupConfigHandler()
req := httptest.NewRequest(http.MethodGet, "/api/config/networks", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", w.Code)
}
if got := w.Header().Get("Access-Control-Allow-Origin"); got == "" {
t.Fatal("expected CORS header on config endpoint")
}
var payload testNetworksCatalog
if err := json.Unmarshal(w.Body.Bytes(), &payload); err != nil {
t.Fatalf("failed to parse networks payload: %v", err)
}
if payload.DefaultChainID != 138 {
t.Fatalf("expected defaultChainId 138, got %d", payload.DefaultChainID)
}
if payload.ExplorerURL == "" || payload.TokenListURL == "" || payload.GeneratedBy == "" {
t.Fatal("expected root metadata fields to be populated")
}
if len(payload.Chains) < 3 {
t.Fatalf("expected multiple chain entries, got %d", len(payload.Chains))
}
var foundChain138 bool
for _, chain := range payload.Chains {
if chain.ChainIDDecimal != 138 {
continue
}
foundChain138 = true
if chain.ShortName == "" || chain.InfoURL == "" || chain.ExplorerAPIURL == "" {
t.Fatal("expected Chain 138 optional metadata to be populated")
}
if len(chain.RPCURLs) == 0 || len(chain.BlockExplorerURL) == 0 {
t.Fatal("expected Chain 138 RPC and explorer URLs")
}
if chain.Testnet {
t.Fatal("expected Chain 138 to be marked as mainnet")
}
}
if !foundChain138 {
t.Fatal("expected Chain 138 entry in networks catalog")
}
}
func TestConfigTokenListEndpointProvidesOptionalMetadata(t *testing.T) {
handler := setupConfigHandler()
req := httptest.NewRequest(http.MethodGet, "/api/config/token-list", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", w.Code)
}
var payload testTokenList
if err := json.Unmarshal(w.Body.Bytes(), &payload); err != nil {
t.Fatalf("failed to parse token list payload: %v", err)
}
if len(payload.Keywords) == 0 {
t.Fatal("expected token list keywords")
}
if payload.Extensions.DefaultChainID != 138 || payload.Extensions.ExplorerURL == "" || payload.Extensions.NetworksConfigURL == "" {
t.Fatal("expected root-level token list extensions")
}
var foundCXAUC bool
var foundCUSDT bool
for _, token := range payload.Tokens {
switch token.Symbol {
case "cXAUC":
foundCXAUC = true
if token.Extensions.UnitOfAccount == "" || token.Extensions.UnitDescription == "" {
t.Fatal("expected cXAUC optional unit metadata")
}
case "cUSDT":
foundCUSDT = true
if token.Decimals != 6 {
t.Fatalf("expected cUSDT decimals 6, got %d", token.Decimals)
}
}
}
if !foundCXAUC || !foundCUSDT {
t.Fatal("expected cXAUC and cUSDT in token list")
}
}
func TestConfigCapabilitiesEndpointProvidesRPCCapabilityMatrix(t *testing.T) {
handler := setupConfigHandler()
req := httptest.NewRequest(http.MethodGet, "/api/config/capabilities", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", w.Code)
}
var payload testCapabilitiesCatalog
if err := json.Unmarshal(w.Body.Bytes(), &payload); err != nil {
t.Fatalf("failed to parse capabilities payload: %v", err)
}
if payload.ChainID != 138 || payload.ChainName == "" || payload.RPCURL == "" || payload.GeneratedBy == "" {
t.Fatal("expected populated chain-level capability metadata")
}
if !payload.WalletSupport.WalletAddEthereumChain || !payload.WalletSupport.WalletWatchAsset {
t.Fatal("expected wallet support flags to be true")
}
if !containsString(payload.HTTP.SupportedMethods, "eth_feeHistory") {
t.Fatal("expected eth_feeHistory support to be documented")
}
if !containsString(payload.HTTP.UnsupportedMethods, "eth_maxPriorityFeePerGas") {
t.Fatal("expected missing eth_maxPriorityFeePerGas support to be documented")
}
if !containsString(payload.Tracing.SupportedMethods, "trace_block") {
t.Fatal("expected trace_block support to be documented")
}
}
func TestConfigEndpointsSupportOptionsPreflight(t *testing.T) {
handler := setupConfigHandler()
req := httptest.NewRequest(http.MethodOptions, "/api/config/token-list", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("expected 200 preflight response, got %d", w.Code)
}
if got := w.Header().Get("Access-Control-Allow-Methods"); got == "" {
t.Fatal("expected Access-Control-Allow-Methods header")
}
}