gRPC error translation: invalid argument support

This commit is contained in:
Stephan D
2026-02-26 16:59:09 +01:00
parent b4b5616de0
commit 0f95f898a8
8 changed files with 99 additions and 66 deletions

View File

@@ -72,7 +72,7 @@ func (s *Service) Register(router routers.GRPC) error {
return nil
}
return router.Register(func(reg grpc.ServiceRegistrar) {
orchestrationv2.RegisterPaymentOrchestratorServiceServer(reg, newV2GRPCServer(s.v2))
orchestrationv2.RegisterPaymentOrchestratorServiceServer(reg, newV2GRPCServer(s.v2, s.logger))
})
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/psvc"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/sexec"
"github.com/tech/sendico/payments/storage"
"github.com/tech/sendico/pkg/api/routers/gsresponse"
"github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/mservice"
@@ -145,21 +146,40 @@ func buildPaymentRepositoryV2(repo storage.Repository, logger mlogger.Logger) (p
type v2GRPCServer struct {
orchestrationv2.UnimplementedPaymentOrchestratorServiceServer
svc psvc.Service
svc psvc.Service
logger mlogger.Logger
}
func newV2GRPCServer(svc psvc.Service) *v2GRPCServer {
return &v2GRPCServer{svc: svc}
func newV2GRPCServer(svc psvc.Service, logger mlogger.Logger) *v2GRPCServer {
if logger == nil {
logger = zap.NewNop()
}
return &v2GRPCServer{
svc: svc,
logger: logger.Named("grpc"),
}
}
func (s *v2GRPCServer) ExecutePayment(ctx context.Context, req *orchestrationv2.ExecutePaymentRequest) (*orchestrationv2.ExecutePaymentResponse, error) {
return s.svc.ExecutePayment(ctx, req)
resp, err := s.svc.ExecutePayment(ctx, req)
if err != nil {
return gsresponse.Execute(ctx, gsresponse.Auto[orchestrationv2.ExecutePaymentResponse](s.logger, mservice.PaymentOrchestrator, err))
}
return resp, nil
}
func (s *v2GRPCServer) GetPayment(ctx context.Context, req *orchestrationv2.GetPaymentRequest) (*orchestrationv2.GetPaymentResponse, error) {
return s.svc.GetPayment(ctx, req)
resp, err := s.svc.GetPayment(ctx, req)
if err != nil {
return gsresponse.Execute(ctx, gsresponse.Auto[orchestrationv2.GetPaymentResponse](s.logger, mservice.PaymentOrchestrator, err))
}
return resp, nil
}
func (s *v2GRPCServer) ListPayments(ctx context.Context, req *orchestrationv2.ListPaymentsRequest) (*orchestrationv2.ListPaymentsResponse, error) {
return s.svc.ListPayments(ctx, req)
resp, err := s.svc.ListPayments(ctx, req)
if err != nil {
return gsresponse.Execute(ctx, gsresponse.Auto[orchestrationv2.ListPaymentsResponse](s.logger, mservice.PaymentOrchestrator, err))
}
return resp, nil
}

View File

@@ -5,75 +5,47 @@ import (
"strings"
"testing"
ledgerclient "github.com/tech/sendico/ledger/client"
"github.com/tech/sendico/payments/storage"
quotestorage "github.com/tech/sendico/payments/storage/quote"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/psvc"
"github.com/tech/sendico/pkg/merrors"
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func TestNewOrchestrationV2Service_FailsWhenLedgerClientMissing(t *testing.T) {
svc, repo, err := newOrchestrationV2Service(zap.NewNop(), fakeStorageRepo{}, v2RuntimeDeps{})
func TestV2GRPCServerExecutePayment_MapsInvalidArgument(t *testing.T) {
srv := newV2GRPCServer(fakeV2Service{
executeErr: merrors.InvalidArgument("intent_ref is required for batch quotation"),
}, zap.NewNop())
_, err := srv.ExecutePayment(context.Background(), &orchestrationv2.ExecutePaymentRequest{})
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "ledger client is required") {
t.Fatalf("unexpected error: %v", err)
if got, want := status.Code(err), codes.InvalidArgument; got != want {
t.Fatalf("unexpected grpc status code: got=%s want=%s", got, want)
}
if svc != nil {
t.Fatal("expected nil service")
}
if repo != nil {
t.Fatal("expected nil payment repo")
if got := status.Convert(err).Message(); !strings.Contains(got, "intent_ref is required for batch quotation") {
t.Fatalf("unexpected grpc status message: %q", got)
}
}
func TestNewOrchestrationV2Service_FailsWhenLedgerClientUnavailable(t *testing.T) {
ledger := unavailableLedgerClient{Fake: &ledgerclient.Fake{}}
svc, repo, err := newOrchestrationV2Service(zap.NewNop(), fakeStorageRepo{}, v2RuntimeDeps{
LedgerClient: ledger,
})
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "ledger client is unavailable") {
t.Fatalf("unexpected error: %v", err)
}
if svc != nil {
t.Fatal("expected nil service")
}
if repo != nil {
t.Fatal("expected nil payment repo")
}
type fakeV2Service struct {
executeErr error
}
type unavailableLedgerClient struct {
*ledgerclient.Fake
func (f fakeV2Service) ExecutePayment(context.Context, *orchestrationv2.ExecutePaymentRequest) (*orchestrationv2.ExecutePaymentResponse, error) {
return nil, f.executeErr
}
func (u unavailableLedgerClient) Available() bool {
return false
func (fakeV2Service) GetPayment(context.Context, *orchestrationv2.GetPaymentRequest) (*orchestrationv2.GetPaymentResponse, error) {
return &orchestrationv2.GetPaymentResponse{}, nil
}
type fakeStorageRepo struct{}
func (fakeStorageRepo) Ping(context.Context) error {
return nil
func (fakeV2Service) ListPayments(context.Context, *orchestrationv2.ListPaymentsRequest) (*orchestrationv2.ListPaymentsResponse, error) {
return &orchestrationv2.ListPaymentsResponse{}, nil
}
func (fakeStorageRepo) Payments() storage.PaymentsStore {
return nil
func (fakeV2Service) ReconcileExternal(context.Context, psvc.ReconcileExternalInput) (*psvc.ReconcileExternalOutput, error) {
return &psvc.ReconcileExternalOutput{}, nil
}
func (fakeStorageRepo) PaymentMethods() storage.PaymentMethodsStore {
return nil
}
func (fakeStorageRepo) Quotes() quotestorage.QuotesStore {
return nil
}
func (fakeStorageRepo) Routes() storage.RoutesStore {
return nil
}
var _ storage.Repository = fakeStorageRepo{}