Files
mats faa5330b81
Build and Publish / BuildAndDeployArm64 (push) Failing after 6s
Build and Publish / BuildAndDeployAmd64 (push) Failing after 5s
better ucp conformance
2026-06-25 09:35:27 +02:00

154 lines
4.4 KiB
Go

package ucp
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"time"
"git.k6n.net/mats/go-cart-actor/pkg/actor"
"git.k6n.net/mats/go-cart-actor/pkg/cart"
"git.k6n.net/mats/go-cart-actor/pkg/checkout"
checkoutMessages "git.k6n.net/mats/go-cart-actor/proto/checkout"
"google.golang.org/protobuf/proto"
)
type testCheckoutApplier struct {
grains map[uint64]*checkout.CheckoutGrain
}
func newTestCheckoutApplier() *testCheckoutApplier {
return &testCheckoutApplier{
grains: make(map[uint64]*checkout.CheckoutGrain),
}
}
func (a *testCheckoutApplier) Get(_ context.Context, id uint64) (*checkout.CheckoutGrain, error) {
g, ok := a.grains[id]
if !ok {
g = checkout.NewCheckoutGrain(id, 0, 0, time.UnixMilli(1000000), nil)
a.grains[id] = g
}
return g, nil
}
func (a *testCheckoutApplier) Apply(ctx context.Context, id uint64, msgs ...proto.Message) (*actor.MutationResult[checkout.CheckoutGrain], error) {
g, err := a.Get(ctx, id)
if err != nil {
return nil, err
}
for _, msg := range msgs {
switch m := msg.(type) {
case *checkoutMessages.InitializeCheckout:
g.CartId = cart.CartId(m.CartId)
}
}
return &actor.MutationResult[checkout.CheckoutGrain]{
Result: *g,
}, nil
}
func TestCheckoutHandler_CreateWithCartId(t *testing.T) {
// Start a mock Cart server.
mockCartServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.URL.Path, "/cart/byid/") {
t.Errorf("expected path to contain /cart/byid/, got %q", r.URL.Path)
}
// Return dummy cart state bytes.
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{}`))
}))
defer mockCartServer.Close()
os.Setenv("CART_INTERNAL_URL", mockCartServer.URL)
defer os.Unsetenv("CART_INTERNAL_URL")
applier := newTestCheckoutApplier()
handler := CheckoutHandler(applier)
// Create a dummy cart ID.
dummyCartId, _ := cart.NewCartId()
reqBody := fmt.Sprintf(`{"cartId": %q}`, dummyCartId.String())
req := httptest.NewRequest("POST", "/", strings.NewReader(reqBody))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Fatalf("expected 201 Created, got %d. Body: %s", rec.Code, rec.Body.String())
}
var resp CheckoutResponse
if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
t.Fatalf("failed to decode response: %v", err)
}
if resp.Id == "" {
t.Fatal("expected non-empty checkout session id")
}
if resp.CartId != dummyCartId.String() {
t.Fatalf("expected cartId %q, got %q", dummyCartId.String(), resp.CartId)
}
}
func TestCheckoutHandler_CreateWithLineItemsFallback(t *testing.T) {
// Start a mock Cart server that supports both creating a cart and getting it.
var createdCartId cart.CartId
mockCartServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" && strings.Contains(r.URL.Path, "/ucp/v1/carts/") {
var err error
createdCartId, err = cart.NewCartId()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
w.Write([]byte(fmt.Sprintf(`{"id": %q, "status": "active"}`, createdCartId.String())))
return
}
if r.Method == "GET" && strings.Contains(r.URL.Path, "/cart/byid/") {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{}`))
return
}
w.WriteHeader(http.StatusNotFound)
}))
defer mockCartServer.Close()
os.Setenv("CART_INTERNAL_URL", mockCartServer.URL)
defer os.Unsetenv("CART_INTERNAL_URL")
applier := newTestCheckoutApplier()
handler := CheckoutHandler(applier)
// POST with line_items and no cartId.
reqBody := `{"line_items": [{"sku": "SKU123", "quantity": 2}]}`
req := httptest.NewRequest("POST", "/", strings.NewReader(reqBody))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Fatalf("expected 201 Created, got %d. Body: %s", rec.Code, rec.Body.String())
}
var resp CheckoutResponse
if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
t.Fatalf("failed to decode response: %v", err)
}
if resp.Id == "" {
t.Fatal("expected non-empty checkout session id")
}
if resp.CartId != createdCartId.String() {
t.Fatalf("expected cartId %q to match created implicit cart %q", resp.CartId, createdCartId.String())
}
}