mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-03 01:41:44 +00:00
@@ -0,0 +1,142 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/sonr-io/sonr/test/e2e/client"
|
||||
)
|
||||
|
||||
// TestConfig holds common test configuration
|
||||
type TestConfig struct {
|
||||
ChainID string
|
||||
BaseURL string
|
||||
FaucetURL string
|
||||
StakingDenom string
|
||||
NormalDenom string
|
||||
Client *client.StarshipClient
|
||||
FaucetClient *FaucetClient
|
||||
DefaultTimeout time.Duration
|
||||
BlockTime time.Duration
|
||||
}
|
||||
|
||||
// NewTestConfig creates a new test configuration
|
||||
func NewTestConfig() *TestConfig {
|
||||
return &TestConfig{
|
||||
ChainID: "sonrtest_1-1",
|
||||
BaseURL: "http://localhost:1317",
|
||||
FaucetURL: "http://localhost:8000",
|
||||
StakingDenom: "usnr",
|
||||
NormalDenom: "snr",
|
||||
DefaultTimeout: 30 * time.Second,
|
||||
BlockTime: 2 * time.Second,
|
||||
Client: client.NewStarshipClient("http://localhost:1317"),
|
||||
FaucetClient: NewFaucetClient("http://localhost:8000"),
|
||||
}
|
||||
}
|
||||
|
||||
// AssertBalance asserts that an account has the expected balance
|
||||
func AssertBalance(t *testing.T, cfg *TestConfig, address, denom string, expectedAmount math.Int) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), cfg.DefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
balance, err := cfg.Client.GetBalance(ctx, address, denom)
|
||||
require.NoError(t, err, "failed to query balance")
|
||||
require.True(t, balance.Equal(expectedAmount),
|
||||
"expected balance %s, got %s", expectedAmount.String(), balance.String())
|
||||
}
|
||||
|
||||
// AssertBalanceGreaterThan asserts that an account balance is greater than expected
|
||||
func AssertBalanceGreaterThan(t *testing.T, cfg *TestConfig, address, denom string, minAmount math.Int) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), cfg.DefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
balance, err := cfg.Client.GetBalance(ctx, address, denom)
|
||||
require.NoError(t, err, "failed to query balance")
|
||||
require.True(t, balance.GT(minAmount),
|
||||
"expected balance > %s, got %s", minAmount.String(), balance.String())
|
||||
}
|
||||
|
||||
// AssertBalanceLessThan asserts that an account balance is less than expected
|
||||
func AssertBalanceLessThan(t *testing.T, cfg *TestConfig, address, denom string, maxAmount math.Int) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), cfg.DefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
balance, err := cfg.Client.GetBalance(ctx, address, denom)
|
||||
require.NoError(t, err, "failed to query balance")
|
||||
require.True(t, balance.LT(maxAmount),
|
||||
"expected balance < %s, got %s", maxAmount.String(), balance.String())
|
||||
}
|
||||
|
||||
// AssertSupply asserts that a denomination has the expected total supply
|
||||
func AssertSupply(t *testing.T, cfg *TestConfig, denom string, expectedSupply math.Int) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), cfg.DefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
supply, err := cfg.Client.GetSupply(ctx, denom)
|
||||
require.NoError(t, err, "failed to query supply")
|
||||
require.True(t, supply.Equal(expectedSupply),
|
||||
"expected supply %s, got %s", expectedSupply.String(), supply.String())
|
||||
}
|
||||
|
||||
// AssertTransferChannelExists asserts that an open transfer channel exists
|
||||
func AssertTransferChannelExists(t *testing.T, cfg *TestConfig) string {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), cfg.DefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
channelID, err := cfg.Client.GetTransferChannel(ctx)
|
||||
require.NoError(t, err, "failed to find transfer channel")
|
||||
require.NotEmpty(t, channelID, "transfer channel ID should not be empty")
|
||||
|
||||
return channelID
|
||||
}
|
||||
|
||||
// WaitForBlocks waits for a specified number of blocks
|
||||
func WaitForBlocks(ctx context.Context, cfg *TestConfig, blocks int) error {
|
||||
waitTime := time.Duration(blocks) * cfg.BlockTime
|
||||
select {
|
||||
case <-time.After(waitTime):
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// AssertNodeInfo asserts basic node information
|
||||
func AssertNodeInfo(t *testing.T, cfg *TestConfig, expectedNetwork string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), cfg.DefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
nodeInfo, err := cfg.Client.GetNodeInfo(ctx)
|
||||
require.NoError(t, err, "failed to query node info")
|
||||
require.Equal(t, expectedNetwork, nodeInfo.DefaultNodeInfo.Network,
|
||||
"unexpected network ID")
|
||||
require.NotEmpty(t, nodeInfo.ApplicationVersion.Version,
|
||||
"application version should not be empty")
|
||||
}
|
||||
|
||||
// SetupTestUsers creates and funds test users
|
||||
func SetupTestUsers(t *testing.T, cfg *TestConfig, fundAmount math.Int) []CreateTestUser {
|
||||
users := GetDefaultTestUsers(fundAmount, cfg.NormalDenom)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), cfg.DefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
err := cfg.FaucetClient.FundTestUsers(ctx, users)
|
||||
require.NoError(t, err, "failed to fund test users")
|
||||
|
||||
// Wait for funding transactions to be included
|
||||
err = WaitForBlocks(ctx, cfg, 2)
|
||||
require.NoError(t, err, "failed to wait for blocks")
|
||||
|
||||
// Verify funding
|
||||
for _, user := range users {
|
||||
AssertBalance(t, cfg, user.Address, user.Denom, user.Amount)
|
||||
}
|
||||
|
||||
return users
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// FaucetClient provides HTTP client for Starship faucet API
|
||||
type FaucetClient struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// NewFaucetClient creates a new faucet HTTP client
|
||||
func NewFaucetClient(baseURL string) *FaucetClient {
|
||||
return &FaucetClient{
|
||||
baseURL: baseURL,
|
||||
httpClient: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FundRequest represents faucet funding request
|
||||
type FundRequest struct {
|
||||
Address string `json:"address"`
|
||||
Coins []string `json:"coins"`
|
||||
}
|
||||
|
||||
// FundResponse represents faucet funding response
|
||||
type FundResponse struct {
|
||||
Status string `json:"status"`
|
||||
TxHash string `json:"tx_hash,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// FundAccount requests tokens from the faucet for an account
|
||||
func (f *FaucetClient) FundAccount(ctx context.Context, address string, coins []sdk.Coin) (*FundResponse, error) {
|
||||
url := fmt.Sprintf("%s/credit", f.baseURL)
|
||||
|
||||
// Convert coins to string format
|
||||
coinStrs := make([]string, len(coins))
|
||||
for i, coin := range coins {
|
||||
coinStrs[i] = coin.String()
|
||||
}
|
||||
|
||||
reqBody := FundRequest{
|
||||
Address: address,
|
||||
Coins: coinStrs,
|
||||
}
|
||||
|
||||
jsonBody, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal fund request: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonBody))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create fund request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := f.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fund account: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var fundResp FundResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&fundResp); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode fund response: %w", err)
|
||||
}
|
||||
|
||||
if fundResp.Status != "success" {
|
||||
return nil, fmt.Errorf("faucet funding failed: %s", fundResp.Error)
|
||||
}
|
||||
|
||||
return &fundResp, nil
|
||||
}
|
||||
|
||||
// FundAccountWithRetry funds an account with retry logic
|
||||
func (f *FaucetClient) FundAccountWithRetry(ctx context.Context, address string, coins []sdk.Coin, maxRetries int) error {
|
||||
const retryDelay = 3 * time.Second
|
||||
|
||||
for attempt := 0; attempt < maxRetries; attempt++ {
|
||||
_, err := f.FundAccount(ctx, address, coins)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if attempt == maxRetries-1 {
|
||||
return fmt.Errorf("failed to fund account after %d attempts: %w", maxRetries, err)
|
||||
}
|
||||
|
||||
time.Sleep(retryDelay)
|
||||
}
|
||||
|
||||
return fmt.Errorf("unreachable code")
|
||||
}
|
||||
|
||||
// CreateTestUser represents a test user with funding
|
||||
type CreateTestUser struct {
|
||||
Address string
|
||||
Amount math.Int
|
||||
Denom string
|
||||
}
|
||||
|
||||
// GetDefaultTestUsers returns default test users with addresses from Starship config
|
||||
func GetDefaultTestUsers(amount math.Int, denom string) []CreateTestUser {
|
||||
return []CreateTestUser{
|
||||
{
|
||||
Address: "idx13a6zjh96w9z9y2defkktdc6vn4r5h3s7jwxuam", // acc0 from Starship config
|
||||
Amount: amount,
|
||||
Denom: denom,
|
||||
},
|
||||
{
|
||||
Address: "idx1xehj0xc24k2c740jslfyd4d6mt8c4dczgntqhg", // acc1 from Starship config
|
||||
Amount: amount,
|
||||
Denom: denom,
|
||||
},
|
||||
{
|
||||
Address: "idx1jyq30438zx0g4urancle25r6tk5td6pgeytpfu", // user0 from Starship config
|
||||
Amount: amount,
|
||||
Denom: denom,
|
||||
},
|
||||
{
|
||||
Address: "idx1wz5qn36kdakkqunkvwuuvpr2l4amd7y0m3qdq6", // user1 from Starship config
|
||||
Amount: amount,
|
||||
Denom: denom,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FundTestUsers funds multiple test users
|
||||
func (f *FaucetClient) FundTestUsers(ctx context.Context, users []CreateTestUser) error {
|
||||
for _, user := range users {
|
||||
coins := []sdk.Coin{
|
||||
{
|
||||
Denom: user.Denom,
|
||||
Amount: user.Amount,
|
||||
},
|
||||
}
|
||||
|
||||
if err := f.FundAccountWithRetry(ctx, user.Address, coins, 3); err != nil {
|
||||
return fmt.Errorf("failed to fund user %s: %w", user.Address, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user