130 lines
4.0 KiB
Go
130 lines
4.0 KiB
Go
package rest
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/explorer/backend/api/freshness"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type fakeStatsRow struct {
|
|
scan func(dest ...any) error
|
|
}
|
|
|
|
func (r fakeStatsRow) Scan(dest ...any) error {
|
|
return r.scan(dest...)
|
|
}
|
|
|
|
func TestLoadExplorerStatsReturnsValues(t *testing.T) {
|
|
rpc := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
var req struct {
|
|
Method string `json:"method"`
|
|
}
|
|
require.NoError(t, json.NewDecoder(r.Body).Decode(&req))
|
|
w.Header().Set("Content-Type", "application/json")
|
|
switch req.Method {
|
|
case "eth_blockNumber":
|
|
_, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":"0x2c"}`))
|
|
case "eth_getBlockByNumber":
|
|
ts := time.Now().Add(-2 * time.Second).Unix()
|
|
_, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":{"timestamp":"0x` + strconv.FormatInt(ts, 16) + `"}}`))
|
|
default:
|
|
http.Error(w, `{"jsonrpc":"2.0","id":1,"error":{"message":"unsupported"}}`, http.StatusBadRequest)
|
|
}
|
|
}))
|
|
defer rpc.Close()
|
|
t.Setenv("RPC_URL", rpc.URL)
|
|
var call int
|
|
queryRow := func(_ context.Context, _ string, _ ...any) pgx.Row {
|
|
call++
|
|
return fakeStatsRow{
|
|
scan: func(dest ...any) error {
|
|
switch call {
|
|
case 1:
|
|
*dest[0].(*int64) = 11
|
|
case 2:
|
|
*dest[0].(*int64) = 22
|
|
case 3:
|
|
*dest[0].(*int64) = 33
|
|
case 4:
|
|
*dest[0].(*int64) = 44
|
|
case 5:
|
|
*dest[0].(*sql.NullFloat64) = sql.NullFloat64{Float64: 2000, Valid: true}
|
|
case 6:
|
|
*dest[0].(*sql.NullFloat64) = sql.NullFloat64{Float64: 1.25, Valid: true}
|
|
case 7:
|
|
*dest[0].(*sql.NullFloat64) = sql.NullFloat64{Float64: 37.5, Valid: true}
|
|
case 8:
|
|
*dest[0].(*sql.NullInt64) = sql.NullInt64{Int64: 18, Valid: true}
|
|
case 9:
|
|
*dest[0].(*int64) = 44
|
|
*dest[1].(*time.Time) = time.Now().Add(-2 * time.Second)
|
|
case 10:
|
|
*dest[0].(*string) = "0xabc"
|
|
*dest[1].(*int64) = 40
|
|
*dest[2].(*time.Time) = time.Now().Add(-5 * time.Second)
|
|
case 11:
|
|
*dest[0].(*int64) = 40
|
|
*dest[1].(*time.Time) = time.Now().Add(-5 * time.Second)
|
|
default:
|
|
t.Fatalf("unexpected query call %d", call)
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
stats, err := loadExplorerStats(context.Background(), 138, queryRow)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(11), stats.TotalBlocks)
|
|
require.Equal(t, int64(22), stats.TotalTransactions)
|
|
require.Equal(t, int64(33), stats.TotalAddresses)
|
|
require.Equal(t, int64(44), stats.LatestBlock)
|
|
require.NotNil(t, stats.AverageBlockTime)
|
|
require.Equal(t, 2000.0, *stats.AverageBlockTime)
|
|
require.NotNil(t, stats.GasPrices)
|
|
require.NotNil(t, stats.GasPrices.Average)
|
|
require.Equal(t, 1.25, *stats.GasPrices.Average)
|
|
require.NotNil(t, stats.NetworkUtilizationPercentage)
|
|
require.Equal(t, 37.5, *stats.NetworkUtilizationPercentage)
|
|
require.NotNil(t, stats.TransactionsToday)
|
|
require.Equal(t, int64(18), *stats.TransactionsToday)
|
|
require.NotNil(t, stats.Freshness.ChainHead.BlockNumber)
|
|
require.Equal(t, int64(40), *stats.Freshness.LatestIndexedTransaction.BlockNumber)
|
|
require.Equal(t, int64(4), *stats.Freshness.LatestNonEmptyBlock.DistanceFromHead)
|
|
require.Equal(t, "reported", string(stats.Freshness.ChainHead.Source))
|
|
require.Equal(t, freshness.CompletenessComplete, stats.Completeness.GasMetrics)
|
|
require.Equal(t, freshness.CompletenessComplete, stats.Completeness.UtilizationMetric)
|
|
}
|
|
|
|
func TestLoadExplorerStatsReturnsErrorWhenQueryFails(t *testing.T) {
|
|
t.Setenv("RPC_URL", "")
|
|
queryRow := func(_ context.Context, query string, _ ...any) pgx.Row {
|
|
return fakeStatsRow{
|
|
scan: func(dest ...any) error {
|
|
if strings.Contains(query, "COUNT(*) FROM transactions") {
|
|
return errors.New("boom")
|
|
}
|
|
target, ok := dest[0].(*int64)
|
|
require.True(t, ok)
|
|
*target = 1
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
_, err := loadExplorerStats(context.Background(), 138, queryRow)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "query total transactions")
|
|
}
|