192 lines
7.1 KiB
Go
192 lines
7.1 KiB
Go
package paymentapiimp
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/tech/sendico/pkg/auth"
|
|
"github.com/tech/sendico/pkg/mlogger"
|
|
"github.com/tech/sendico/pkg/model"
|
|
"github.com/tech/sendico/pkg/mservice"
|
|
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
|
"github.com/tech/sendico/server/interface/api/sresponse"
|
|
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
|
"go.mongodb.org/mongo-driver/v2/bson"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestInitiatePaymentsByQuote_ExecutesBatchPayment(t *testing.T) {
|
|
orgRef := bson.NewObjectID()
|
|
exec := &fakeExecutionClientForBatch{}
|
|
api := newBatchAPI(exec)
|
|
|
|
body := `{"idempotencyKey":"idem-batch","quoteRef":"quote-1"}`
|
|
rr := invokeInitiatePaymentsByQuote(t, api, orgRef, body)
|
|
if got, want := rr.Code, http.StatusOK; got != want {
|
|
t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String())
|
|
}
|
|
|
|
if got, want := len(exec.executeBatchReqs), 1; got != want {
|
|
t.Fatalf("execute batch calls mismatch: got=%d want=%d", got, want)
|
|
}
|
|
if got := len(exec.executeReqs); got != 0 {
|
|
t.Fatalf("expected no execute calls, got=%d", got)
|
|
}
|
|
if got, want := exec.executeBatchReqs[0].GetQuotationRef(), "quote-1"; got != want {
|
|
t.Fatalf("quotation_ref mismatch: got=%q want=%q", got, want)
|
|
}
|
|
if got, want := exec.executeBatchReqs[0].GetMeta().GetTrace().GetIdempotencyKey(), "idem-batch"; got != want {
|
|
t.Fatalf("idempotency mismatch: got=%q want=%q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestInitiatePaymentsByQuote_ForwardsClientPaymentRef(t *testing.T) {
|
|
orgRef := bson.NewObjectID()
|
|
exec := &fakeExecutionClientForBatch{}
|
|
api := newBatchAPI(exec)
|
|
|
|
body := `{"idempotencyKey":"idem-batch","quoteRef":"quote-1","clientPaymentRef":"client-ref-1"}`
|
|
rr := invokeInitiatePaymentsByQuote(t, api, orgRef, body)
|
|
if got, want := rr.Code, http.StatusOK; got != want {
|
|
t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String())
|
|
}
|
|
|
|
if got, want := len(exec.executeBatchReqs), 1; got != want {
|
|
t.Fatalf("execute batch calls mismatch: got=%d want=%d", got, want)
|
|
}
|
|
if got, want := exec.executeBatchReqs[0].GetClientPaymentRef(), "client-ref-1"; got != want {
|
|
t.Fatalf("client_payment_ref mismatch: got=%q want=%q", got, want)
|
|
}
|
|
if got := len(exec.executeReqs); got != 0 {
|
|
t.Fatalf("expected no execute calls, got=%d", got)
|
|
}
|
|
}
|
|
|
|
func TestInitiatePaymentsByQuote_DoesNotForwardLegacyClientPaymentRefFromMetadata(t *testing.T) {
|
|
orgRef := bson.NewObjectID()
|
|
exec := &fakeExecutionClientForBatch{}
|
|
api := newBatchAPI(exec)
|
|
|
|
body := `{"idempotencyKey":"idem-batch","quoteRef":"quote-1","metadata":{"client_payment_ref":"legacy-client-ref"}}`
|
|
rr := invokeInitiatePaymentsByQuote(t, api, orgRef, body)
|
|
if got, want := rr.Code, http.StatusOK; got != want {
|
|
t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String())
|
|
}
|
|
|
|
if got, want := len(exec.executeBatchReqs), 1; got != want {
|
|
t.Fatalf("execute batch calls mismatch: got=%d want=%d", got, want)
|
|
}
|
|
if got := exec.executeBatchReqs[0].GetClientPaymentRef(); got != "" {
|
|
t.Fatalf("expected empty client_payment_ref, got=%q", got)
|
|
}
|
|
}
|
|
|
|
func TestInitiatePaymentsByQuote_RejectsDeprecatedIntentRefField(t *testing.T) {
|
|
orgRef := bson.NewObjectID()
|
|
exec := &fakeExecutionClientForBatch{}
|
|
api := newBatchAPI(exec)
|
|
|
|
body := `{"idempotencyKey":"idem-batch","quoteRef":"quote-1","intentRef":"intent-legacy"}`
|
|
rr := invokeInitiatePaymentsByQuote(t, api, orgRef, body)
|
|
if got, want := rr.Code, http.StatusBadRequest; got != want {
|
|
t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String())
|
|
}
|
|
if got := len(exec.executeReqs); got != 0 {
|
|
t.Fatalf("expected no execute calls, got=%d", got)
|
|
}
|
|
}
|
|
|
|
func TestInitiatePaymentsByQuote_RejectsDeprecatedIntentRefsField(t *testing.T) {
|
|
orgRef := bson.NewObjectID()
|
|
exec := &fakeExecutionClientForBatch{}
|
|
api := newBatchAPI(exec)
|
|
|
|
body := `{"idempotencyKey":"idem-batch","quoteRef":"quote-1","intentRefs":["intent-a","intent-b"]}`
|
|
rr := invokeInitiatePaymentsByQuote(t, api, orgRef, body)
|
|
if got, want := rr.Code, http.StatusBadRequest; got != want {
|
|
t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String())
|
|
}
|
|
if got := len(exec.executeReqs); got != 0 {
|
|
t.Fatalf("expected no execute calls, got=%d", got)
|
|
}
|
|
}
|
|
|
|
func newBatchAPI(exec executionClient) *PaymentAPI {
|
|
return &PaymentAPI{
|
|
logger: mlogger.Logger(zap.NewNop()),
|
|
execution: exec,
|
|
enf: fakeEnforcerForBatch{allowed: true},
|
|
oph: mutil.CreatePH(mservice.Organizations),
|
|
permissionRef: bson.NewObjectID(),
|
|
}
|
|
}
|
|
|
|
func invokeInitiatePaymentsByQuote(t *testing.T, api *PaymentAPI, orgRef bson.ObjectID, body string) *httptest.ResponseRecorder {
|
|
t.Helper()
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/by-multiquote", bytes.NewBufferString(body))
|
|
routeCtx := chi.NewRouteContext()
|
|
routeCtx.URLParams.Add("organizations_ref", orgRef.Hex())
|
|
req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, routeCtx))
|
|
|
|
rr := httptest.NewRecorder()
|
|
handler := api.initiatePaymentsByQuote(req, &model.Account{}, &sresponse.TokenData{
|
|
Token: "token",
|
|
Expiration: time.Now().UTC().Add(time.Hour),
|
|
})
|
|
handler.ServeHTTP(rr, req)
|
|
return rr
|
|
}
|
|
|
|
type fakeExecutionClientForBatch struct {
|
|
executeReqs []*orchestrationv2.ExecutePaymentRequest
|
|
executeBatchReqs []*orchestrationv2.ExecuteBatchPaymentRequest
|
|
}
|
|
|
|
func (f *fakeExecutionClientForBatch) ExecutePayment(_ context.Context, req *orchestrationv2.ExecutePaymentRequest) (*orchestrationv2.ExecutePaymentResponse, error) {
|
|
f.executeReqs = append(f.executeReqs, req)
|
|
return &orchestrationv2.ExecutePaymentResponse{
|
|
Payment: &orchestrationv2.Payment{PaymentRef: bson.NewObjectID().Hex()},
|
|
}, nil
|
|
}
|
|
|
|
func (f *fakeExecutionClientForBatch) ExecuteBatchPayment(_ context.Context, req *orchestrationv2.ExecuteBatchPaymentRequest) (*orchestrationv2.ExecuteBatchPaymentResponse, error) {
|
|
f.executeBatchReqs = append(f.executeBatchReqs, req)
|
|
return &orchestrationv2.ExecuteBatchPaymentResponse{
|
|
Payments: []*orchestrationv2.Payment{{PaymentRef: bson.NewObjectID().Hex()}},
|
|
}, nil
|
|
}
|
|
|
|
func (*fakeExecutionClientForBatch) ListPayments(context.Context, *orchestrationv2.ListPaymentsRequest) (*orchestrationv2.ListPaymentsResponse, error) {
|
|
return &orchestrationv2.ListPaymentsResponse{}, nil
|
|
}
|
|
|
|
func (*fakeExecutionClientForBatch) Close() error { return nil }
|
|
|
|
type fakeEnforcerForBatch struct {
|
|
allowed bool
|
|
}
|
|
|
|
func (f fakeEnforcerForBatch) Enforce(context.Context, bson.ObjectID, bson.ObjectID, bson.ObjectID, bson.ObjectID, model.Action) (bool, error) {
|
|
return f.allowed, nil
|
|
}
|
|
|
|
func (fakeEnforcerForBatch) EnforceBatch(context.Context, []model.PermissionBoundStorable, bson.ObjectID, model.Action) (map[bson.ObjectID]bool, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (fakeEnforcerForBatch) GetRoles(context.Context, bson.ObjectID, bson.ObjectID) ([]model.Role, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (fakeEnforcerForBatch) GetPermissions(context.Context, bson.ObjectID, bson.ObjectID) ([]model.Role, []model.Permission, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
var _ auth.Enforcer = (*fakeEnforcerForBatch)(nil)
|