intent reference generation + propagation
This commit is contained in:
@@ -205,7 +205,7 @@ func (s *svc) resolveAndPlan(ctx context.Context, requestCtx *reqval.Ctx) (*qsna
|
||||
IntentRef: requestCtx.IntentRef,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, remapResolveError(err)
|
||||
}
|
||||
graph, err := s.planner.Compile(xplan.Input{
|
||||
IntentSnapshot: resolved.IntentSnapshot,
|
||||
@@ -269,6 +269,15 @@ func remapIdempotencyError(err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func remapResolveError(err error) error {
|
||||
switch {
|
||||
case errors.Is(err, qsnap.ErrIntentRefRequired), errors.Is(err, qsnap.ErrIntentRefNotFound):
|
||||
return merrors.InvalidArgument(err.Error())
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func mustFingerprint(idemSvc idem.Service, requestCtx *reqval.Ctx) string {
|
||||
if idemSvc == nil || requestCtx == nil {
|
||||
return ""
|
||||
|
||||
@@ -106,6 +106,27 @@ func TestExecutePayment_IdempotencyMismatch(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutePayment_BatchQuoteRequiresIntentRef(t *testing.T) {
|
||||
env := newTestEnv(t, func(_ string, req sexec.StepRequest) (*sexec.ExecuteOutput, error) {
|
||||
step := req.StepExecution
|
||||
step.State = agg.StepStateCompleted
|
||||
return &sexec.ExecuteOutput{StepExecution: step}, nil
|
||||
})
|
||||
env.quotes.Put(newExecutableBatchQuote(env.orgID, "quote-batch", []string{"intent-a", "intent-b"}, buildLedgerRoute()))
|
||||
|
||||
_, err := env.svc.ExecutePayment(context.Background(), &orchestrationv2.ExecutePaymentRequest{
|
||||
Meta: testMeta(env.orgID, "idem-batch"),
|
||||
QuotationRef: "quote-batch",
|
||||
ClientPaymentRef: "client-batch",
|
||||
})
|
||||
if !errors.Is(err, merrors.ErrInvalidArg) {
|
||||
t.Fatalf("expected invalid argument for missing intent_ref, got %v", err)
|
||||
}
|
||||
if got := err.Error(); !strings.Contains(got, "intent_ref is required for batch quotation") {
|
||||
t.Fatalf("unexpected error message: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutePayment_RetryThenSuccess(t *testing.T) {
|
||||
env := newTestEnv(t, func(_ string, req sexec.StepRequest) (*sexec.ExecuteOutput, error) {
|
||||
if req.StepExecution.Attempt == 1 {
|
||||
@@ -627,6 +648,39 @@ func newExecutableQuote(orgRef bson.ObjectID, quoteRef, intentRef string, route
|
||||
}
|
||||
}
|
||||
|
||||
func newExecutableBatchQuote(orgRef bson.ObjectID, quoteRef string, intentRefs []string, route *paymenttypes.QuoteRouteSpecification) *model.PaymentQuoteRecord {
|
||||
now := time.Now().UTC()
|
||||
items := make([]*model.PaymentQuoteItemV2, 0, len(intentRefs))
|
||||
for _, intentRef := range intentRefs {
|
||||
items = append(items, &model.PaymentQuoteItemV2{
|
||||
Intent: &model.PaymentIntent{
|
||||
Ref: intentRef,
|
||||
Kind: model.PaymentKindPayout,
|
||||
Source: testLedgerEndpoint("ledger-src"),
|
||||
Destination: testLedgerEndpoint("ledger-dst"),
|
||||
Amount: &paymenttypes.Money{Amount: "10", Currency: "USD"},
|
||||
SettlementCurrency: "USD",
|
||||
},
|
||||
Quote: &model.PaymentQuoteSnapshot{
|
||||
QuoteRef: quoteRef,
|
||||
DebitAmount: &paymenttypes.Money{Amount: "10", Currency: "USD"},
|
||||
Route: route,
|
||||
},
|
||||
Status: &model.QuoteStatusV2{State: model.QuoteStateExecutable},
|
||||
})
|
||||
}
|
||||
return &model.PaymentQuoteRecord{
|
||||
Base: modelBase(now),
|
||||
OrganizationBoundBase: pm.OrganizationBoundBase{
|
||||
OrganizationRef: orgRef,
|
||||
},
|
||||
QuoteRef: quoteRef,
|
||||
RequestShape: model.QuoteRequestShapeBatch,
|
||||
Items: items,
|
||||
ExpiresAt: now.Add(1 * time.Hour),
|
||||
}
|
||||
}
|
||||
|
||||
func buildLedgerRoute() *paymenttypes.QuoteRouteSpecification {
|
||||
return &paymenttypes.QuoteRouteSpecification{
|
||||
Hops: []*paymenttypes.QuoteRouteHop{
|
||||
|
||||
Reference in New Issue
Block a user