payit/internal/web/handlers_test.go
Ruidy bf721dc130
feat(web): add logging middleware and refactor handlers
Introduced LoggerMiddleware for HTTP request logging. Refactored handler
methods to return http.HandlerFunc for improved composability. Updated
route registration and tests to use new handler signatures.
2025-09-28 19:43:30 +02:00

127 lines
3.5 KiB
Go

package web
import (
"bytes"
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"
"github.com/rjNemo/payit/internal/payments"
)
type fakeCheckoutService struct {
result payments.CheckoutSessionResult
err error
req payments.CheckoutSessionRequest
}
func (f *fakeCheckoutService) CreateSession(ctx context.Context, req payments.CheckoutSessionRequest) (payments.CheckoutSessionResult, error) {
f.req = req
if f.err != nil {
return payments.CheckoutSessionResult{}, f.err
}
return f.result, nil
}
func TestCreateCheckoutSessionSuccess(t *testing.T) {
handler := &Handler{
checkout: &fakeCheckoutService{
result: payments.CheckoutSessionResult{ID: "cs_test_1", URL: "https://stripe.test/checkout"},
},
}
body, _ := json.Marshal(map[string]int{"quantity": 2})
req := httptest.NewRequest(http.MethodPost, "/api/checkout", bytes.NewReader(body))
rec := httptest.NewRecorder()
handler.createCheckoutSession()(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
if ct := rec.Header().Get("Content-Type"); ct != "application/json" {
t.Fatalf("expected json content type, got %s", ct)
}
var payload payments.CheckoutSessionResult
if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil {
t.Fatalf("expected valid json response: %v", err)
}
if payload.ID != "cs_test_1" || payload.URL == "" {
t.Fatalf("unexpected payload: %#v", payload)
}
svc := handler.checkout.(*fakeCheckoutService)
if svc.req.Quantity != 2 {
t.Fatalf("expected quantity 2, got %d", svc.req.Quantity)
}
}
func TestCreateCheckoutSessionDefaultsQuantity(t *testing.T) {
fakeSvc := &fakeCheckoutService{
result: payments.CheckoutSessionResult{ID: "cs_test_1"},
}
handler := &Handler{checkout: fakeSvc}
req := httptest.NewRequest(http.MethodPost, "/api/checkout", http.NoBody)
rec := httptest.NewRecorder()
handler.createCheckoutSession()(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
if fakeSvc.req.Quantity != 0 {
t.Fatalf("expected zero quantity in request, got %d", fakeSvc.req.Quantity)
}
}
func TestCreateCheckoutSessionRejectsInvalidJSON(t *testing.T) {
handler := &Handler{checkout: &fakeCheckoutService{}}
req := httptest.NewRequest(http.MethodPost, "/api/checkout", bytes.NewBufferString("{"))
rec := httptest.NewRecorder()
handler.createCheckoutSession()(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected status 400, got %d", rec.Code)
}
}
func TestCreateCheckoutSessionStripeFailure(t *testing.T) {
handler := &Handler{
checkout: &fakeCheckoutService{err: errors.New("stripe failure")},
}
req := httptest.NewRequest(http.MethodPost, "/api/checkout", http.NoBody)
rec := httptest.NewRecorder()
handler.createCheckoutSession()(rec, req)
if rec.Code != http.StatusInternalServerError {
t.Fatalf("expected status 500, got %d", rec.Code)
}
}
func TestCreateCheckoutSessionMethodNotAllowed(t *testing.T) {
handler := &Handler{checkout: &fakeCheckoutService{}}
mux := http.NewServeMux()
mux.HandleFunc("POST /api/checkout", handler.createCheckoutSession())
req := httptest.NewRequest(http.MethodGet, "/api/checkout", http.NoBody)
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, req)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected status 405, got %d", rec.Code)
}
if allow := rec.Header().Get("Allow"); allow != http.MethodPost {
t.Fatalf("expected Allow header to be POST, got %s", allow)
}
}