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 }