all the refactor
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"git.k6n.net/mats/go-cart-actor/pkg/order"
|
||||
)
|
||||
|
||||
func TestLifecycleIdempotencyRefund(t *testing.T) {
|
||||
s := testServer(t)
|
||||
s.provider = order.NewMockProvider()
|
||||
ctx := context.Background()
|
||||
|
||||
// Ingest an order to get it into Captured status
|
||||
body := []byte(`{
|
||||
"checkoutId":"K-1","idempotencyKey":"checkout-K-1","cartId":"cart-1",
|
||||
"currency":"SEK","country":"SE","customerEmail":"customer@example.com",
|
||||
"lines":[{"reference":"l1","sku":"l1","name":"Widget","quantity":1,"unitPrice":15000,"taxRate":25}],
|
||||
"payment":{"provider":"klarna","reference":"ref-123","amount":15000}
|
||||
}`)
|
||||
if err := ingestOrderFromQueue(ctx, s, body); err != nil {
|
||||
t.Fatalf("ingest: %v", err)
|
||||
}
|
||||
|
||||
orderID, ok := s.idem.Get("checkout-K-1")
|
||||
if !ok {
|
||||
t.Fatalf("expected order ID to be stored in idempotency cache")
|
||||
}
|
||||
|
||||
// ── First refund request (with idempotency key) ────────────────────────
|
||||
reqBody := []byte(`{"amount": 5000}`)
|
||||
req1 := httptest.NewRequest("POST", "/api/orders/"+order.OrderId(orderID).String()+"/refunds", bytes.NewReader(reqBody))
|
||||
req1.Header.Set("Idempotency-Key", "refund-key-123")
|
||||
req1.SetPathValue("id", order.OrderId(orderID).String())
|
||||
w1 := httptest.NewRecorder()
|
||||
|
||||
s.handleRefund(w1, req1)
|
||||
|
||||
if w1.Code != http.StatusOK {
|
||||
t.Fatalf("first refund status = %d, want 200", w1.Code)
|
||||
}
|
||||
|
||||
g1, err := s.applier.Get(ctx, orderID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if g1.RefundedAmount != 5000 {
|
||||
t.Fatalf("refunded amount after first call = %d, want 5000", g1.RefundedAmount)
|
||||
}
|
||||
|
||||
// ── Second refund request (with the same idempotency key) ──────────────
|
||||
req2 := httptest.NewRequest("POST", "/api/orders/"+order.OrderId(orderID).String()+"/refunds", bytes.NewReader(reqBody))
|
||||
req2.Header.Set("Idempotency-Key", "refund-key-123")
|
||||
req2.SetPathValue("id", order.OrderId(orderID).String())
|
||||
w2 := httptest.NewRecorder()
|
||||
|
||||
s.handleRefund(w2, req2)
|
||||
|
||||
if w2.Code != http.StatusOK {
|
||||
t.Fatalf("second refund status = %d, want 200", w2.Code)
|
||||
}
|
||||
|
||||
g2, err := s.applier.Get(ctx, orderID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// It should NOT have refunded another 5000 (total refunded remains 5000)
|
||||
if g2.RefundedAmount != 5000 {
|
||||
t.Fatalf("refunded amount after second call = %d, want 5000 (idempotency hit failed, did double refund)", g2.RefundedAmount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLifecycleWithoutIdempotencyRefund(t *testing.T) {
|
||||
s := testServer(t)
|
||||
s.provider = order.NewMockProvider()
|
||||
ctx := context.Background()
|
||||
|
||||
// Ingest an order to get it into Captured status
|
||||
body := []byte(`{
|
||||
"checkoutId":"K-1","idempotencyKey":"checkout-K-1","cartId":"cart-1",
|
||||
"currency":"SEK","country":"SE","customerEmail":"customer@example.com",
|
||||
"lines":[{"reference":"l1","sku":"l1","name":"Widget","quantity":1,"unitPrice":15000,"taxRate":25}],
|
||||
"payment":{"provider":"klarna","reference":"ref-123","amount":15000}
|
||||
}`)
|
||||
if err := ingestOrderFromQueue(ctx, s, body); err != nil {
|
||||
t.Fatalf("ingest: %v", err)
|
||||
}
|
||||
|
||||
orderID, ok := s.idem.Get("checkout-K-1")
|
||||
if !ok {
|
||||
t.Fatalf("expected order ID to be stored in idempotency cache")
|
||||
}
|
||||
|
||||
// First refund request (no idempotency key)
|
||||
reqBody := []byte(`{"amount": 5000}`)
|
||||
req1 := httptest.NewRequest("POST", "/api/orders/"+order.OrderId(orderID).String()+"/refunds", bytes.NewReader(reqBody))
|
||||
req1.SetPathValue("id", order.OrderId(orderID).String())
|
||||
w1 := httptest.NewRecorder()
|
||||
|
||||
s.handleRefund(w1, req1)
|
||||
|
||||
if w1.Code != http.StatusOK {
|
||||
t.Fatalf("first refund status = %d, want 200", w1.Code)
|
||||
}
|
||||
|
||||
// Second refund request (no idempotency key)
|
||||
req2 := httptest.NewRequest("POST", "/api/orders/"+order.OrderId(orderID).String()+"/refunds", bytes.NewReader(reqBody))
|
||||
req2.SetPathValue("id", order.OrderId(orderID).String())
|
||||
w2 := httptest.NewRecorder()
|
||||
|
||||
s.handleRefund(w2, req2)
|
||||
|
||||
if w2.Code != http.StatusOK {
|
||||
t.Fatalf("second refund status = %d, want 200", w2.Code)
|
||||
}
|
||||
|
||||
g2, err := s.applier.Get(ctx, orderID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Without idempotency, it SHOULD have refunded twice (total 10000)
|
||||
if g2.RefundedAmount != 10000 {
|
||||
t.Fatalf("refunded amount after second call without idempotency = %d, want 10000", g2.RefundedAmount)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user