Files

108 lines
3.7 KiB
Go

package routable
import (
"errors"
"math/big"
"net/http"
"time"
"github.com/hanzo-fi/go-libs/v5/pkg/observe/log"
"github.com/hanzo-fi/payments/ee/plugins/routable/client"
"github.com/hanzo-fi/payments/pkg/domain/plugins"
"github.com/hanzo-fi/payments/pkg/domain/models"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"go.uber.org/mock/gomock"
)
var _ = Describe("Routable createTransfer", func() {
var (
ctrl *gomock.Controller
mock *client.MockClient
plg *Plugin
logger = logging.NewDefaultLogger(GinkgoWriter, true, false, false)
)
BeforeEach(func() {
ctrl = gomock.NewController(GinkgoT())
mock = client.NewMockClient(ctrl)
plg = &Plugin{
Plugin: plugins.NewBasePlugin(),
name: "routable",
logger: logger,
client: mock,
config: Config{ActingTeamMember: "tm_default"},
}
})
AfterEach(func() {
ctrl.Finish()
})
pi := func() models.PSPPaymentInitiation {
return models.PSPPaymentInitiation{
Reference: "pi_2",
CreatedAt: time.Now().UTC(),
Amount: big.NewInt(5000), // 50.00 USD
Asset: "USD/2",
SourceAccount: &models.PSPAccount{Reference: "acc_1"},
DestinationAccount: &models.PSPAccount{Reference: "co_1"},
}
}
It("marks the response Payment as TRANSFER even though the rail is a Routable payable", func(ctx SpecContext) {
mock.EXPECT().CreatePayable(gomock.Any(), gomock.Any()).Return(
&client.Payable{ID: "pa_t", Status: "completed", Amount: "50.00", CurrencyCode: "USD", CreatedAt: time.Now().UTC()},
http.StatusCreated,
nil,
)
resp, err := plg.createTransfer(ctx, models.CreateTransferRequest{PaymentInitiation: pi()})
Expect(err).To(BeNil())
Expect(resp.Payment).NotTo(BeNil())
Expect(resp.Payment.Type).To(Equal(models.PAYMENT_TYPE_TRANSFER))
})
It("returns PollingTransferID for sync 201 responses with a non-terminal status", func(ctx SpecContext) {
mock.EXPECT().CreatePayable(gomock.Any(), gomock.Any()).Return(
&client.Payable{ID: "pa_t2", Status: "pending", Amount: "50.00", CurrencyCode: "USD", CreatedAt: time.Now().UTC()},
http.StatusCreated,
nil,
)
resp, err := plg.createTransfer(ctx, models.CreateTransferRequest{PaymentInitiation: pi()})
Expect(err).To(BeNil())
Expect(resp.Payment).To(BeNil())
Expect(resp.PollingTransferID).NotTo(BeNil())
Expect(*resp.PollingTransferID).To(Equal("pa_t2"))
})
// Same ErrInvalidRequest wrapping contract as createPayout (shared
// initiatePayable choke point).
It("wraps validation errors with ErrInvalidRequest on createTransfer", func(ctx SpecContext) {
bad := pi()
bad.SourceAccount = nil
_, err := plg.createTransfer(ctx, models.CreateTransferRequest{PaymentInitiation: bad})
Expect(errors.Is(err, models.ErrInvalidRequest)).To(BeTrue())
bad = pi()
bad.DestinationAccount = nil
_, err = plg.createTransfer(ctx, models.CreateTransferRequest{PaymentInitiation: bad})
Expect(errors.Is(err, models.ErrInvalidRequest)).To(BeTrue())
})
// Async 202 path: Routable echoes only {id}. The plugin must return
// PollingTransferID without trying to map the half-empty payable
// (which would error out on the missing currency / amount).
It("returns PollingTransferID for async 202 responses with no mapping attempted", func(ctx SpecContext) {
mock.EXPECT().CreatePayable(gomock.Any(), gomock.Any()).Return(
&client.Payable{ID: "pa_t3"}, // 202 body: just {id}, no amount/currency
http.StatusAccepted,
nil,
)
resp, err := plg.createTransfer(ctx, models.CreateTransferRequest{PaymentInitiation: pi()})
Expect(err).To(BeNil(), "must NOT error on the missing currency in a 202 body")
Expect(resp.Payment).To(BeNil())
Expect(resp.PollingTransferID).NotTo(BeNil())
Expect(*resp.PollingTransferID).To(Equal("pa_t3"))
})
})