Files
explorer-monorepo/backend/api/rest/mission_control_test.go
defiQUG bdae5a9f6e feat: explorer API, wallet, CCIP scripts, and config refresh
- Backend REST/gateway/track routes, analytics, Blockscout proxy paths.
- Frontend wallet and liquidity surfaces; MetaMask token list alignment.
- Deployment docs, verification scripts, address inventory updates.

Check: go build ./... under backend/ (pass).
Made-with: Cursor
2026-04-07 23:22:12 -07:00

219 lines
7.5 KiB
Go

package rest
import (
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"github.com/stretchr/testify/require"
)
func resetMissionControlTestGlobals() {
liquidityPoolsCache = sync.Map{}
registryOnce = sync.Once{}
registryAddrToKey = nil
registryLoadErr = nil
}
func TestHandleMissionControlLiquidityTokenPathRequiresEnv(t *testing.T) {
resetMissionControlTestGlobals()
t.Setenv("TOKEN_AGGREGATION_BASE_URL", "")
t.Setenv("TOKEN_AGGREGATION_URL", "")
s := NewServer(nil, 138)
req := httptest.NewRequest(http.MethodGet, "/api/v1/mission-control/liquidity/token/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22/pools", nil)
w := httptest.NewRecorder()
s.handleMissionControlLiquidityTokenPath(w, req)
require.Equal(t, http.StatusServiceUnavailable, w.Code)
require.Contains(t, w.Body.String(), "TOKEN_AGGREGATION_BASE_URL not configured")
}
func TestHandleMissionControlLiquidityTokenPathCachesSuccess(t *testing.T) {
resetMissionControlTestGlobals()
var hitCount int
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hitCount++
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"data":{"count":1,"pools":[]}}`))
}))
defer upstream.Close()
t.Setenv("TOKEN_AGGREGATION_BASE_URL", upstream.URL)
t.Setenv("CHAIN_ID", "138")
s := NewServer(nil, 138)
path := "/api/v1/mission-control/liquidity/token/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22/pools"
w1 := httptest.NewRecorder()
s.handleMissionControlLiquidityTokenPath(w1, httptest.NewRequest(http.MethodGet, path, nil))
require.Equal(t, http.StatusOK, w1.Code)
require.Equal(t, "miss", w1.Header().Get("X-Mission-Control-Cache"))
w2 := httptest.NewRecorder()
s.handleMissionControlLiquidityTokenPath(w2, httptest.NewRequest(http.MethodGet, path, nil))
require.Equal(t, http.StatusOK, w2.Code)
require.Equal(t, "hit", w2.Header().Get("X-Mission-Control-Cache"))
require.Equal(t, 1, hitCount, "second request should be served from the in-memory cache")
require.JSONEq(t, w1.Body.String(), w2.Body.String())
}
func TestHandleMissionControlLiquidityTokenPathBypassesCacheWhenRequested(t *testing.T) {
resetMissionControlTestGlobals()
var hitCount int
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hitCount++
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"data":{"count":1,"pools":[]}}`))
}))
defer upstream.Close()
t.Setenv("TOKEN_AGGREGATION_BASE_URL", upstream.URL)
t.Setenv("CHAIN_ID", "138")
s := NewServer(nil, 138)
path := "/api/v1/mission-control/liquidity/token/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22/pools"
w1 := httptest.NewRecorder()
s.handleMissionControlLiquidityTokenPath(w1, httptest.NewRequest(http.MethodGet, path, nil))
require.Equal(t, http.StatusOK, w1.Code)
require.Equal(t, "miss", w1.Header().Get("X-Mission-Control-Cache"))
req2 := httptest.NewRequest(http.MethodGet, path+"?refresh=1", nil)
req2.Header.Set("Cache-Control", "no-cache")
w2 := httptest.NewRecorder()
s.handleMissionControlLiquidityTokenPath(w2, req2)
require.Equal(t, http.StatusOK, w2.Code)
require.Equal(t, "bypass", w2.Header().Get("X-Mission-Control-Cache"))
require.Equal(t, 2, hitCount, "refresh=1 should force a fresh upstream read")
}
func TestHandleMissionControlBridgeTraceLabelsFromRegistry(t *testing.T) {
resetMissionControlTestGlobals()
fromAddr := "0x1111111111111111111111111111111111111111"
toAddr := "0x2222222222222222222222222222222222222222"
registryJSON := `{
"chains": {
"138": {
"contracts": {
"CHAIN138_SOURCE_BRIDGE": "` + fromAddr + `",
"CHAIN138_DEST_BRIDGE": "` + toAddr + `"
}
}
}
}`
registryPath := filepath.Join(t.TempDir(), "smart-contracts-master.json")
require.NoError(t, os.WriteFile(registryPath, []byte(registryJSON), 0o644))
blockscout := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/api/v2/transactions/0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", r.URL.Path)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{
"from": {"hash":"` + fromAddr + `"},
"to": {"hash":"` + toAddr + `"}
}`))
}))
defer blockscout.Close()
t.Setenv("SMART_CONTRACTS_MASTER_JSON", registryPath)
t.Setenv("BLOCKSCOUT_INTERNAL_URL", blockscout.URL)
t.Setenv("EXPLORER_PUBLIC_BASE", "https://explorer.example.org")
s := NewServer(nil, 138)
req := httptest.NewRequest(http.MethodGet, "/api/v1/mission-control/bridge/trace?tx=0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", nil)
w := httptest.NewRecorder()
s.HandleMissionControlBridgeTrace(w, req)
require.Equal(t, http.StatusOK, w.Code)
var out struct {
Data map[string]any `json:"data"`
}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &out))
require.Equal(t, strings.ToLower(fromAddr), out.Data["from"])
require.Equal(t, strings.ToLower(toAddr), out.Data["to"])
require.Equal(t, "CHAIN138_SOURCE_BRIDGE", out.Data["from_registry"])
require.Equal(t, "CHAIN138_DEST_BRIDGE", out.Data["to_registry"])
require.Equal(t, "https://explorer.example.org/tx/0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", out.Data["blockscout_url"])
}
func TestHandleMissionControlBridgeTraceFallsBackToAddressInventoryLabels(t *testing.T) {
resetMissionControlTestGlobals()
fromAddr := "0x4A666F96fC8764181194447A7dFdb7d471b301C8"
toAddr := "0x152ed3e9912161b76bdfd368d0c84b7c31c10de7"
tempDir := t.TempDir()
registryPath := filepath.Join(tempDir, "smart-contracts-master.json")
inventoryPath := filepath.Join(tempDir, "address-inventory.json")
require.NoError(t, os.WriteFile(registryPath, []byte(`{
"chains": {
"138": {
"contracts": {
"CCIP_Router": "0x42DAb7b888Dd382bD5Adcf9E038dBF1fD03b4817"
}
}
}
}`), 0o644))
require.NoError(t, os.WriteFile(inventoryPath, []byte(`{
"inventory": {
"DEPLOYER_ADMIN_138": "`+fromAddr+`",
"CW_L1_BRIDGE_CHAIN138": "`+toAddr+`"
}
}`), 0o644))
blockscout := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{
"from": {"hash":"` + fromAddr + `"},
"to": {"hash":"` + toAddr + `"}
}`))
}))
defer blockscout.Close()
t.Setenv("SMART_CONTRACTS_MASTER_JSON", registryPath)
t.Setenv("EXPLORER_ADDRESS_INVENTORY_FILE", inventoryPath)
t.Setenv("BLOCKSCOUT_INTERNAL_URL", blockscout.URL)
s := NewServer(nil, 138)
req := httptest.NewRequest(http.MethodGet, "/api/v1/mission-control/bridge/trace?tx=0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", nil)
w := httptest.NewRecorder()
s.HandleMissionControlBridgeTrace(w, req)
require.Equal(t, http.StatusOK, w.Code)
var out struct {
Data map[string]any `json:"data"`
}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &out))
require.Equal(t, strings.ToLower(fromAddr), out.Data["from"])
require.Equal(t, strings.ToLower(toAddr), out.Data["to"])
require.Equal(t, "DEPLOYER_ADMIN_138", out.Data["from_registry"])
require.Equal(t, "CW_L1_BRIDGE_CHAIN138", out.Data["to_registry"])
}
func TestHandleMissionControlBridgeTraceRejectsBadHash(t *testing.T) {
resetMissionControlTestGlobals()
s := NewServer(nil, 138)
req := httptest.NewRequest(http.MethodGet, "/api/v1/mission-control/bridge/trace?tx=not-a-tx", nil)
w := httptest.NewRecorder()
s.HandleMissionControlBridgeTrace(w, req)
require.Equal(t, http.StatusBadRequest, w.Code)
require.Contains(t, w.Body.String(), "invalid transaction hash")
}