mirror of
https://github.com/hanzo-fi/payments.git
synced 2026-10-30 20:31:20 +00:00
142 lines
5.5 KiB
Go
142 lines
5.5 KiB
Go
package routable
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"testing"
|
|
|
|
"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/httpwrapper"
|
|
"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"
|
|
)
|
|
|
|
func TestPlugin(t *testing.T) {
|
|
RegisterFailHandler(Fail)
|
|
RunSpecs(t, "Routable Plugin Suite")
|
|
}
|
|
|
|
var _ = Describe("Routable Plugin", func() {
|
|
var (
|
|
plg *Plugin
|
|
logger = logging.NewDefaultLogger(GinkgoWriter, true, false, false)
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
plg = &Plugin{Plugin: plugins.NewBasePlugin()}
|
|
})
|
|
|
|
Context("config validation", func() {
|
|
It("rejects missing apiKey", func() {
|
|
_, err := New("routable", logger, json.RawMessage(`{"actingTeamMember":"tm"}`))
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(ContainSubstring("APIKey"))
|
|
})
|
|
It("accepts a config without actingTeamMember (it's optional)", func() {
|
|
p, err := New("routable", logger, json.RawMessage(`{"apiKey":"key"}`))
|
|
Expect(err).To(BeNil())
|
|
Expect(p.config.ActingTeamMember).To(BeEmpty())
|
|
})
|
|
It("falls back to the default endpoint when omitted", func() {
|
|
p, err := New("routable", logger, json.RawMessage(`{"apiKey":"key"}`))
|
|
Expect(err).To(BeNil())
|
|
Expect(p.config.resolvedEndpoint()).To(Equal("https://api.routable.com"))
|
|
})
|
|
})
|
|
|
|
Context("install", func() {
|
|
var (
|
|
ctrl *gomock.Controller
|
|
mock *client.MockClient
|
|
)
|
|
BeforeEach(func() {
|
|
ctrl = gomock.NewController(GinkgoT())
|
|
mock = client.NewMockClient(ctrl)
|
|
plg = &Plugin{Plugin: plugins.NewBasePlugin(), name: "routable", logger: logger, client: mock, config: Config{}}
|
|
})
|
|
AfterEach(func() {
|
|
ctrl.Finish()
|
|
})
|
|
|
|
It("returns the documented workflow when the credential probe succeeds", func() {
|
|
mock.EXPECT().ListAccounts(gomock.Any(), 1, 1).Return(&client.ListAccountsResponse{}, nil)
|
|
res, err := plg.Install(context.Background(), models.InstallRequest{})
|
|
Expect(err).To(BeNil())
|
|
Expect(res.Workflow).To(Equal(workflow()))
|
|
})
|
|
|
|
// Fail-fast contract for ops: a bad API key must NOT make it past
|
|
// install. httpwrapper maps 401/403 to ErrStatusCodeClientError,
|
|
// which the activity translator already classifies as a
|
|
// non-retryable INVALID_ARGUMENT error so the engine surfaces it
|
|
// as an install failure instead of looping forever.
|
|
It("surfaces a credential failure (401/403) as an install error", func() {
|
|
mock.EXPECT().ListAccounts(gomock.Any(), 1, 1).Return(nil, httpwrapper.ErrStatusCodeClientError)
|
|
_, err := plg.Install(context.Background(), models.InstallRequest{})
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(errors.Is(err, httpwrapper.ErrStatusCodeClientError)).To(BeTrue())
|
|
Expect(err.Error()).To(ContainSubstring("verifying routable credentials"))
|
|
})
|
|
})
|
|
|
|
Context("uninstall", func() {
|
|
It("returns an empty response", func() {
|
|
_, err := plg.Uninstall(context.Background(), models.UninstallRequest{})
|
|
Expect(err).To(BeNil())
|
|
})
|
|
})
|
|
|
|
Context("calls before install", func() {
|
|
It("returns ErrNotYetInstalled for FetchNextAccounts", func() {
|
|
_, err := plg.FetchNextAccounts(context.Background(), models.FetchNextAccountsRequest{})
|
|
Expect(err).To(MatchError(plugins.ErrNotYetInstalled))
|
|
})
|
|
It("returns ErrNotYetInstalled for FetchNextBalances", func() {
|
|
_, err := plg.FetchNextBalances(context.Background(), models.FetchNextBalancesRequest{})
|
|
Expect(err).To(MatchError(plugins.ErrNotYetInstalled))
|
|
})
|
|
It("returns ErrNotYetInstalled for FetchNextExternalAccounts", func() {
|
|
_, err := plg.FetchNextExternalAccounts(context.Background(), models.FetchNextExternalAccountsRequest{})
|
|
Expect(err).To(MatchError(plugins.ErrNotYetInstalled))
|
|
})
|
|
It("returns ErrNotYetInstalled for FetchNextPayments", func() {
|
|
_, err := plg.FetchNextPayments(context.Background(), models.FetchNextPaymentsRequest{})
|
|
Expect(err).To(MatchError(plugins.ErrNotYetInstalled))
|
|
})
|
|
It("returns ErrNotYetInstalled for CreateTransfer", func() {
|
|
_, err := plg.CreateTransfer(context.Background(), models.CreateTransferRequest{})
|
|
Expect(err).To(MatchError(plugins.ErrNotYetInstalled))
|
|
})
|
|
It("returns ErrNotYetInstalled for CreatePayout", func() {
|
|
_, err := plg.CreatePayout(context.Background(), models.CreatePayoutRequest{})
|
|
Expect(err).To(MatchError(plugins.ErrNotYetInstalled))
|
|
})
|
|
It("returns ErrNotYetInstalled for PollTransferStatus", func() {
|
|
_, err := plg.PollTransferStatus(context.Background(), models.PollTransferStatusRequest{})
|
|
Expect(err).To(MatchError(plugins.ErrNotYetInstalled))
|
|
})
|
|
It("returns ErrNotYetInstalled for PollPayoutStatus", func() {
|
|
_, err := plg.PollPayoutStatus(context.Background(), models.PollPayoutStatusRequest{})
|
|
Expect(err).To(MatchError(plugins.ErrNotYetInstalled))
|
|
})
|
|
})
|
|
|
|
Context("base plugin inheritance", func() {
|
|
It("returns ErrNotImplemented for unimplemented methods", func() {
|
|
_, err := plg.CreateBankAccount(context.Background(), models.CreateBankAccountRequest{})
|
|
Expect(err).To(MatchError(plugins.ErrNotImplemented))
|
|
_, err = plg.CreateWebhooks(context.Background(), models.CreateWebhooksRequest{})
|
|
Expect(err).To(MatchError(plugins.ErrNotImplemented))
|
|
_, err = plg.TranslateWebhook(context.Background(), models.TranslateWebhookRequest{})
|
|
Expect(err).To(MatchError(plugins.ErrNotImplemented))
|
|
_, err = plg.ReversePayout(context.Background(), models.ReversePayoutRequest{})
|
|
Expect(err).To(MatchError(plugins.ErrNotImplemented))
|
|
})
|
|
})
|
|
})
|