2022-04-26 21:23:27 +01:00
|
|
|
// Copyright (C) 2022 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package billing_test
|
|
|
|
|
|
|
|
import (
|
2022-06-16 17:48:07 +01:00
|
|
|
"encoding/json"
|
2022-04-26 21:23:27 +01:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2022-09-06 13:43:09 +01:00
|
|
|
"storj.io/common/currency"
|
2022-04-26 21:23:27 +01:00
|
|
|
"storj.io/common/testcontext"
|
|
|
|
"storj.io/common/testrand"
|
2022-06-16 17:48:07 +01:00
|
|
|
"storj.io/storj/private/blockchain"
|
2022-04-26 21:23:27 +01:00
|
|
|
"storj.io/storj/satellite"
|
2023-09-25 22:06:01 +01:00
|
|
|
"storj.io/storj/satellite/payments"
|
2022-04-26 21:23:27 +01:00
|
|
|
"storj.io/storj/satellite/payments/billing"
|
|
|
|
"storj.io/storj/satellite/satellitedb/satellitedbtest"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestTransactionsDBList(t *testing.T) {
|
|
|
|
const (
|
|
|
|
limit = 3
|
|
|
|
transactionCount = limit * 4
|
|
|
|
)
|
|
|
|
|
|
|
|
// create transactions
|
|
|
|
userID := testrand.UUID()
|
|
|
|
|
2023-07-18 14:25:40 +01:00
|
|
|
firstTimestamp := makeTimestamp()
|
|
|
|
|
2022-04-26 21:23:27 +01:00
|
|
|
var txs []billing.Transaction
|
2022-06-16 17:48:07 +01:00
|
|
|
var txStatus billing.TransactionStatus
|
|
|
|
var txType billing.TransactionType
|
2022-04-26 21:23:27 +01:00
|
|
|
for i := 0; i < transactionCount; i++ {
|
2022-06-16 17:48:07 +01:00
|
|
|
txSource := "storjscan"
|
2022-05-10 20:19:53 +01:00
|
|
|
txStatus = billing.TransactionStatusCompleted
|
2022-06-16 17:48:07 +01:00
|
|
|
txType = billing.TransactionTypeCredit
|
2022-04-26 21:23:27 +01:00
|
|
|
if i%2 == 0 {
|
2022-06-16 17:48:07 +01:00
|
|
|
txSource = "stripe"
|
2022-04-26 21:23:27 +01:00
|
|
|
}
|
|
|
|
if i%3 == 0 {
|
2022-06-16 17:48:07 +01:00
|
|
|
txSource = "coinpayments"
|
2022-04-26 21:23:27 +01:00
|
|
|
}
|
|
|
|
|
2022-06-16 17:48:07 +01:00
|
|
|
address, err := blockchain.BytesToAddress(testrand.Bytes(20))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
metadata, err := json.Marshal(map[string]interface{}{
|
|
|
|
"ReferenceID": "some stripe invoice ID",
|
|
|
|
"Wallet": address.Hex(),
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2022-04-26 21:23:27 +01:00
|
|
|
createTX := billing.Transaction{
|
2022-06-16 17:48:07 +01:00
|
|
|
UserID: userID,
|
2022-09-06 13:43:09 +01:00
|
|
|
Amount: currency.AmountFromBaseUnits(4, currency.USDollars),
|
2022-04-26 21:23:27 +01:00
|
|
|
Description: "credit from storjscan payment",
|
2022-06-16 17:48:07 +01:00
|
|
|
Source: txSource,
|
|
|
|
Status: txStatus,
|
|
|
|
Type: txType,
|
|
|
|
Metadata: metadata,
|
2023-07-18 14:25:40 +01:00
|
|
|
Timestamp: firstTimestamp.Add(time.Duration(i) * time.Second),
|
2022-04-26 21:23:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
txs = append(txs, createTX)
|
|
|
|
}
|
|
|
|
|
2022-06-16 17:48:07 +01:00
|
|
|
t.Run("insert and list transactions", func(t *testing.T) {
|
2022-04-26 21:23:27 +01:00
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
|
|
|
for _, tx := range txs {
|
2022-06-16 17:48:07 +01:00
|
|
|
_, err := db.Billing().Insert(ctx, tx)
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
2023-03-24 12:08:40 +00:00
|
|
|
|
|
|
|
actual, err := db.Billing().List(ctx, userID)
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
2023-03-24 12:08:40 +00:00
|
|
|
require.Equal(t, len(txs), len(actual))
|
|
|
|
|
|
|
|
// The listing is in descending insertion order so compare
|
|
|
|
// accordingly (first listed compared with last inserted, etc.)
|
|
|
|
for i, act := range actual {
|
|
|
|
exp := txs[len(txs)-i-1]
|
|
|
|
compareTransactions(t, exp, act)
|
2022-04-26 21:23:27 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTransactionsDBBalance(t *testing.T) {
|
2022-09-06 13:43:09 +01:00
|
|
|
tenUSD := currency.AmountFromBaseUnits(1000, currency.USDollars)
|
|
|
|
tenMicroUSD := currency.AmountFromBaseUnits(10000000, currency.USDollarsMicro)
|
|
|
|
twentyMicroUSD := currency.AmountFromBaseUnits(20000000, currency.USDollarsMicro)
|
|
|
|
thirtyUSD := currency.AmountFromBaseUnits(3000, currency.USDollars)
|
|
|
|
fortyMicroUSD := currency.AmountFromBaseUnits(40000000, currency.USDollarsMicro)
|
|
|
|
negativeTwentyUSD := currency.AmountFromBaseUnits(-2000, currency.USDollars)
|
2022-06-16 17:48:07 +01:00
|
|
|
|
|
|
|
userID := testrand.UUID()
|
|
|
|
|
|
|
|
address, err := blockchain.BytesToAddress(testrand.Bytes(20))
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
2022-06-16 17:48:07 +01:00
|
|
|
|
2023-09-25 22:06:01 +01:00
|
|
|
creditMetadata, err := json.Marshal(map[string]interface{}{
|
|
|
|
"Wallet": address.Hex(),
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
debitMetadata, err := json.Marshal(map[string]interface{}{
|
2022-06-16 17:48:07 +01:00
|
|
|
"ReferenceID": "some stripe invoice ID",
|
|
|
|
})
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
credit10TX := billing.Transaction{
|
2022-06-16 17:48:07 +01:00
|
|
|
UserID: userID,
|
2022-04-26 21:23:27 +01:00
|
|
|
Amount: tenUSD,
|
|
|
|
Description: "credit from storjscan payment",
|
2022-06-16 17:48:07 +01:00
|
|
|
Source: "storjscan",
|
2022-05-10 20:19:53 +01:00
|
|
|
Status: billing.TransactionStatusCompleted,
|
2022-06-16 17:48:07 +01:00
|
|
|
Type: billing.TransactionTypeCredit,
|
2023-09-25 22:06:01 +01:00
|
|
|
Metadata: creditMetadata,
|
2023-03-28 02:42:26 +01:00
|
|
|
Timestamp: makeTimestamp().Add(time.Second),
|
2022-04-26 21:23:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
credit30TX := billing.Transaction{
|
2022-06-16 17:48:07 +01:00
|
|
|
UserID: userID,
|
2022-04-26 21:23:27 +01:00
|
|
|
Amount: thirtyUSD,
|
|
|
|
Description: "credit from storjscan payment",
|
2022-06-16 17:48:07 +01:00
|
|
|
Source: "storjscan",
|
2022-05-10 20:19:53 +01:00
|
|
|
Status: billing.TransactionStatusCompleted,
|
2022-06-16 17:48:07 +01:00
|
|
|
Type: billing.TransactionTypeCredit,
|
2023-09-25 22:06:01 +01:00
|
|
|
Metadata: creditMetadata,
|
2023-03-28 02:42:26 +01:00
|
|
|
Timestamp: makeTimestamp().Add(time.Second * 2),
|
2022-04-26 21:23:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
charge20TX := billing.Transaction{
|
2022-06-16 17:48:07 +01:00
|
|
|
UserID: userID,
|
2022-04-26 21:23:27 +01:00
|
|
|
Amount: negativeTwentyUSD,
|
|
|
|
Description: "charge for storage and bandwidth",
|
2022-06-16 17:48:07 +01:00
|
|
|
Source: "storjscan",
|
2022-05-10 20:19:53 +01:00
|
|
|
Status: billing.TransactionStatusCompleted,
|
2022-06-16 17:48:07 +01:00
|
|
|
Type: billing.TransactionTypeDebit,
|
2023-09-25 22:06:01 +01:00
|
|
|
Metadata: debitMetadata,
|
2023-03-28 02:42:26 +01:00
|
|
|
Timestamp: makeTimestamp().Add(time.Second * 3),
|
2022-04-26 21:23:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("add 10 USD to account", func(t *testing.T) {
|
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
2022-06-16 17:48:07 +01:00
|
|
|
_, err := db.Billing().Insert(ctx, credit10TX)
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
txs, err := db.Billing().List(ctx, userID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, txs, 1)
|
|
|
|
compareTransactions(t, credit10TX, txs[0])
|
2022-06-16 17:48:07 +01:00
|
|
|
balance, err := db.Billing().GetBalance(ctx, userID)
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
2022-08-27 00:08:03 +01:00
|
|
|
require.Equal(t, tenMicroUSD.BaseUnits(), balance.BaseUnits())
|
2022-04-26 21:23:27 +01:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("add 10 and 30 USD to account", func(t *testing.T) {
|
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
2022-06-16 17:48:07 +01:00
|
|
|
_, err := db.Billing().Insert(ctx, credit10TX)
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
2022-06-16 17:48:07 +01:00
|
|
|
_, err = db.Billing().Insert(ctx, credit30TX)
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
txs, err := db.Billing().List(ctx, userID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, txs, 2)
|
|
|
|
compareTransactions(t, credit30TX, txs[0])
|
|
|
|
compareTransactions(t, credit10TX, txs[1])
|
2022-06-16 17:48:07 +01:00
|
|
|
balance, err := db.Billing().GetBalance(ctx, userID)
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
2022-08-27 00:08:03 +01:00
|
|
|
require.Equal(t, fortyMicroUSD.BaseUnits(), balance.BaseUnits())
|
2022-04-26 21:23:27 +01:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("add 10 USD, add 30 USD, subtract 20 USD", func(t *testing.T) {
|
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
2022-06-16 17:48:07 +01:00
|
|
|
_, err := db.Billing().Insert(ctx, credit10TX)
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
2022-06-16 17:48:07 +01:00
|
|
|
_, err = db.Billing().Insert(ctx, credit30TX)
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
2022-06-16 17:48:07 +01:00
|
|
|
_, err = db.Billing().Insert(ctx, charge20TX)
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
txs, err := db.Billing().List(ctx, userID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, txs, 3)
|
|
|
|
compareTransactions(t, charge20TX, txs[0])
|
|
|
|
compareTransactions(t, credit30TX, txs[1])
|
|
|
|
compareTransactions(t, credit10TX, txs[2])
|
2022-06-16 17:48:07 +01:00
|
|
|
balance, err := db.Billing().GetBalance(ctx, userID)
|
2022-04-26 21:23:27 +01:00
|
|
|
require.NoError(t, err)
|
2022-08-27 00:08:03 +01:00
|
|
|
require.Equal(t, twentyMicroUSD.BaseUnits(), balance.BaseUnits())
|
2022-06-16 17:48:07 +01:00
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestUpdateTransactions(t *testing.T) {
|
2022-09-06 13:43:09 +01:00
|
|
|
tenUSD := currency.AmountFromBaseUnits(1000, currency.USDollars)
|
2023-09-25 22:06:01 +01:00
|
|
|
minusTenUSD := currency.AmountFromBaseUnits(-1000, currency.USDollars)
|
2022-06-16 17:48:07 +01:00
|
|
|
userID := testrand.UUID()
|
|
|
|
address, err := blockchain.BytesToAddress(testrand.Bytes(20))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2023-09-25 22:06:01 +01:00
|
|
|
creditMetadata, err := json.Marshal(map[string]interface{}{
|
|
|
|
"Wallet": address.Hex(),
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
debitMetadata, err := json.Marshal(map[string]interface{}{
|
2022-06-16 17:48:07 +01:00
|
|
|
"ReferenceID": "some stripe invoice ID",
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
credit10TX := billing.Transaction{
|
|
|
|
UserID: userID,
|
|
|
|
Amount: tenUSD,
|
|
|
|
Description: "credit from storjscan payment",
|
2023-09-25 22:06:01 +01:00
|
|
|
Source: billing.StorjScanSource,
|
|
|
|
Status: payments.PaymentStatusConfirmed,
|
2022-06-16 17:48:07 +01:00
|
|
|
Type: billing.TransactionTypeCredit,
|
2023-09-25 22:06:01 +01:00
|
|
|
Metadata: creditMetadata,
|
|
|
|
Timestamp: makeTimestamp().Add(time.Second),
|
|
|
|
}
|
|
|
|
|
|
|
|
debit10TX := billing.Transaction{
|
|
|
|
UserID: userID,
|
|
|
|
Amount: minusTenUSD,
|
|
|
|
Description: "Paid Stripe Invoice",
|
|
|
|
Source: billing.StripeSource,
|
|
|
|
Status: billing.TransactionStatusPending,
|
|
|
|
Type: billing.TransactionTypeDebit,
|
|
|
|
Metadata: debitMetadata,
|
2023-03-28 02:42:26 +01:00
|
|
|
Timestamp: makeTimestamp().Add(time.Second),
|
2022-06-16 17:48:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("update metadata", func(t *testing.T) {
|
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
2023-09-25 22:06:01 +01:00
|
|
|
_, err := db.Billing().Insert(ctx, credit10TX)
|
2022-06-16 17:48:07 +01:00
|
|
|
require.NoError(t, err)
|
2023-09-25 22:06:01 +01:00
|
|
|
txIDs, err := db.Billing().Insert(ctx, debit10TX)
|
2022-06-16 17:48:07 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
metadata, err := json.Marshal(map[string]interface{}{
|
2023-09-25 22:06:01 +01:00
|
|
|
"ReferenceID": "some other stripe invoice ID",
|
2022-06-16 17:48:07 +01:00
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
2023-03-24 12:08:40 +00:00
|
|
|
err = db.Billing().UpdateMetadata(ctx, txIDs[0], metadata)
|
2022-06-16 17:48:07 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
expMetadata, err := json.Marshal(map[string]interface{}{
|
2023-09-25 22:06:01 +01:00
|
|
|
"ReferenceID": "some other stripe invoice ID",
|
2022-06-16 17:48:07 +01:00
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
2023-09-25 22:06:01 +01:00
|
|
|
debit10TX.Metadata = expMetadata
|
2022-06-16 17:48:07 +01:00
|
|
|
tx, err := db.Billing().List(ctx, userID)
|
|
|
|
require.NoError(t, err)
|
2023-09-25 22:06:01 +01:00
|
|
|
assert.Equal(t, 2, compareMultipleTransactions(t,
|
|
|
|
[]billing.Transaction{credit10TX, debit10TX},
|
|
|
|
tx))
|
2022-06-16 17:48:07 +01:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-09-25 22:06:01 +01:00
|
|
|
t.Run("confirm new token deposit", func(t *testing.T) {
|
2022-06-16 17:48:07 +01:00
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
2023-09-25 22:06:01 +01:00
|
|
|
_, err := db.Billing().Insert(ctx, credit10TX)
|
2022-06-16 17:48:07 +01:00
|
|
|
require.NoError(t, err)
|
2023-09-25 22:06:01 +01:00
|
|
|
credit10TX.Status = payments.PaymentStatusConfirmed
|
2022-06-16 17:48:07 +01:00
|
|
|
tx, err := db.Billing().List(ctx, userID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
compareTransactions(t, credit10TX, tx[0])
|
2022-04-26 21:23:27 +01:00
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-09-25 22:06:01 +01:00
|
|
|
func TestCompletePendingPayment(t *testing.T) {
|
|
|
|
tenUSD := currency.AmountFromBaseUnits(1000, currency.USDollars)
|
|
|
|
minusTenUSD := currency.AmountFromBaseUnits(-1000, currency.USDollars)
|
|
|
|
userID := testrand.UUID()
|
|
|
|
address, err := blockchain.BytesToAddress(testrand.Bytes(20))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
creditMetadata, err := json.Marshal(map[string]interface{}{
|
|
|
|
"Wallet": address.Hex(),
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
debitMetadata, err := json.Marshal(map[string]interface{}{
|
|
|
|
"ReferenceID": "some stripe invoice ID",
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
credit10TX := billing.Transaction{
|
|
|
|
UserID: userID,
|
|
|
|
Amount: tenUSD,
|
|
|
|
Description: "credit from storjscan payment",
|
|
|
|
Source: billing.StorjScanSource,
|
|
|
|
Status: payments.PaymentStatusConfirmed,
|
|
|
|
Type: billing.TransactionTypeCredit,
|
|
|
|
Metadata: creditMetadata,
|
|
|
|
Timestamp: makeTimestamp().Add(time.Second),
|
|
|
|
}
|
|
|
|
|
|
|
|
debit10TX := billing.Transaction{
|
|
|
|
UserID: userID,
|
|
|
|
Amount: minusTenUSD,
|
|
|
|
Description: "Paid Stripe Invoice",
|
|
|
|
Source: billing.StripeSource,
|
|
|
|
Status: billing.TransactionStatusPending,
|
|
|
|
Type: billing.TransactionTypeDebit,
|
|
|
|
Metadata: debitMetadata,
|
|
|
|
Timestamp: makeTimestamp().Add(time.Second),
|
|
|
|
}
|
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
|
|
|
_, err := db.Billing().Insert(ctx, credit10TX)
|
|
|
|
require.NoError(t, err)
|
|
|
|
credit10TX.Status = payments.PaymentStatusConfirmed
|
|
|
|
tx, err := db.Billing().List(ctx, userID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
compareTransactions(t, credit10TX, tx[0])
|
|
|
|
|
|
|
|
txIDs, err := db.Billing().Insert(ctx, debit10TX)
|
|
|
|
require.NoError(t, err)
|
|
|
|
err = db.Billing().CompletePendingInvoiceTokenPayments(ctx, txIDs[0])
|
|
|
|
require.NoError(t, err)
|
|
|
|
debit10TX.Status = billing.TransactionStatusCompleted
|
|
|
|
tx, err = db.Billing().List(ctx, userID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, 2, compareMultipleTransactions(t,
|
|
|
|
[]billing.Transaction{credit10TX, debit10TX}, tx))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFailPendingPayment(t *testing.T) {
|
|
|
|
tenUSD := currency.AmountFromBaseUnits(1000, currency.USDollars)
|
|
|
|
minusTenUSD := currency.AmountFromBaseUnits(-1000, currency.USDollars)
|
|
|
|
userID := testrand.UUID()
|
|
|
|
address, err := blockchain.BytesToAddress(testrand.Bytes(20))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
creditMetadata, err := json.Marshal(map[string]interface{}{
|
|
|
|
"Wallet": address.Hex(),
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
debitMetadata, err := json.Marshal(map[string]interface{}{
|
|
|
|
"ReferenceID": "some stripe invoice ID",
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
credit10TX := billing.Transaction{
|
|
|
|
UserID: userID,
|
|
|
|
Amount: tenUSD,
|
|
|
|
Description: "credit from storjscan payment",
|
|
|
|
Source: billing.StorjScanSource,
|
|
|
|
Status: payments.PaymentStatusConfirmed,
|
|
|
|
Type: billing.TransactionTypeCredit,
|
|
|
|
Metadata: creditMetadata,
|
|
|
|
Timestamp: makeTimestamp().Add(time.Second),
|
|
|
|
}
|
|
|
|
|
|
|
|
debit10TX := billing.Transaction{
|
|
|
|
UserID: userID,
|
|
|
|
Amount: minusTenUSD,
|
|
|
|
Description: "Paid Stripe Invoice",
|
|
|
|
Source: billing.StripeSource,
|
|
|
|
Status: billing.TransactionStatusPending,
|
|
|
|
Type: billing.TransactionTypeDebit,
|
|
|
|
Metadata: debitMetadata,
|
|
|
|
Timestamp: makeTimestamp().Add(time.Second),
|
|
|
|
}
|
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
|
|
|
_, err := db.Billing().Insert(ctx, credit10TX)
|
|
|
|
require.NoError(t, err)
|
|
|
|
credit10TX.Status = payments.PaymentStatusConfirmed
|
|
|
|
tx, err := db.Billing().List(ctx, userID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
compareTransactions(t, credit10TX, tx[0])
|
|
|
|
|
|
|
|
txIDs, err := db.Billing().Insert(ctx, debit10TX)
|
|
|
|
require.NoError(t, err)
|
|
|
|
err = db.Billing().FailPendingInvoiceTokenPayments(ctx, txIDs[0])
|
|
|
|
require.NoError(t, err)
|
|
|
|
debit10TX.Status = billing.TransactionStatusFailed
|
|
|
|
tx, err = db.Billing().List(ctx, userID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, 2, compareMultipleTransactions(t,
|
|
|
|
[]billing.Transaction{credit10TX, debit10TX}, tx))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func compareMultipleTransactions(t *testing.T, exp, act []billing.Transaction) int {
|
|
|
|
var matches = 0
|
|
|
|
for _, expectedTx := range exp {
|
|
|
|
for _, actualTX := range act {
|
|
|
|
if expectedTx.Description == actualTX.Description {
|
|
|
|
matches++
|
|
|
|
compareTransactions(t, expectedTx, actualTX)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return matches
|
|
|
|
}
|
|
|
|
|
2022-04-26 21:23:27 +01:00
|
|
|
// compareTransactions is a helper method to compare tx used to create db entry,
|
|
|
|
// with the tx returned from the db. Method doesn't compare created at field, but
|
|
|
|
// ensures that is not empty.
|
|
|
|
func compareTransactions(t *testing.T, exp, act billing.Transaction) {
|
2022-06-16 17:48:07 +01:00
|
|
|
assert.Equal(t, exp.UserID, act.UserID)
|
2022-09-06 13:43:09 +01:00
|
|
|
assert.Equal(t, currency.AmountFromDecimal(exp.Amount.AsDecimal().Truncate(currency.USDollarsMicro.DecimalPlaces()), currency.USDollarsMicro), act.Amount)
|
2022-04-26 21:23:27 +01:00
|
|
|
assert.Equal(t, exp.Description, act.Description)
|
2022-06-16 17:48:07 +01:00
|
|
|
assert.Equal(t, exp.Status, act.Status)
|
|
|
|
assert.Equal(t, exp.Source, act.Source)
|
|
|
|
assert.Equal(t, exp.Type, act.Type)
|
2023-03-28 02:42:26 +01:00
|
|
|
var expUpdatedMetadata map[string]interface{}
|
|
|
|
var actUpdatedMetadata map[string]interface{}
|
2022-06-16 17:48:07 +01:00
|
|
|
err := json.Unmarshal(exp.Metadata, &expUpdatedMetadata)
|
|
|
|
require.NoError(t, err)
|
|
|
|
err = json.Unmarshal(act.Metadata, &actUpdatedMetadata)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expUpdatedMetadata["ReferenceID"], actUpdatedMetadata["ReferenceID"])
|
|
|
|
assert.Equal(t, expUpdatedMetadata["Wallet"], actUpdatedMetadata["Wallet"])
|
2023-03-28 02:42:26 +01:00
|
|
|
assert.NotEqual(t, time.Time{}, act.CreatedAt)
|
|
|
|
}
|
|
|
|
|
|
|
|
func makeTimestamp() time.Time {
|
|
|
|
// Truncate to microseconds to paper over a loss of nanosecond precision
|
|
|
|
// going in and out of the database due to timestamp column resolution.
|
|
|
|
return time.Now().Truncate(time.Microsecond)
|
2022-04-26 21:23:27 +01:00
|
|
|
}
|