Files

297 lines
10 KiB
Go

package bankingbridge
import (
"context"
"encoding/json"
"errors"
"math/big"
"testing"
"time"
"github.com/hanzo-fi/go-libs/v5/pkg/types/pointer"
"github.com/hanzo-fi/payments/ee/plugins/bankingbridge/client"
"github.com/hanzo-fi/payments/pkg/domain/plugins"
"github.com/hanzo-fi/payments/pkg/domain/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
func (suite *PluginTestSuite) TestFetchNextPayments_Success() {
ctx := context.Background()
req := models.FetchNextPaymentsRequest{
PageSize: 2,
State: nil,
}
bookedAt := time.Now().Add(-time.Hour).Truncate(time.Millisecond).UTC()
importedAt1 := time.Now().Add(-time.Minute).Truncate(time.Millisecond).UTC()
importedAt2 := time.Now().Truncate(time.Millisecond).UTC()
trxs := []client.Transaction{
{ID: "someID!", AccountReference: "acc1", BookedAt: bookedAt, ImportedAt: importedAt1, AmountInMinors: int64(5432), Asset: "CAD"},
{ID: "someID!!", AccountReference: "acc2", BookedAt: bookedAt, ImportedAt: importedAt2, AmountInMinors: int64(5431), Asset: "KRW"},
}
newCursor := "newCursor"
suite.client.EXPECT().GetTransactions(gomock.Any(), "", "", req.PageSize).Return(trxs, true, newCursor, nil)
resp, err := suite.plugin.FetchNextPayments(ctx, req)
require.NoError(suite.T(), err)
assert.Len(suite.T(), resp.Payments, len(trxs))
assert.Equal(suite.T(), trxs[0].ID, resp.Payments[0].Reference)
assert.Equal(suite.T(), trxs[0].BookedAt, resp.Payments[0].CreatedAt)
require.NotNil(suite.T(), resp.Payments[0].Amount)
assert.Equal(suite.T(), trxs[0].AmountInMinors, resp.Payments[0].Amount.Int64())
assert.Equal(suite.T(), trxs[0].Asset, resp.Payments[0].Asset)
assert.Contains(suite.T(), string(resp.Payments[0].Raw), trxs[0].ID)
assert.True(suite.T(), resp.HasMore)
var state workflowState
err = json.Unmarshal(resp.NewState, &state)
require.NoError(suite.T(), err)
assert.Equal(suite.T(), newCursor, state.Cursor)
lastSeenImportedAt, err := time.Parse(ImportedAtLayout, state.LastSeenImportedAt)
require.NoError(suite.T(), err)
assert.Equal(suite.T(), importedAt2, lastSeenImportedAt)
}
func (suite *PluginTestSuite) TestFetchNextPayments_ClientError() {
ctx := context.Background()
req := models.FetchNextPaymentsRequest{
PageSize: 2,
State: nil,
}
expectedErr := errors.New("new err")
suite.client.EXPECT().GetTransactions(gomock.Any(), "", "", req.PageSize).Return(nil, false, "", expectedErr)
_, err := suite.plugin.FetchNextPayments(ctx, req)
require.Error(suite.T(), err)
assert.Equal(suite.T(), expectedErr, err)
}
func (suite *PluginTestSuite) TestFetchNextPayments_NotYetInstalled() {
ctx := context.Background()
req := models.FetchNextPaymentsRequest{
PageSize: 2,
State: nil,
}
suite.plugin.client = nil
_, err := suite.plugin.FetchNextPayments(ctx, req)
require.Error(suite.T(), err)
assert.Equal(suite.T(), plugins.ErrNotYetInstalled, err)
}
func (suite *PluginTestSuite) TestToPSPPayment() {
bookedAt := time.Now().Add(-time.Hour)
importedAt := time.Now().UTC()
rawMessage := json.RawMessage(`{"key":"value"}`)
tests := []struct {
name string
input client.Transaction
raw json.RawMessage
expected models.PSPPayment
}{
{
name: "incoming SEPA credit transfer",
input: client.Transaction{
ID: "123",
BookedAt: bookedAt,
AmountInMinors: 1000,
Asset: "USD",
BankTransactionCode: "PMNT.RCDT.ESCT",
AccountReference: "acc123",
BookingDate: "2023-10-01",
ValutaDate: "2023-10-02",
NumberOfTransactions: 1,
EntryReference: "entry1",
ServicerReference: "servicer1",
IsReversal: false,
IsBatch: false,
BatchMessageID: "batch1",
BatchPaymentInfoID: "batchInfo1",
ImportedAt: importedAt,
},
raw: rawMessage,
expected: models.PSPPayment{
Reference: "123",
CreatedAt: bookedAt,
Amount: big.NewInt(1000),
Asset: "USD",
Type: models.PAYMENT_TYPE_PAYIN,
Scheme: models.PAYMENT_SCHEME_SEPA_CREDIT,
Status: models.PAYMENT_STATUS_SUCCEEDED,
DestinationAccountReference: pointer.For("acc123"),
Raw: rawMessage,
Metadata: map[string]string{
MetadataPrefix + "bookingDate": "2023-10-01",
MetadataPrefix + "valueDate": "2023-10-02",
MetadataPrefix + "bankTransactionCode": "PMNT.RCDT.ESCT",
MetadataPrefix + "numberOfTransactions": "1",
MetadataPrefix + "entryReference": "entry1",
MetadataPrefix + "servicerReference": "servicer1",
MetadataPrefix + "isReversal": "false",
MetadataPrefix + "isBatch": "false",
MetadataPrefix + "batchMessageId": "batch1",
MetadataPrefix + "batchPaymentInfoId": "batchInfo1",
MetadataPrefix + "importedAt": importedAt.UTC().Format(ImportedAtLayout),
},
},
},
{
name: "outgoing SEPA credit transfer",
input: client.Transaction{
ID: "456",
BookedAt: bookedAt,
AmountInMinors: -500,
Asset: "EUR",
BankTransactionCode: "PMNT.ICDT.ESCT",
AccountReference: "acc456",
BookingDate: "2023-10-03",
ValutaDate: "2023-10-04",
NumberOfTransactions: 2,
EntryReference: "entry2",
ServicerReference: "servicer2",
IsReversal: false,
IsBatch: true,
BatchMessageID: "batch2",
BatchPaymentInfoID: "batchInfo2",
ImportedAt: importedAt,
},
raw: rawMessage,
expected: models.PSPPayment{
Reference: "456",
CreatedAt: bookedAt,
Amount: big.NewInt(500),
Asset: "EUR",
Type: models.PAYMENT_TYPE_PAYOUT,
Scheme: models.PAYMENT_SCHEME_SEPA_CREDIT,
Status: models.PAYMENT_STATUS_SUCCEEDED,
SourceAccountReference: pointer.For("acc456"),
Metadata: map[string]string{
MetadataPrefix + "bookingDate": "2023-10-03",
MetadataPrefix + "valueDate": "2023-10-04",
MetadataPrefix + "bankTransactionCode": "PMNT.ICDT.ESCT",
MetadataPrefix + "numberOfTransactions": "2",
MetadataPrefix + "entryReference": "entry2",
MetadataPrefix + "servicerReference": "servicer2",
MetadataPrefix + "isReversal": "false",
MetadataPrefix + "isBatch": "true",
MetadataPrefix + "batchMessageId": "batch2",
MetadataPrefix + "batchPaymentInfoId": "batchInfo2",
MetadataPrefix + "importedAt": importedAt.UTC().Format(ImportedAtLayout),
},
Raw: rawMessage,
},
},
{
name: "unknown payment type",
input: client.Transaction{
ID: "457",
BookedAt: bookedAt,
AmountInMinors: -503,
Asset: "EUR",
BankTransactionCode: "",
AccountReference: "acc456",
BookingDate: "2023-10-03",
ValutaDate: "2023-10-04",
NumberOfTransactions: 2,
EntryReference: "entry2",
ServicerReference: "servicer2",
IsReversal: false,
IsBatch: true,
BatchMessageID: "batch2",
BatchPaymentInfoID: "batchInfo2",
ImportedAt: importedAt,
},
raw: rawMessage,
expected: models.PSPPayment{
Reference: "457",
CreatedAt: bookedAt,
Amount: big.NewInt(503),
Asset: "EUR",
Type: models.PAYMENT_TYPE_PAYOUT,
Scheme: models.PAYMENT_SCHEME_UNKNOWN,
Status: models.PAYMENT_STATUS_SUCCEEDED,
SourceAccountReference: pointer.For("acc456"),
Metadata: map[string]string{
MetadataPrefix + "bookingDate": "2023-10-03",
MetadataPrefix + "valueDate": "2023-10-04",
MetadataPrefix + "bankTransactionCode": "",
MetadataPrefix + "numberOfTransactions": "2",
MetadataPrefix + "entryReference": "entry2",
MetadataPrefix + "servicerReference": "servicer2",
MetadataPrefix + "isReversal": "false",
MetadataPrefix + "isBatch": "true",
MetadataPrefix + "batchMessageId": "batch2",
MetadataPrefix + "batchPaymentInfoId": "batchInfo2",
MetadataPrefix + "importedAt": importedAt.UTC().Format(ImportedAtLayout),
},
Raw: rawMessage,
},
},
}
for _, tt := range tests {
suite.T().Run(tt.name, func(t *testing.T) {
result := ToPSPPayment(tt.input, tt.raw)
assert.Equal(t, tt.expected, result)
})
}
}
func (suite *PluginTestSuite) TestMetadata_EndToEndIds() {
detail := json.RawMessage(`{"id":"F4kRqwTa6x3EK2or1k5YCd5sULm","rawDetail":{"references":{"messageId":"EOL/20260305163140/767407","endToEndId":"260305KREDBEBB0000900001000003","paymentInformationId":"260305KREDBEBB0000900001"}}}`)
trx := client.Transaction{
Details: []json.RawMessage{detail},
}
m := metadata(trx)
assert.Equal(suite.T(), "260305KREDBEBB0000900001000003", m[MetadataPrefix+"endToEndId"])
}
func (suite *PluginTestSuite) TestMetadata_EndToEndId_MultipleDetails_UsesFirst() {
details := []json.RawMessage{
json.RawMessage(`{"rawDetail":{"references":{"endToEndId":"ID-ONE"}}}`),
json.RawMessage(`{"rawDetail":{"references":{"endToEndId":"ID-TWO"}}}`),
}
trx := client.Transaction{
Details: details,
}
m := metadata(trx)
assert.Equal(suite.T(), "ID-ONE", m[MetadataPrefix+"endToEndId"])
}
func (suite *PluginTestSuite) TestMetadata_RemittanceInfo_Unstructured() {
detail := json.RawMessage(`{"rawDetail":{"remittanceInfo":{"unstructured":["Invoice 1234","Order 5678"]}}}`)
trx := client.Transaction{
Details: []json.RawMessage{detail},
}
m := metadata(trx)
assert.Equal(suite.T(), "Invoice 1234 Order 5678", m[MetadataPrefix+"remittanceInfo"])
}
func (suite *PluginTestSuite) TestMetadata_RemittanceInfo_Structured() {
detail := json.RawMessage(`{"rawDetail":{"remittanceInfo":{"structured":[{"creditorReferenceInfo":{"reference":"RF18539007547034"}}]}}}`)
trx := client.Transaction{
Details: []json.RawMessage{detail},
}
m := metadata(trx)
assert.Equal(suite.T(), "RF18539007547034", m[MetadataPrefix+"remittanceInfo"])
}