mirror of
https://github.com/hanzo-fi/payments.git
synced 2026-10-30 20:31:20 +00:00
565 lines
14 KiB
Go
565 lines
14 KiB
Go
package models_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"math/big"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hanzo-fi/go-libs/v5/pkg/types/pointer"
|
|
"github.com/hanzo-fi/payments/pkg/domain/models"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestFromPSPPaymentToPayment(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
now := time.Now().UTC()
|
|
connectorID := models.ConnectorID{
|
|
Reference: uuid.New(),
|
|
Provider: "test",
|
|
}
|
|
|
|
t.Run("parent reference is empty", func(t *testing.T) {
|
|
t.Parallel()
|
|
// Given
|
|
|
|
pspPayment := models.PSPPayment{
|
|
ParentReference: "",
|
|
Reference: "test1",
|
|
CreatedAt: now.UTC(),
|
|
Type: models.PAYMENT_TYPE_PAYOUT,
|
|
Amount: big.NewInt(100),
|
|
Asset: "USD/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_CANCELLED,
|
|
SourceAccountReference: pointer.For("acc"),
|
|
Metadata: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Raw: []byte(`{}`),
|
|
}
|
|
|
|
actual, err := models.FromPSPPaymentToPayment(pspPayment, connectorID)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
|
|
pid := models.PaymentID{
|
|
PaymentReference: models.PaymentReference{
|
|
Reference: "test1",
|
|
Type: models.PAYMENT_TYPE_PAYOUT,
|
|
},
|
|
ConnectorID: connectorID,
|
|
}
|
|
expected := models.Payment{
|
|
ID: pid,
|
|
ConnectorID: connectorID,
|
|
Reference: "test1",
|
|
CreatedAt: now.UTC(),
|
|
Type: models.PAYMENT_TYPE_PAYOUT,
|
|
InitialAmount: big.NewInt(100),
|
|
Amount: big.NewInt(100),
|
|
Asset: "USD/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_CANCELLED,
|
|
SourceAccountID: &models.AccountID{
|
|
Reference: "acc",
|
|
ConnectorID: connectorID,
|
|
},
|
|
Metadata: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Adjustments: []models.PaymentAdjustment{
|
|
{
|
|
ID: models.PaymentAdjustmentID{
|
|
PaymentID: pid,
|
|
Reference: "test1",
|
|
CreatedAt: now.UTC(),
|
|
Status: models.PAYMENT_STATUS_CANCELLED,
|
|
},
|
|
Reference: "test1",
|
|
CreatedAt: now.UTC(),
|
|
Status: models.PAYMENT_STATUS_CANCELLED,
|
|
Amount: big.NewInt(100),
|
|
Asset: pointer.For("USD/2"),
|
|
Metadata: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Raw: []byte(`{}`),
|
|
},
|
|
},
|
|
}
|
|
|
|
comparePayment(t, expected, actual)
|
|
})
|
|
|
|
t.Run("parent reference is not empty", func(t *testing.T) {
|
|
t.Parallel()
|
|
// Given
|
|
|
|
pspPayment := models.PSPPayment{
|
|
ParentReference: "parent_reference",
|
|
Reference: "test1",
|
|
CreatedAt: now.UTC(),
|
|
Type: models.PAYMENT_TYPE_TRANSFER,
|
|
Amount: big.NewInt(150),
|
|
Asset: "EUR/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
DestinationAccountReference: pointer.For("acc"),
|
|
Metadata: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Raw: []byte(`{}`),
|
|
}
|
|
|
|
actual, err := models.FromPSPPaymentToPayment(pspPayment, connectorID)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
|
|
pid := models.PaymentID{
|
|
PaymentReference: models.PaymentReference{
|
|
Reference: "parent_reference",
|
|
Type: models.PAYMENT_TYPE_TRANSFER,
|
|
},
|
|
ConnectorID: connectorID,
|
|
}
|
|
expected := models.Payment{
|
|
ID: pid,
|
|
ConnectorID: connectorID,
|
|
Reference: "parent_reference",
|
|
CreatedAt: now.UTC(),
|
|
Type: models.PAYMENT_TYPE_TRANSFER,
|
|
InitialAmount: big.NewInt(150),
|
|
Amount: big.NewInt(150),
|
|
Asset: "EUR/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
DestinationAccountID: &models.AccountID{
|
|
Reference: "acc",
|
|
ConnectorID: connectorID,
|
|
},
|
|
Metadata: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Adjustments: []models.PaymentAdjustment{
|
|
{
|
|
ID: models.PaymentAdjustmentID{
|
|
PaymentID: pid,
|
|
Reference: "test1",
|
|
CreatedAt: now.UTC(),
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
},
|
|
Reference: "test1",
|
|
CreatedAt: now.UTC(),
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
Amount: big.NewInt(150),
|
|
Asset: pointer.For("EUR/2"),
|
|
Metadata: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Raw: []byte(`{}`),
|
|
},
|
|
},
|
|
}
|
|
|
|
comparePayment(t, expected, actual)
|
|
})
|
|
}
|
|
|
|
func comparePayment(t *testing.T, expected, actual models.Payment) {
|
|
require.Equal(t, expected.ID, actual.ID)
|
|
require.Equal(t, expected.ConnectorID, actual.ConnectorID)
|
|
require.Equal(t, expected.Reference, actual.Reference)
|
|
require.Equal(t, expected.CreatedAt, actual.CreatedAt)
|
|
require.Equal(t, expected.Type, actual.Type)
|
|
require.Equal(t, expected.InitialAmount, actual.InitialAmount)
|
|
require.Equal(t, expected.Amount, actual.Amount)
|
|
require.Equal(t, expected.Asset, actual.Asset)
|
|
require.Equal(t, expected.Scheme, actual.Scheme)
|
|
require.Equal(t, expected.Status, actual.Status)
|
|
|
|
switch {
|
|
case expected.SourceAccountID == nil && actual.SourceAccountID == nil:
|
|
case expected.SourceAccountID != nil && actual.SourceAccountID != nil:
|
|
require.Equal(t, *expected.SourceAccountID, *actual.SourceAccountID)
|
|
default:
|
|
require.Fail(t, "source account id mismatch")
|
|
}
|
|
|
|
switch {
|
|
case expected.DestinationAccountID == nil && actual.DestinationAccountID == nil:
|
|
case expected.DestinationAccountID != nil && actual.DestinationAccountID != nil:
|
|
require.Equal(t, *expected.DestinationAccountID, *actual.DestinationAccountID)
|
|
default:
|
|
require.Fail(t, "destination account id mismatch")
|
|
}
|
|
|
|
require.Equal(t, len(expected.Metadata), len(actual.Metadata))
|
|
for k, v := range expected.Metadata {
|
|
_, ok := actual.Metadata[k]
|
|
require.True(t, ok)
|
|
require.Equal(t, v, actual.Metadata[k])
|
|
}
|
|
|
|
compareAdjustments(t, expected.Adjustments, actual.Adjustments)
|
|
}
|
|
|
|
func compareAdjustments(t *testing.T, expected, actual []models.PaymentAdjustment) {
|
|
require.Equal(t, len(expected), len(actual))
|
|
for i := range expected {
|
|
require.Equal(t, expected[i].ID, actual[i].ID)
|
|
require.Equal(t, expected[i].Reference, actual[i].Reference)
|
|
require.Equal(t, expected[i].CreatedAt, actual[i].CreatedAt)
|
|
require.Equal(t, expected[i].Status, actual[i].Status)
|
|
require.Equal(t, expected[i].Amount, actual[i].Amount)
|
|
require.Equal(t, expected[i].Asset, actual[i].Asset)
|
|
|
|
require.Equal(t, len(expected[i].Metadata), len(actual[i].Metadata))
|
|
for k, v := range expected[i].Metadata {
|
|
_, ok := actual[i].Metadata[k]
|
|
require.True(t, ok)
|
|
require.Equal(t, v, actual[i].Metadata[k])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFromPSPPayments(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
now := time.Now().UTC()
|
|
connectorID := models.ConnectorID{
|
|
Reference: uuid.New(),
|
|
Provider: "test",
|
|
}
|
|
|
|
t.Run("valid payments", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
pspPayments := []models.PSPPayment{
|
|
{
|
|
Reference: "payment1",
|
|
CreatedAt: now,
|
|
Type: models.PAYMENT_TYPE_PAYIN,
|
|
Amount: big.NewInt(100),
|
|
Asset: "USD/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
Raw: []byte(`{}`),
|
|
},
|
|
{
|
|
Reference: "payment2",
|
|
CreatedAt: now,
|
|
Type: models.PAYMENT_TYPE_PAYOUT,
|
|
Amount: big.NewInt(200),
|
|
Asset: "EUR/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_PENDING,
|
|
Raw: []byte(`{}`),
|
|
},
|
|
}
|
|
|
|
payments, err := models.FromPSPPayments(pspPayments, connectorID)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
assert.Len(t, payments, 2)
|
|
assert.Equal(t, "payment1", payments[0].Reference)
|
|
assert.Equal(t, "payment2", payments[1].Reference)
|
|
})
|
|
|
|
t.Run("invalid payment", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
invalidPayment := models.PSPPayment{
|
|
CreatedAt: now,
|
|
Type: models.PAYMENT_TYPE_PAYIN,
|
|
Amount: big.NewInt(100),
|
|
Asset: "USD/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
Raw: []byte(`{}`),
|
|
}
|
|
pspPayments := []models.PSPPayment{invalidPayment}
|
|
|
|
_, err := models.FromPSPPayments(pspPayments, connectorID)
|
|
|
|
// Then
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "missing payment reference: validation error")
|
|
})
|
|
}
|
|
|
|
func TestPaymentMarshalUnmarshal(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
now := time.Now().UTC()
|
|
connectorID := models.ConnectorID{
|
|
Reference: uuid.New(),
|
|
Provider: "test",
|
|
}
|
|
|
|
payment := models.Payment{
|
|
ID: models.PaymentID{
|
|
PaymentReference: models.PaymentReference{
|
|
Reference: "payment123",
|
|
Type: models.PAYMENT_TYPE_PAYIN,
|
|
},
|
|
ConnectorID: connectorID,
|
|
},
|
|
ConnectorID: connectorID,
|
|
Reference: "payment123",
|
|
CreatedAt: now,
|
|
Type: models.PAYMENT_TYPE_PAYIN,
|
|
InitialAmount: big.NewInt(100),
|
|
Amount: big.NewInt(100),
|
|
Asset: "USD/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
SourceAccountID: &models.AccountID{
|
|
Reference: "source123",
|
|
ConnectorID: connectorID,
|
|
},
|
|
DestinationAccountID: &models.AccountID{
|
|
Reference: "dest123",
|
|
ConnectorID: connectorID,
|
|
},
|
|
Metadata: map[string]string{
|
|
"key": "value",
|
|
},
|
|
Adjustments: []models.PaymentAdjustment{
|
|
{
|
|
ID: models.PaymentAdjustmentID{
|
|
PaymentID: models.PaymentID{
|
|
PaymentReference: models.PaymentReference{
|
|
Reference: "payment123",
|
|
Type: models.PAYMENT_TYPE_PAYIN,
|
|
},
|
|
ConnectorID: connectorID,
|
|
},
|
|
Reference: "adj123",
|
|
CreatedAt: now,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
},
|
|
Reference: "adj123",
|
|
CreatedAt: now,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
Amount: big.NewInt(100),
|
|
Asset: pointer.For("USD/2"),
|
|
Metadata: map[string]string{
|
|
"key": "value",
|
|
},
|
|
Raw: []byte(`{}`),
|
|
},
|
|
},
|
|
}
|
|
|
|
data, err := json.Marshal(payment)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
|
|
var unmarshaledPayment models.Payment
|
|
err = json.Unmarshal(data, &unmarshaledPayment)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
comparePayment(t, payment, unmarshaledPayment)
|
|
}
|
|
|
|
func TestPSPPaymentValidate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
now := time.Now().UTC()
|
|
|
|
t.Run("valid payment", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
payment := models.PSPPayment{
|
|
Reference: "payment123",
|
|
CreatedAt: now,
|
|
Type: models.PAYMENT_TYPE_PAYIN,
|
|
Amount: big.NewInt(100),
|
|
Asset: "USD/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
Raw: []byte(`{}`),
|
|
}
|
|
|
|
err := payment.Validate()
|
|
|
|
// Then
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
t.Run("missing reference", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
payment := models.PSPPayment{
|
|
CreatedAt: now,
|
|
Type: models.PAYMENT_TYPE_PAYIN,
|
|
Amount: big.NewInt(100),
|
|
Asset: "USD/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
Raw: []byte(`{}`),
|
|
}
|
|
|
|
err := payment.Validate()
|
|
|
|
// Then
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "missing payment reference: validation error")
|
|
})
|
|
|
|
t.Run("missing createdAt", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
payment := models.PSPPayment{
|
|
Reference: "payment123",
|
|
Type: models.PAYMENT_TYPE_PAYIN,
|
|
Amount: big.NewInt(100),
|
|
Asset: "USD/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
Raw: []byte(`{}`),
|
|
}
|
|
|
|
err := payment.Validate()
|
|
|
|
// Then
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "missing payment createdAt")
|
|
})
|
|
|
|
t.Run("missing type", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
payment := models.PSPPayment{
|
|
Reference: "payment123",
|
|
CreatedAt: now,
|
|
Amount: big.NewInt(100),
|
|
Asset: "USD/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
Raw: []byte(`{}`),
|
|
}
|
|
|
|
err := payment.Validate()
|
|
|
|
// Then
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "missing payment type: validation error")
|
|
})
|
|
|
|
t.Run("missing amount", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
payment := models.PSPPayment{
|
|
Reference: "payment123",
|
|
CreatedAt: now,
|
|
Type: models.PAYMENT_TYPE_PAYIN,
|
|
Asset: "USD/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
Raw: []byte(`{}`),
|
|
}
|
|
|
|
err := payment.Validate()
|
|
|
|
// Then
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "missing payment amount")
|
|
})
|
|
|
|
t.Run("invalid asset", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
payment := models.PSPPayment{
|
|
Reference: "payment123",
|
|
CreatedAt: now,
|
|
Type: models.PAYMENT_TYPE_PAYIN,
|
|
Amount: big.NewInt(100),
|
|
Asset: "invalid",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
Raw: []byte(`{}`),
|
|
}
|
|
|
|
err := payment.Validate()
|
|
|
|
// Then
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "invalid payment asset")
|
|
})
|
|
|
|
t.Run("missing raw", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
payment := models.PSPPayment{
|
|
Reference: "payment123",
|
|
CreatedAt: now,
|
|
Type: models.PAYMENT_TYPE_PAYIN,
|
|
Amount: big.NewInt(100),
|
|
Asset: "USD/2",
|
|
Scheme: models.PAYMENT_SCHEME_OTHER,
|
|
Status: models.PAYMENT_STATUS_SUCCEEDED,
|
|
}
|
|
|
|
err := payment.Validate()
|
|
|
|
// Then
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "missing payment raw: validation error")
|
|
})
|
|
}
|
|
|
|
func TestPSPPaymentHasParent(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("with parent reference", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
payment := models.PSPPayment{
|
|
ParentReference: "parent123",
|
|
Reference: "payment123",
|
|
}
|
|
|
|
hasParent := payment.HasParent()
|
|
|
|
// Then
|
|
assert.True(t, hasParent)
|
|
})
|
|
|
|
t.Run("without parent reference", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given
|
|
payment := models.PSPPayment{
|
|
Reference: "payment123",
|
|
}
|
|
|
|
hasParent := payment.HasParent()
|
|
|
|
// Then
|
|
assert.False(t, hasParent)
|
|
})
|
|
}
|