Fixes + stable gateway ids

This commit is contained in:
Stephan D
2026-02-18 20:38:08 +01:00
parent 4dc182bfa2
commit 770c7b9da9
119 changed files with 3000 additions and 734 deletions

View File

@@ -2,14 +2,6 @@ package quote_persistence_service
import "strconv"
func cloneBoolPtr(src *bool) *bool {
if src == nil {
return nil
}
value := *src
return &value
}
func itoa(value int) string {
return strconv.Itoa(value)
}

View File

@@ -9,9 +9,7 @@ import (
)
type StatusInput struct {
Kind quotationv2.QuoteKind
Lifecycle quotationv2.QuoteLifecycle
Executable *bool
State quotationv2.QuoteState
BlockReason quotationv2.QuoteBlockReason
}

View File

@@ -17,7 +17,6 @@ func TestPersistSingle(t *testing.T) {
svc := New()
store := &fakeQuotesStore{}
orgID := bson.NewObjectID()
trueValue := true
record, err := svc.Persist(context.Background(), store, PersistInput{
OrganizationID: orgID,
@@ -30,9 +29,7 @@ func TestPersistSingle(t *testing.T) {
QuoteRef: "quote-1",
},
Status: &StatusInput{
Kind: quotationv2.QuoteKind_QUOTE_KIND_EXECUTABLE,
Lifecycle: quotationv2.QuoteLifecycle_QUOTE_LIFECYCLE_ACTIVE,
Executable: &trueValue,
State: quotationv2.QuoteState_QUOTE_STATE_EXECUTABLE,
},
})
if err != nil {
@@ -50,11 +47,11 @@ func TestPersistSingle(t *testing.T) {
if store.created.StatusV2 == nil {
t.Fatalf("expected v2 status metadata")
}
if store.created.StatusV2.Kind != model.QuoteKindExecutable {
t.Fatalf("unexpected kind: %q", store.created.StatusV2.Kind)
if store.created.StatusV2.State != model.QuoteStateExecutable {
t.Fatalf("unexpected state: %q", store.created.StatusV2.State)
}
if store.created.StatusV2.Executable == nil || !*store.created.StatusV2.Executable {
t.Fatalf("expected executable=true in persisted status")
if store.created.StatusV2.BlockReason != model.QuoteBlockReasonUnspecified {
t.Fatalf("unexpected block_reason: %q", store.created.StatusV2.BlockReason)
}
}
@@ -79,13 +76,11 @@ func TestPersistBatch(t *testing.T) {
},
Statuses: []*StatusInput{
{
Kind: quotationv2.QuoteKind_QUOTE_KIND_EXECUTABLE,
Lifecycle: quotationv2.QuoteLifecycle_QUOTE_LIFECYCLE_ACTIVE,
State: quotationv2.QuoteState_QUOTE_STATE_BLOCKED,
BlockReason: quotationv2.QuoteBlockReason_QUOTE_BLOCK_REASON_ROUTE_UNAVAILABLE,
},
{
Kind: quotationv2.QuoteKind_QUOTE_KIND_INDICATIVE,
Lifecycle: quotationv2.QuoteLifecycle_QUOTE_LIFECYCLE_ACTIVE,
State: quotationv2.QuoteState_QUOTE_STATE_INDICATIVE,
},
},
})
@@ -122,13 +117,12 @@ func TestPersistValidation(t *testing.T) {
Intent: &model.PaymentIntent{Ref: "intent"},
Quote: &model.PaymentQuoteSnapshot{QuoteRef: "q"},
Status: &StatusInput{
Kind: quotationv2.QuoteKind_QUOTE_KIND_EXECUTABLE,
Lifecycle: quotationv2.QuoteLifecycle_QUOTE_LIFECYCLE_ACTIVE,
Executable: boolPtr(false),
State: quotationv2.QuoteState_QUOTE_STATE_BLOCKED,
BlockReason: quotationv2.QuoteBlockReason_QUOTE_BLOCK_REASON_UNSPECIFIED,
},
})
if !errors.Is(err, merrors.ErrInvalidArg) {
t.Fatalf("expected invalid argument for executable=false, got %v", err)
t.Fatalf("expected invalid argument for blocked without reason, got %v", err)
}
_, err = svc.Persist(context.Background(), store, PersistInput{
@@ -170,7 +164,3 @@ func (f *fakeQuotesStore) GetByRef(context.Context, bson.ObjectID, string) (*mod
func (f *fakeQuotesStore) GetByIdempotencyKey(context.Context, bson.ObjectID, string) (*model.PaymentQuoteRecord, error) {
return nil, quotestorage.ErrQuoteNotFound
}
func boolPtr(v bool) *bool {
return &v
}

View File

@@ -11,18 +11,19 @@ func mapStatusInput(input *StatusInput) (*model.QuoteStatusV2, error) {
return nil, merrors.InvalidArgument("status is required")
}
if input.Executable != nil && !*input.Executable {
return nil, merrors.InvalidArgument("status.executable must be true when set")
if input.State == quotationv2.QuoteState_QUOTE_STATE_UNSPECIFIED {
return nil, merrors.InvalidArgument("status.state is required")
}
if input.Executable != nil &&
input.BlockReason != quotationv2.QuoteBlockReason_QUOTE_BLOCK_REASON_UNSPECIFIED {
return nil, merrors.InvalidArgument("status.executable and status.block_reason are mutually exclusive")
if input.State == quotationv2.QuoteState_QUOTE_STATE_BLOCKED {
if input.BlockReason == quotationv2.QuoteBlockReason_QUOTE_BLOCK_REASON_UNSPECIFIED {
return nil, merrors.InvalidArgument("status.block_reason is required for blocked quote")
}
} else if input.BlockReason != quotationv2.QuoteBlockReason_QUOTE_BLOCK_REASON_UNSPECIFIED {
return nil, merrors.InvalidArgument("status.block_reason is only valid for blocked quote")
}
return &model.QuoteStatusV2{
Kind: mapQuoteKind(input.Kind),
Lifecycle: mapQuoteLifecycle(input.Lifecycle),
Executable: cloneBoolPtr(input.Executable),
State: mapQuoteState(input.State),
BlockReason: mapQuoteBlockReason(input.BlockReason),
}, nil
}
@@ -43,25 +44,18 @@ func mapStatusInputs(inputs []*StatusInput) ([]*model.QuoteStatusV2, error) {
return result, nil
}
func mapQuoteKind(kind quotationv2.QuoteKind) model.QuoteKind {
switch kind {
case quotationv2.QuoteKind_QUOTE_KIND_EXECUTABLE:
return model.QuoteKindExecutable
case quotationv2.QuoteKind_QUOTE_KIND_INDICATIVE:
return model.QuoteKindIndicative
func mapQuoteState(state quotationv2.QuoteState) model.QuoteState {
switch state {
case quotationv2.QuoteState_QUOTE_STATE_INDICATIVE:
return model.QuoteStateIndicative
case quotationv2.QuoteState_QUOTE_STATE_EXECUTABLE:
return model.QuoteStateExecutable
case quotationv2.QuoteState_QUOTE_STATE_BLOCKED:
return model.QuoteStateBlocked
case quotationv2.QuoteState_QUOTE_STATE_EXPIRED:
return model.QuoteStateExpired
default:
return model.QuoteKindUnspecified
}
}
func mapQuoteLifecycle(lifecycle quotationv2.QuoteLifecycle) model.QuoteLifecycle {
switch lifecycle {
case quotationv2.QuoteLifecycle_QUOTE_LIFECYCLE_ACTIVE:
return model.QuoteLifecycleActive
case quotationv2.QuoteLifecycle_QUOTE_LIFECYCLE_EXPIRED:
return model.QuoteLifecycleExpired
default:
return model.QuoteLifecycleUnspecified
return model.QuoteStateUnspecified
}
}