1// Copyright (C) 2019 Storj Labs, Inc. 2// See LICENSE for copying information. 3 4package consoleapi_test 5 6import ( 7 "context" 8 "encoding/json" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "net/http" 13 "testing" 14 "time" 15 16 "github.com/stretchr/testify/require" 17 18 "storj.io/common/storj" 19 "storj.io/common/testcontext" 20 "storj.io/storj/private/testplanet" 21 "storj.io/storj/storagenode/payouts" 22 "storj.io/storj/storagenode/reputation" 23) 24 25func TestHeldAmountApi(t *testing.T) { 26 testplanet.Run(t, 27 testplanet.Config{ 28 SatelliteCount: 1, 29 StorageNodeCount: 1, 30 }, 31 func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) { 32 satellite := planet.Satellites[0] 33 sno := planet.StorageNodes[0] 34 console := sno.Console 35 payoutsDB := sno.DB.Payout() 36 reputationDB := sno.DB.Reputation() 37 satellitesDB := sno.DB.Satellites() 38 baseURL := fmt.Sprintf("http://%s/api/heldamount", console.Listener.Addr()) 39 40 // pause nodestats reputation cache because later tests assert a specific joinedat. 41 sno.NodeStats.Cache.Reputation.Pause() 42 43 period := "2020-03" 44 paystub := payouts.PayStub{ 45 SatelliteID: satellite.ID(), 46 Period: period, 47 Created: time.Now().UTC(), 48 Codes: "qwe", 49 UsageAtRest: 1, 50 UsageGet: 2, 51 UsagePut: 3, 52 UsageGetRepair: 4, 53 UsagePutRepair: 5, 54 UsageGetAudit: 6, 55 CompAtRest: 7, 56 CompGet: 8, 57 CompPut: 9, 58 CompGetRepair: 10, 59 CompPutRepair: 11, 60 CompGetAudit: 12, 61 SurgePercent: 13, 62 Held: 14, 63 Owed: 15, 64 Disposed: 16, 65 Paid: 17, 66 } 67 err := payoutsDB.StorePayStub(ctx, paystub) 68 require.NoError(t, err) 69 70 t.Run("test SatellitePayStubMonthly", func(t *testing.T) { 71 // should return paystub inserted earlier 72 url := fmt.Sprintf("%s/paystubs/%s?id=%s", baseURL, period, satellite.ID().String()) 73 res, err := httpGet(ctx, url) 74 require.NoError(t, err) 75 require.NotNil(t, res) 76 require.Equal(t, http.StatusOK, res.StatusCode) 77 78 paystub.UsageAtRest /= 720 79 80 expected, err := json.Marshal(paystub) 81 require.NoError(t, err) 82 83 defer func() { 84 err = res.Body.Close() 85 require.NoError(t, err) 86 }() 87 body, err := ioutil.ReadAll(res.Body) 88 require.NoError(t, err) 89 90 require.Equal(t, string(expected)+"\n", string(body)) 91 92 // should return 404 cause no payouts for the period. 93 url = fmt.Sprintf("%s/paystubs/%s?id=%s", baseURL, "2020-01", satellite.ID().String()) 94 res2, err := httpGet(ctx, url) 95 require.NoError(t, err) 96 require.NotNil(t, res2) 97 require.Equal(t, http.StatusOK, res2.StatusCode) 98 99 defer func() { 100 err = res2.Body.Close() 101 require.NoError(t, err) 102 }() 103 body2, err := ioutil.ReadAll(res2.Body) 104 require.NoError(t, err) 105 106 expected = []byte("null\n") 107 require.Equal(t, expected, body2) 108 109 // should return 400 cause of wrong satellite id. 110 url = fmt.Sprintf("%s/paystubs/%s?id=%s", baseURL, "2020-01", "123") 111 res3, err := httpGet(ctx, url) 112 require.NoError(t, err) 113 require.NotNil(t, res3) 114 require.Equal(t, http.StatusBadRequest, res3.StatusCode) 115 116 defer func() { 117 err = res3.Body.Close() 118 require.NoError(t, err) 119 }() 120 }) 121 122 paystub2 := payouts.PayStub{ 123 SatelliteID: storj.NodeID{1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 0}, 124 Period: period, 125 Created: time.Now().UTC(), 126 Codes: "qwe", 127 UsageAtRest: 1, 128 UsageGet: 2, 129 UsagePut: 3, 130 UsageGetRepair: 4, 131 UsagePutRepair: 5, 132 UsageGetAudit: 6, 133 CompAtRest: 7, 134 CompGet: 8, 135 CompPut: 9, 136 CompGetRepair: 10, 137 CompPutRepair: 11, 138 CompGetAudit: 12, 139 SurgePercent: 13, 140 Held: 14, 141 Owed: 15, 142 Disposed: 16, 143 Paid: 17, 144 } 145 err = payoutsDB.StorePayStub(ctx, paystub2) 146 require.NoError(t, err) 147 148 t.Run("test AllPayStubsMonthly", func(t *testing.T) { 149 // should return 2 paystubs inserted earlier 150 url := fmt.Sprintf("%s/paystubs/%s", baseURL, period) 151 res, err := httpGet(ctx, url) 152 require.NoError(t, err) 153 require.NotNil(t, res) 154 require.Equal(t, http.StatusOK, res.StatusCode) 155 156 paystub2.UsageAtRest /= 720 157 158 expected, err := json.Marshal([]payouts.PayStub{paystub2, paystub}) 159 require.NoError(t, err) 160 161 defer func() { 162 err = res.Body.Close() 163 require.NoError(t, err) 164 }() 165 body, err := ioutil.ReadAll(res.Body) 166 require.NoError(t, err) 167 168 require.Equal(t, string(expected)+"\n", string(body)) 169 170 // should return 2 paystubs inserted earlier 171 url = fmt.Sprintf("%s/paystubs/%s", baseURL, "2020-01") 172 res2, err := httpGet(ctx, url) 173 require.NoError(t, err) 174 require.NotNil(t, res2) 175 require.Equal(t, http.StatusOK, res2.StatusCode) 176 177 defer func() { 178 err = res2.Body.Close() 179 require.NoError(t, err) 180 }() 181 body2, err := ioutil.ReadAll(res2.Body) 182 require.NoError(t, err) 183 184 require.Equal(t, "null\n", string(body2)) 185 }) 186 187 period2 := "2020-02" 188 paystub3 := payouts.PayStub{ 189 SatelliteID: satellite.ID(), 190 Period: period2, 191 Created: time.Now().UTC(), 192 Codes: "qwe", 193 UsageAtRest: 1, 194 UsageGet: 2, 195 UsagePut: 3, 196 UsageGetRepair: 4, 197 UsagePutRepair: 5, 198 UsageGetAudit: 6, 199 CompAtRest: 7, 200 CompGet: 8, 201 CompPut: 9, 202 CompGetRepair: 10, 203 CompPutRepair: 11, 204 CompGetAudit: 12, 205 SurgePercent: 13, 206 Held: 14, 207 Owed: 15, 208 Disposed: 16, 209 Paid: 17, 210 } 211 err = payoutsDB.StorePayStub(ctx, paystub3) 212 require.NoError(t, err) 213 214 t.Run("test SatellitePayStubPeriod", func(t *testing.T) { 215 // should return all paystubs inserted earlier 216 url := fmt.Sprintf("%s/paystubs/%s/%s?id=%s", baseURL, period2, period, satellite.ID().String()) 217 res, err := httpGet(ctx, url) 218 require.NoError(t, err) 219 require.NotNil(t, res) 220 require.Equal(t, http.StatusOK, res.StatusCode) 221 222 paystub3.UsageAtRest /= 720 223 224 expected, err := json.Marshal([]payouts.PayStub{paystub3, paystub}) 225 require.NoError(t, err) 226 227 defer func() { 228 err = res.Body.Close() 229 require.NoError(t, err) 230 }() 231 body, err := ioutil.ReadAll(res.Body) 232 require.NoError(t, err) 233 234 require.Equal(t, string(expected)+"\n", string(body)) 235 236 url = fmt.Sprintf("%s/paystubs/%s/%s?id=%s", baseURL, period, period, satellite.ID().String()) 237 res2, err := httpGet(ctx, url) 238 require.NoError(t, err) 239 require.NotNil(t, res2) 240 require.Equal(t, http.StatusOK, res2.StatusCode) 241 242 expected, err = json.Marshal([]payouts.PayStub{paystub}) 243 require.NoError(t, err) 244 245 defer func() { 246 err = res2.Body.Close() 247 require.NoError(t, err) 248 }() 249 body2, err := ioutil.ReadAll(res2.Body) 250 require.NoError(t, err) 251 252 require.Equal(t, string(expected)+"\n", string(body2)) 253 254 url = fmt.Sprintf("%s/paystubs/%s/%s?id=%s", baseURL, period2, period, paystub2.SatelliteID.String()) 255 res3, err := httpGet(ctx, url) 256 require.NoError(t, err) 257 require.NotNil(t, res3) 258 require.Equal(t, http.StatusOK, res3.StatusCode) 259 260 expected, err = json.Marshal([]payouts.PayStub{paystub2}) 261 require.NoError(t, err) 262 263 defer func() { 264 err = res3.Body.Close() 265 require.NoError(t, err) 266 }() 267 body3, err := ioutil.ReadAll(res3.Body) 268 require.NoError(t, err) 269 270 require.Equal(t, string(expected)+"\n", string(body3)) 271 272 // should return 400 because of bad satellite id. 273 url = fmt.Sprintf("%s/paystubs/%s/%s?id=%s", baseURL, period2, period, "1") 274 res4, err := httpGet(ctx, url) 275 require.NoError(t, err) 276 require.NotNil(t, res4) 277 require.Equal(t, http.StatusBadRequest, res4.StatusCode) 278 279 defer func() { 280 err = res4.Body.Close() 281 require.NoError(t, err) 282 }() 283 284 // should return 400 because of bad period. 285 url = fmt.Sprintf("%s/paystubs/%s/%s?id=%s", baseURL, period, period2, satellite.ID().String()) 286 res5, err := httpGet(ctx, url) 287 require.NoError(t, err) 288 require.NotNil(t, res5) 289 require.Equal(t, http.StatusBadRequest, res5.StatusCode) 290 291 defer func() { 292 err = res5.Body.Close() 293 require.NoError(t, err) 294 }() 295 296 body5, err := ioutil.ReadAll(res5.Body) 297 require.NoError(t, err) 298 299 require.Equal(t, "{\"error\":\"consoleapi payouts: wrong period format: period has wrong format\"}\n", string(body5)) 300 }) 301 302 t.Run("test AllPayStubsPeriod", func(t *testing.T) { 303 // should return all paystubs inserted earlier 304 url := fmt.Sprintf("%s/paystubs/%s/%s", baseURL, period2, period) 305 res, err := httpGet(ctx, url) 306 require.NoError(t, err) 307 require.NotNil(t, res) 308 require.Equal(t, http.StatusOK, res.StatusCode) 309 310 expected, err := json.Marshal([]payouts.PayStub{paystub3, paystub2, paystub}) 311 require.NoError(t, err) 312 313 defer func() { 314 err = res.Body.Close() 315 require.NoError(t, err) 316 }() 317 body, err := ioutil.ReadAll(res.Body) 318 require.NoError(t, err) 319 320 require.Equal(t, string(expected)+"\n", string(body)) 321 322 url = fmt.Sprintf("%s/paystubs/%s/%s", baseURL, period, period) 323 res2, err := httpGet(ctx, url) 324 require.NoError(t, err) 325 require.NotNil(t, res2) 326 require.Equal(t, http.StatusOK, res2.StatusCode) 327 328 expected, err = json.Marshal([]payouts.PayStub{paystub2, paystub}) 329 require.NoError(t, err) 330 331 defer func() { 332 err = res2.Body.Close() 333 require.NoError(t, err) 334 }() 335 body2, err := ioutil.ReadAll(res2.Body) 336 require.NoError(t, err) 337 338 require.Equal(t, string(expected)+"\n", string(body2)) 339 340 // should return 400 because of bad period. 341 url = fmt.Sprintf("%s/paystubs/%s/%s", baseURL, period, period2) 342 res5, err := httpGet(ctx, url) 343 require.NoError(t, err) 344 require.NotNil(t, res5) 345 require.Equal(t, http.StatusBadRequest, res5.StatusCode) 346 347 defer func() { 348 err = res5.Body.Close() 349 require.NoError(t, err) 350 }() 351 352 body5, err := ioutil.ReadAll(res5.Body) 353 require.NoError(t, err) 354 355 require.Equal(t, "{\"error\":\"consoleapi payouts: wrong period format: period has wrong format\"}\n", string(body5)) 356 }) 357 358 t.Run("test HeldbackHistory", func(t *testing.T) { 359 date := time.Now().UTC().AddDate(0, -2, 0).Round(time.Minute) 360 err = reputationDB.Store(context.Background(), reputation.Stats{ 361 SatelliteID: satellite.ID(), 362 JoinedAt: date, 363 }) 364 require.NoError(t, err) 365 366 err = satellitesDB.SetAddress(ctx, satellite.ID(), satellite.Addr()) 367 require.NoError(t, err) 368 369 // should return all heldback history inserted earlier 370 url := fmt.Sprintf("%s/held-history", baseURL) 371 res, err := httpGet(ctx, url) 372 require.NoError(t, err) 373 require.NotNil(t, res) 374 require.Equal(t, http.StatusOK, res.StatusCode) 375 376 held := payouts.SatelliteHeldHistory{ 377 SatelliteID: satellite.ID(), 378 SatelliteName: satellite.Addr(), 379 HoldForFirstPeriod: 28, 380 HoldForSecondPeriod: 0, 381 HoldForThirdPeriod: 0, 382 TotalHeld: 28, 383 TotalDisposed: 32, 384 JoinedAt: date.Round(time.Minute), 385 } 386 387 var periods []payouts.SatelliteHeldHistory 388 periods = append(periods, held) 389 390 expected, err := json.Marshal(periods) 391 require.NoError(t, err) 392 393 defer func() { 394 err = res.Body.Close() 395 require.NoError(t, err) 396 }() 397 body, err := ioutil.ReadAll(res.Body) 398 require.NoError(t, err) 399 require.Equal(t, string(expected)+"\n", string(body)) 400 }) 401 402 t.Run("test Periods", func(t *testing.T) { 403 url := fmt.Sprintf("%s/periods", baseURL) 404 res, err := httpGet(ctx, url) 405 require.NoError(t, err) 406 require.NotNil(t, res) 407 require.Equal(t, http.StatusOK, res.StatusCode) 408 409 var periods []string 410 periods = append(periods, "2020-03", "2020-02") 411 412 expected, err := json.Marshal(periods) 413 require.NoError(t, err) 414 415 defer func() { 416 err = res.Body.Close() 417 require.NoError(t, err) 418 }() 419 body, err := ioutil.ReadAll(res.Body) 420 require.NoError(t, err) 421 422 require.Equal(t, string(expected)+"\n", string(body)) 423 424 // 425 url = fmt.Sprintf("%s/periods?id=%s", baseURL, paystub2.SatelliteID.String()) 426 res2, err := httpGet(ctx, url) 427 require.NoError(t, err) 428 require.NotNil(t, res) 429 require.Equal(t, http.StatusOK, res.StatusCode) 430 431 var periods2 []string 432 periods2 = append(periods2, "2020-03") 433 434 expected2, err := json.Marshal(periods2) 435 require.NoError(t, err) 436 437 defer func() { 438 err = res2.Body.Close() 439 require.NoError(t, err) 440 }() 441 body2, err := ioutil.ReadAll(res2.Body) 442 require.NoError(t, err) 443 444 require.Equal(t, string(expected2)+"\n", string(body2)) 445 }) 446 }, 447 ) 448} 449 450func httpGet(ctx context.Context, url string) (*http.Response, error) { 451 req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) 452 if err != nil { 453 return nil, err 454 } 455 return http.DefaultClient.Do(req) 456} 457 458func httpPost(ctx context.Context, url string, contentType string, b io.Reader) (*http.Response, error) { 459 req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, b) 460 if err != nil { 461 return nil, err 462 } 463 req.Header.Set("Content-Type", contentType) 464 return http.DefaultClient.Do(req) 465} 466