github.com/status-im/status-go@v1.1.0/services/wallet/thirdparty/coingecko/client_test.go (about)

     1  package coingecko
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"reflect"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/status-im/status-go/services/wallet/thirdparty"
    13  )
    14  
    15  type TestTokenPlatform struct {
    16  	Ethereum string `json:"ethereum"`
    17  	Arb      string `json:"arb"`
    18  }
    19  
    20  type TestGeckoToken struct {
    21  	ID        string            `json:"id"`
    22  	Symbol    string            `json:"symbol"`
    23  	Name      string            `json:"name"`
    24  	Platforms TestTokenPlatform `json:"platforms"`
    25  }
    26  
    27  func setupTest(t *testing.T, response []byte) (*httptest.Server, func()) {
    28  	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    29  		w.WriteHeader(200)
    30  		_, err := w.Write(response)
    31  		if err != nil {
    32  			return
    33  		}
    34  	}))
    35  
    36  	return srv, func() {
    37  		srv.Close()
    38  	}
    39  }
    40  
    41  func TestGetTokensSuccess(t *testing.T) {
    42  	expected := []GeckoToken{
    43  		{
    44  			ID:     "ethereum",
    45  			Symbol: "eth",
    46  			Name:   "Ethereum",
    47  		},
    48  		{
    49  			ID:     "status",
    50  			Symbol: "snt",
    51  			Name:   "Status",
    52  		},
    53  	}
    54  
    55  	expectedMap := map[string][]GeckoToken{
    56  		"ETH": {{
    57  			ID:     "ethereum",
    58  			Symbol: "eth",
    59  			Name:   "Ethereum",
    60  		}},
    61  		"SNT": {{
    62  			ID:     "status",
    63  			Symbol: "snt",
    64  			Name:   "Status",
    65  		}},
    66  	}
    67  	response, _ := json.Marshal(expected)
    68  
    69  	srv, stop := setupTest(t, response)
    70  	defer stop()
    71  
    72  	geckoClient := &Client{
    73  		httpClient: thirdparty.NewHTTPClient(),
    74  		tokens:     make(map[string][]GeckoToken),
    75  		baseURL:    srv.URL,
    76  	}
    77  
    78  	tokenMap, err := geckoClient.getTokens()
    79  	require.NoError(t, err)
    80  	require.True(t, reflect.DeepEqual(expectedMap, tokenMap))
    81  }
    82  
    83  func TestGetTokensEthPlatform(t *testing.T) {
    84  	tokenList := []TestGeckoToken{
    85  		{
    86  			ID:     "ethereum",
    87  			Symbol: "eth-test",
    88  			Name:   "Ethereum",
    89  			Platforms: TestTokenPlatform{
    90  				Ethereum: "0x123",
    91  			},
    92  		},
    93  		{
    94  			ID:     "usdt-bridge-test",
    95  			Symbol: "usdt-test",
    96  			Name:   "USDT Bridge Test",
    97  			Platforms: TestTokenPlatform{
    98  				Arb: "0x123",
    99  			},
   100  		},
   101  		{
   102  			ID:     "tether",
   103  			Symbol: "usdt-test",
   104  			Name:   "Tether",
   105  			Platforms: TestTokenPlatform{
   106  				Arb:      "0x1234",
   107  				Ethereum: "0x12345",
   108  			},
   109  		},
   110  		{
   111  			ID:     "AirDao",
   112  			Symbol: "amb-test",
   113  			Name:   "Amber",
   114  			Platforms: TestTokenPlatform{
   115  				Arb: "0x123455",
   116  			},
   117  		},
   118  	}
   119  
   120  	expectedMap := map[string][]GeckoToken{
   121  		"ETH-TEST": {{
   122  			ID:          "ethereum",
   123  			Symbol:      "eth-test",
   124  			Name:        "Ethereum",
   125  			EthPlatform: true,
   126  		}},
   127  		"USDT-TEST": {
   128  			{
   129  				ID:          "usdt-bridge-test",
   130  				Symbol:      "usdt-test",
   131  				Name:        "USDT Bridge Test",
   132  				EthPlatform: false,
   133  			},
   134  			{
   135  				ID:          "tether",
   136  				Symbol:      "usdt-test",
   137  				Name:        "Tether",
   138  				EthPlatform: true,
   139  			},
   140  		},
   141  		"AMB-TEST": {{
   142  			ID:          "AirDao",
   143  			Symbol:      "amb-test",
   144  			Name:        "Amber",
   145  			EthPlatform: false,
   146  		}},
   147  	}
   148  
   149  	response, _ := json.Marshal(tokenList)
   150  
   151  	srv, stop := setupTest(t, response)
   152  	defer stop()
   153  
   154  	geckoClient := &Client{
   155  		httpClient: thirdparty.NewHTTPClient(),
   156  		tokens:     make(map[string][]GeckoToken),
   157  		baseURL:    srv.URL,
   158  	}
   159  
   160  	tokenMap, err := geckoClient.getTokens()
   161  	require.NoError(t, err)
   162  	require.True(t, reflect.DeepEqual(expectedMap, tokenMap))
   163  }
   164  
   165  func TestGetTokensFailure(t *testing.T) {
   166  	resp := []byte{}
   167  	srv, stop := setupTest(t, resp)
   168  	defer stop()
   169  
   170  	geckoClient := &Client{
   171  		httpClient: thirdparty.NewHTTPClient(),
   172  		tokens:     make(map[string][]GeckoToken),
   173  		baseURL:    srv.URL,
   174  	}
   175  
   176  	_, err := geckoClient.getTokens()
   177  	require.Error(t, err)
   178  }
   179  
   180  func TestFetchPrices(t *testing.T) {
   181  	mux := http.NewServeMux()
   182  
   183  	// Register handlers for different URL paths
   184  	mux.HandleFunc("/coins/list", func(w http.ResponseWriter, r *http.Request) {
   185  		w.WriteHeader(http.StatusOK)
   186  		w.Header().Set("Content-Type", "application/json")
   187  		response := "[{\"id\":\"ethereum\",\"symbol\":\"eth\",\"name\":\"Ethereum\",\"platforms\":{\"ethereum\":\"0x5e21d1ee5cf0077b314c381720273ae82378d613\"}},{\"id\":\"status\",\"symbol\":\"snt\",\"name\":\"Status\",\"platforms\":{\"ethereum\":\"0x78ba134c3ace18e69837b01703d07f0db6fb0a60\"}}]"
   188  		_, _ = w.Write([]byte(response))
   189  	})
   190  
   191  	mux.HandleFunc("/simple/price", func(w http.ResponseWriter, r *http.Request) {
   192  		w.WriteHeader(http.StatusOK)
   193  		w.Header().Set("Content-Type", "application/json")
   194  		response := "{\"ethereum\":{\"usd\":3181.32},\"status\":{\"usd\":0.02391704}}"
   195  		_, _ = w.Write([]byte(response))
   196  	})
   197  
   198  	srv := httptest.NewServer(mux)
   199  
   200  	geckoClient := &Client{
   201  		httpClient: thirdparty.NewHTTPClient(),
   202  		tokens:     make(map[string][]GeckoToken),
   203  		baseURL:    srv.URL,
   204  	}
   205  
   206  	symbols := []string{"ETH", "SNT", "UNSUPPORTED", "TOKENS"}
   207  	prices, err := geckoClient.FetchPrices(symbols, []string{"USD"})
   208  	require.NoError(t, err)
   209  	require.Len(t, prices, 2)
   210  }
   211  
   212  func TestFetchMarketValues(t *testing.T) {
   213  	mux := http.NewServeMux()
   214  
   215  	// Register handlers for different URL paths
   216  	mux.HandleFunc("/coins/list", func(w http.ResponseWriter, r *http.Request) {
   217  		w.WriteHeader(http.StatusOK)
   218  		w.Header().Set("Content-Type", "application/json")
   219  		response := "[{\"id\":\"ethereum\",\"symbol\":\"eth\",\"name\":\"Ethereum\",\"platforms\":{\"ethereum\":\"0x5e21d1ee5cf0077b314c381720273ae82378d613\"}},{\"id\":\"status\",\"symbol\":\"snt\",\"name\":\"Status\",\"platforms\":{\"ethereum\":\"0x78ba134c3ace18e69837b01703d07f0db6fb0a60\"}}]"
   220  		_, _ = w.Write([]byte(response))
   221  	})
   222  
   223  	mux.HandleFunc("/coins/markets", func(w http.ResponseWriter, r *http.Request) {
   224  		w.WriteHeader(http.StatusOK)
   225  		w.Header().Set("Content-Type", "application/json")
   226  		response := "[{\"id\":\"ethereum\",\"symbol\":\"eth\",\"name\":\"Ethereum\",\"image\":\"https://coin-images.coingecko.com/coins/images/279/large/ethereum.png?1696501628\",\"current_price\":3177.16,\"market_cap\":382035912506,\"market_cap_rank\":2,\"fully_diluted_valuation\":382035912506,\"total_volume\":18958367285,\"high_24h\":3325.57,\"low_24h\":3139.38,\"price_change_24h\":-146.70781392198978,\"price_change_percentage_24h\":-4.41377,\"market_cap_change_24h\":-17315836985.42914,\"market_cap_change_percentage_24h\":-4.33599,\"circulating_supply\":120251313.934882,\"total_supply\":120251313.934882,\"max_supply\":null,\"ath\":4878.26,\"ath_change_percentage\":-34.74074,\"ath_date\":\"2021-11-10T14:24:19.604Z\",\"atl\":0.432979,\"atl_change_percentage\":735159.10684,\"atl_date\":\"2015-10-20T00:00:00.000Z\",\"roi\":{\"times\":64.75457822761112,\"currency\":\"btc\",\"percentage\":6475.457822761112},\"last_updated\":\"2024-08-01T14:17:02.604Z\",\"price_change_percentage_1h_in_currency\":-0.14302683386053758,\"price_change_percentage_24h_in_currency\":-4.413773698570276},{\"id\":\"status\",\"symbol\":\"snt\",\"name\":\"Status\",\"image\":\"https://coin-images.coingecko.com/coins/images/779/large/status.png?1696501931\",\"current_price\":0.02387956,\"market_cap\":94492012,\"market_cap_rank\":420,\"fully_diluted_valuation\":162355386,\"total_volume\":3315607,\"high_24h\":0.02528227,\"low_24h\":0.02351923,\"price_change_24h\":-0.001177587387552543,\"price_change_percentage_24h\":-4.69961,\"market_cap_change_24h\":-5410268.579258412,\"market_cap_change_percentage_24h\":-5.41556,\"circulating_supply\":3960483788.3096976,\"total_supply\":6804870174.0,\"max_supply\":null,\"ath\":0.684918,\"ath_change_percentage\":-96.50467,\"ath_date\":\"2018-01-03T00:00:00.000Z\",\"atl\":0.00592935,\"atl_change_percentage\":303.75704,\"atl_date\":\"2020-03-13T02:10:36.877Z\",\"roi\":null,\"last_updated\":\"2024-08-01T14:16:20.805Z\",\"price_change_percentage_1h_in_currency\":-0.21239208982552796,\"price_change_percentage_24h_in_currency\":-4.699606730698922}]"
   227  		_, _ = w.Write([]byte(response))
   228  	})
   229  
   230  	srv := httptest.NewServer(mux)
   231  
   232  	geckoClient := &Client{
   233  		httpClient: thirdparty.NewHTTPClient(),
   234  		tokens:     make(map[string][]GeckoToken),
   235  		baseURL:    srv.URL,
   236  	}
   237  
   238  	symbols := []string{"ETH", "SNT", "UNSUPPORTED", "TOKENS"}
   239  	prices, err := geckoClient.FetchTokenMarketValues(symbols, "USD")
   240  	require.NoError(t, err)
   241  	require.Len(t, prices, 2)
   242  }