github.com/ethersphere/bee/v2@v2.2.0/pkg/api/balances_test.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package api_test
     6  
     7  import (
     8  	"errors"
     9  	"math/big"
    10  	"net/http"
    11  	"reflect"
    12  	"testing"
    13  
    14  	"github.com/ethersphere/bee/v2/pkg/accounting"
    15  	"github.com/ethersphere/bee/v2/pkg/accounting/mock"
    16  	"github.com/ethersphere/bee/v2/pkg/api"
    17  	"github.com/ethersphere/bee/v2/pkg/bigint"
    18  	"github.com/ethersphere/bee/v2/pkg/jsonhttp"
    19  	"github.com/ethersphere/bee/v2/pkg/jsonhttp/jsonhttptest"
    20  	"github.com/ethersphere/bee/v2/pkg/swarm"
    21  )
    22  
    23  func TestBalances(t *testing.T) {
    24  	t.Parallel()
    25  
    26  	compensatedBalancesFunc := func() (ret map[string]*big.Int, err error) {
    27  		ret = make(map[string]*big.Int)
    28  		ret["DEAD"] = big.NewInt(1000000000000000000)
    29  		ret["BEEF"] = big.NewInt(-100000000000000000)
    30  		ret["PARTY"] = big.NewInt(0)
    31  		return ret, err
    32  	}
    33  
    34  	testServer, _, _, _ := newTestServer(t, testServerOptions{
    35  		AccountingOpts: []mock.Option{mock.WithCompensatedBalancesFunc(compensatedBalancesFunc)},
    36  	})
    37  
    38  	expected := &api.BalancesResponse{
    39  		[]api.BalanceResponse{
    40  			{
    41  				Peer:    "DEAD",
    42  				Balance: bigint.Wrap(big.NewInt(1000000000000000000)),
    43  			},
    44  			{
    45  				Peer:    "BEEF",
    46  				Balance: bigint.Wrap(big.NewInt(-100000000000000000)),
    47  			},
    48  			{
    49  				Peer:    "PARTY",
    50  				Balance: bigint.Wrap(big.NewInt(0)),
    51  			},
    52  		},
    53  	}
    54  
    55  	// We expect a list of items unordered by peer:
    56  	var got *api.BalancesResponse
    57  	jsonhttptest.Request(t, testServer, http.MethodGet, "/balances", http.StatusOK,
    58  		jsonhttptest.WithUnmarshalJSONResponse(&got),
    59  	)
    60  
    61  	if !equalBalances(got, expected) {
    62  		t.Errorf("got balances: %v, expected: %v", got, expected)
    63  	}
    64  }
    65  
    66  func TestBalancesError(t *testing.T) {
    67  	t.Parallel()
    68  
    69  	wantErr := errors.New("ASDF")
    70  	compensatedBalancesFunc := func() (ret map[string]*big.Int, err error) {
    71  		return nil, wantErr
    72  	}
    73  	testServer, _, _, _ := newTestServer(t, testServerOptions{
    74  		AccountingOpts: []mock.Option{mock.WithCompensatedBalancesFunc(compensatedBalancesFunc)},
    75  	})
    76  
    77  	jsonhttptest.Request(t, testServer, http.MethodGet, "/balances", http.StatusInternalServerError,
    78  		jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
    79  			Message: api.ErrCantBalances,
    80  			Code:    http.StatusInternalServerError,
    81  		}),
    82  	)
    83  }
    84  
    85  func TestBalancesPeers(t *testing.T) {
    86  	t.Parallel()
    87  
    88  	peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
    89  	compensatedBalanceFunc := func(swarm.Address) (*big.Int, error) {
    90  		return big.NewInt(100000000000000000), nil
    91  	}
    92  	testServer, _, _, _ := newTestServer(t, testServerOptions{
    93  		AccountingOpts: []mock.Option{mock.WithCompensatedBalanceFunc(compensatedBalanceFunc)},
    94  	})
    95  
    96  	jsonhttptest.Request(t, testServer, http.MethodGet, "/balances/"+peer, http.StatusOK,
    97  		jsonhttptest.WithExpectedJSONResponse(api.BalanceResponse{
    98  			Peer:    peer,
    99  			Balance: bigint.Wrap(big.NewInt(100000000000000000)),
   100  		}),
   101  	)
   102  }
   103  
   104  func TestBalancesPeersError(t *testing.T) {
   105  	t.Parallel()
   106  
   107  	peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
   108  	wantErr := errors.New("Error")
   109  	compensatedBalanceFunc := func(swarm.Address) (*big.Int, error) {
   110  		return nil, wantErr
   111  	}
   112  	testServer, _, _, _ := newTestServer(t, testServerOptions{
   113  		AccountingOpts: []mock.Option{mock.WithCompensatedBalanceFunc(compensatedBalanceFunc)},
   114  	})
   115  
   116  	jsonhttptest.Request(t, testServer, http.MethodGet, "/balances/"+peer, http.StatusInternalServerError,
   117  		jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
   118  			Message: api.ErrCantBalance,
   119  			Code:    http.StatusInternalServerError,
   120  		}),
   121  	)
   122  }
   123  
   124  func TestBalancesPeersNoBalance(t *testing.T) {
   125  	t.Parallel()
   126  
   127  	peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
   128  	compensatedBalanceFunc := func(swarm.Address) (*big.Int, error) {
   129  		return nil, accounting.ErrPeerNoBalance
   130  	}
   131  	testServer, _, _, _ := newTestServer(t, testServerOptions{
   132  		AccountingOpts: []mock.Option{mock.WithCompensatedBalanceFunc(compensatedBalanceFunc)},
   133  	})
   134  
   135  	jsonhttptest.Request(t, testServer, http.MethodGet, "/balances/"+peer, http.StatusNotFound,
   136  		jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
   137  			Message: api.ErrNoBalance,
   138  			Code:    http.StatusNotFound,
   139  		}),
   140  	)
   141  }
   142  
   143  func equalBalances(a, b *api.BalancesResponse) bool {
   144  	var state bool
   145  
   146  	for akeys := range a.Balances {
   147  		state = false
   148  		for bkeys := range b.Balances {
   149  			if reflect.DeepEqual(a.Balances[akeys], b.Balances[bkeys]) {
   150  				state = true
   151  			}
   152  		}
   153  		if !state {
   154  			return false
   155  		}
   156  	}
   157  
   158  	for bkeys := range b.Balances {
   159  		state = false
   160  		for akeys := range a.Balances {
   161  			if reflect.DeepEqual(a.Balances[akeys], b.Balances[bkeys]) {
   162  				state = true
   163  			}
   164  		}
   165  		if !state {
   166  			return false
   167  		}
   168  	}
   169  
   170  	return true
   171  }
   172  
   173  func TestConsumedBalances(t *testing.T) {
   174  	t.Parallel()
   175  
   176  	balancesFunc := func() (ret map[string]*big.Int, err error) {
   177  		ret = make(map[string]*big.Int)
   178  		ret["DEAD"] = big.NewInt(1000000000000000000)
   179  		ret["BEEF"] = big.NewInt(-100000000000000000)
   180  		ret["PARTY"] = big.NewInt(0)
   181  		return ret, err
   182  	}
   183  	testServer, _, _, _ := newTestServer(t, testServerOptions{
   184  		AccountingOpts: []mock.Option{mock.WithBalancesFunc(balancesFunc)},
   185  	})
   186  
   187  	expected := &api.BalancesResponse{
   188  		[]api.BalanceResponse{
   189  			{
   190  				Peer:    "DEAD",
   191  				Balance: bigint.Wrap(big.NewInt(1000000000000000000)),
   192  			},
   193  			{
   194  				Peer:    "BEEF",
   195  				Balance: bigint.Wrap(big.NewInt(-100000000000000000)),
   196  			},
   197  			{
   198  				Peer:    "PARTY",
   199  				Balance: bigint.Wrap(big.NewInt(0)),
   200  			},
   201  		},
   202  	}
   203  
   204  	// We expect a list of items unordered by peer:
   205  	var got *api.BalancesResponse
   206  	jsonhttptest.Request(t, testServer, http.MethodGet, "/consumed", http.StatusOK,
   207  		jsonhttptest.WithUnmarshalJSONResponse(&got),
   208  	)
   209  
   210  	if !equalBalances(got, expected) {
   211  		t.Errorf("got balances: %v, expected: %v", got, expected)
   212  	}
   213  
   214  }
   215  
   216  func TestConsumedError(t *testing.T) {
   217  	t.Parallel()
   218  
   219  	wantErr := errors.New("ASDF")
   220  	balancesFunc := func() (ret map[string]*big.Int, err error) {
   221  		return nil, wantErr
   222  	}
   223  	testServer, _, _, _ := newTestServer(t, testServerOptions{
   224  		AccountingOpts: []mock.Option{mock.WithBalancesFunc(balancesFunc)},
   225  	})
   226  
   227  	jsonhttptest.Request(t, testServer, http.MethodGet, "/consumed", http.StatusInternalServerError,
   228  		jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
   229  			Message: api.ErrCantBalances,
   230  			Code:    http.StatusInternalServerError,
   231  		}),
   232  	)
   233  }
   234  
   235  func TestConsumedPeers(t *testing.T) {
   236  	t.Parallel()
   237  
   238  	peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
   239  	balanceFunc := func(swarm.Address) (*big.Int, error) {
   240  		return big.NewInt(1000000000000000000), nil
   241  	}
   242  	testServer, _, _, _ := newTestServer(t, testServerOptions{
   243  		AccountingOpts: []mock.Option{mock.WithBalanceFunc(balanceFunc)},
   244  	})
   245  
   246  	jsonhttptest.Request(t, testServer, http.MethodGet, "/consumed/"+peer, http.StatusOK,
   247  		jsonhttptest.WithExpectedJSONResponse(api.BalanceResponse{
   248  			Peer:    peer,
   249  			Balance: bigint.Wrap(big.NewInt(1000000000000000000)),
   250  		}),
   251  	)
   252  }
   253  
   254  func TestConsumedPeersError(t *testing.T) {
   255  	t.Parallel()
   256  
   257  	peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
   258  	wantErr := errors.New("Error")
   259  	balanceFunc := func(swarm.Address) (*big.Int, error) {
   260  		return nil, wantErr
   261  	}
   262  	testServer, _, _, _ := newTestServer(t, testServerOptions{
   263  		AccountingOpts: []mock.Option{mock.WithBalanceFunc(balanceFunc)},
   264  	})
   265  
   266  	jsonhttptest.Request(t, testServer, http.MethodGet, "/consumed/"+peer, http.StatusInternalServerError,
   267  		jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
   268  			Message: api.ErrCantBalance,
   269  			Code:    http.StatusInternalServerError,
   270  		}),
   271  	)
   272  }
   273  
   274  func TestConsumedPeersNoBalance(t *testing.T) {
   275  	t.Parallel()
   276  
   277  	peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
   278  	balanceFunc := func(swarm.Address) (*big.Int, error) {
   279  		return nil, accounting.ErrPeerNoBalance
   280  	}
   281  	testServer, _, _, _ := newTestServer(t, testServerOptions{
   282  		AccountingOpts: []mock.Option{mock.WithBalanceFunc(balanceFunc)},
   283  	})
   284  
   285  	jsonhttptest.Request(t, testServer, http.MethodGet, "/consumed/"+peer, http.StatusNotFound,
   286  		jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
   287  			Message: api.ErrNoBalance,
   288  			Code:    http.StatusNotFound,
   289  		}),
   290  	)
   291  }
   292  
   293  func Test_peerBalanceHandler_invalidInputs(t *testing.T) {
   294  	t.Parallel()
   295  
   296  	client, _, _, _ := newTestServer(t, testServerOptions{})
   297  
   298  	tests := []struct {
   299  		name string
   300  		peer string
   301  		want jsonhttp.StatusResponse
   302  	}{{
   303  		name: "peer - odd hex string",
   304  		peer: "123",
   305  		want: jsonhttp.StatusResponse{
   306  			Code:    http.StatusBadRequest,
   307  			Message: "invalid path params",
   308  			Reasons: []jsonhttp.Reason{
   309  				{
   310  					Field: "peer",
   311  					Error: api.ErrHexLength.Error(),
   312  				},
   313  			},
   314  		},
   315  	}, {
   316  		name: "peer - invalid hex character",
   317  		peer: "123G",
   318  		want: jsonhttp.StatusResponse{
   319  			Code:    http.StatusBadRequest,
   320  			Message: "invalid path params",
   321  			Reasons: []jsonhttp.Reason{
   322  				{
   323  					Field: "peer",
   324  					Error: api.HexInvalidByteError('G').Error(),
   325  				},
   326  			},
   327  		},
   328  	}}
   329  
   330  	for _, tc := range tests {
   331  		tc := tc
   332  		t.Run(tc.name, func(t *testing.T) {
   333  			t.Parallel()
   334  
   335  			jsonhttptest.Request(t, client, http.MethodGet, "/consumed/"+tc.peer, tc.want.Code,
   336  				jsonhttptest.WithExpectedJSONResponse(tc.want),
   337  			)
   338  		})
   339  	}
   340  }
   341  
   342  func Test_compensatedPeerBalanceHandler_invalidInputs(t *testing.T) {
   343  	t.Parallel()
   344  
   345  	client, _, _, _ := newTestServer(t, testServerOptions{})
   346  
   347  	tests := []struct {
   348  		name string
   349  		peer string
   350  		want jsonhttp.StatusResponse
   351  	}{{
   352  		name: "peer - odd hex string",
   353  		peer: "123",
   354  		want: jsonhttp.StatusResponse{
   355  			Code:    http.StatusBadRequest,
   356  			Message: "invalid path params",
   357  			Reasons: []jsonhttp.Reason{
   358  				{
   359  					Field: "peer",
   360  					Error: api.ErrHexLength.Error(),
   361  				},
   362  			},
   363  		},
   364  	}, {
   365  		name: "peer - invalid hex character",
   366  		peer: "123G",
   367  		want: jsonhttp.StatusResponse{
   368  			Code:    http.StatusBadRequest,
   369  			Message: "invalid path params",
   370  			Reasons: []jsonhttp.Reason{
   371  				{
   372  					Field: "peer",
   373  					Error: api.HexInvalidByteError('G').Error(),
   374  				},
   375  			},
   376  		},
   377  	}}
   378  
   379  	for _, tc := range tests {
   380  		tc := tc
   381  		t.Run(tc.name, func(t *testing.T) {
   382  			t.Parallel()
   383  
   384  			jsonhttptest.Request(t, client, http.MethodGet, "/balances/"+tc.peer, tc.want.Code,
   385  				jsonhttptest.WithExpectedJSONResponse(tc.want),
   386  			)
   387  		})
   388  	}
   389  }