Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions binding/binding_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package binding

import (
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/eatmoreapple/hx/httpx"
)

type TestExtractor string

func (t TestExtractor) ValueName() string {
return "test"
}

type TestStruct struct {
Name httpx.FromQuery[TestExtractor] `json:"name"`
}

func TestDefault(t *testing.T) {
tests := []struct {
method string
contentType string
expected Binder
}{
{http.MethodGet, "application/json", queryBinder},
{http.MethodPost, "application/json", jsonBinder},
{http.MethodPost, "application/xml", xmlBinder},
{http.MethodPost, "application/x-www-form-urlencoded", formBinder},
{http.MethodPost, "multipart/form-data", formBinder},
{http.MethodPost, "text/plain", queryBinder},
{http.MethodPost, "invalid", queryBinder},
}

for _, tt := range tests {
binder := Default(tt.method, tt.contentType)
if binder != tt.expected {
t.Errorf("expected binder %T, got %T", tt.expected, binder)
}
}
}

func TestGenericBinder(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/?test=hello", nil)
var ts TestStruct

binder := Generic()
if err := binder.Bind(req, &ts); err != nil {
t.Errorf("unexpected error: %v", err)
}

if ts.Name.String() != "hello" {
t.Errorf("expected name %s, got %s", "hello", ts.Name.String())
}
}

func TestGenericBinderPointer(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/?test=hello", nil)

type TestStructPtr struct {
Name *httpx.FromQuery[TestExtractor]
}
var ts TestStructPtr

binder := Generic()
if err := binder.Bind(req, &ts); err != nil {
t.Errorf("unexpected error: %v", err)
}

if ts.Name == nil {
t.Fatal("expected name to be not nil")
}

if ts.Name.String() != "hello" {
t.Errorf("expected name %s, got %s", "hello", ts.Name.String())
}
}

func TestJSONBinder(t *testing.T) {
body := `{"name": "hello"}`
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")

type Data struct {
Name string `json:"name"`
}
var data Data

if err := jsonBinder.Bind(req, &data); err != nil {
t.Errorf("unexpected error: %v", err)
}

if data.Name != "hello" {
t.Errorf("expected name %s, got %s", "hello", data.Name)
}
}
250 changes: 250 additions & 0 deletions handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
package hx

import (
"context"
"encoding/json"
"encoding/xml"
"errors"
"net/http"
"net/http/httptest"
"testing"

"github.com/eatmoreapple/hx/httpx"
)

func TestWarp(t *testing.T) {
handler := Warp(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
})

req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()

if err := handler(w, req); err != nil {
t.Errorf("expected no error, got %v", err)
}

if w.Code != http.StatusOK {
t.Errorf("expected status code %d, got %d", http.StatusOK, w.Code)
}

if w.Body.String() != "ok" {
t.Errorf("expected body %s, got %s", "ok", w.Body.String())
}
}

func TestGeneric(t *testing.T) {
type Request struct {
Name string
}
type Response struct {
Message string
}

handler := Generic[Request, Response](func(ctx context.Context, req Request) (Response, error) {
return Response{Message: "Hello " + req.Name}, nil
})

// Since Generic returns a TypedHandlerFunc, we can't directly call it as http.HandlerFunc
// We need to verify it returns the correct function type
if handler == nil {
t.Error("expected handler to be not nil")
}
}

func TestRender(t *testing.T) {
type Request struct {
Name string `json:"name"`
}

handler := Render[Request](func(ctx context.Context, req Request) (httpx.ResponseRender, error) {
return httpx.JSONResponse{Data: map[string]string{"message": "Hello " + req.Name}}, nil
})

// Create a request with JSON body
req := httptest.NewRequest(http.MethodPost, "/", nil)
// Mock binding by manually setting the request context or just testing the handler logic if possible.
// However, Render uses internal requestHandler which uses ShouldBind.
// We need to provide a body for binding.
// For simplicity, let's assume query binding for GET or body for POST.
// Since we haven't implemented the full test suite for binding yet, we might face issues if binding fails.
// But Render creates a HandlerFunc that does binding.

// Let's try with a simple struct that doesn't require complex binding first, or just empty.
w := httptest.NewRecorder()
if err := handler(w, req); err != nil {
t.Errorf("expected no error, got %v", err)
}
}

func TestJSON(t *testing.T) {
type Request struct{}
type Response struct {
Message string `json:"message"`
}

handler := G(func(ctx context.Context, req Request) (Response, error) {
return Response{Message: "hello"}, nil
}).JSON()

req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()

if err := handler(w, req); err != nil {
t.Errorf("expected no error, got %v", err)
}

if w.Code != http.StatusOK {
t.Errorf("expected status code %d, got %d", http.StatusOK, w.Code)
}

var resp Response
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Errorf("failed to unmarshal response: %v", err)
}

if resp.Message != "hello" {
t.Errorf("expected message %s, got %s", "hello", resp.Message)
}
}

func TestString(t *testing.T) {
type Request struct{}

handler := G(func(ctx context.Context, req Request) (string, error) {
return "hello", nil
}).String()

req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()

if err := handler(w, req); err != nil {
t.Errorf("expected no error, got %v", err)
}

if w.Body.String() != "hello" {
t.Errorf("expected body %s, got %s", "hello", w.Body.String())
}
}

func TestStringPanic(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Error("expected panic but got nil")
}
}()

type Request struct{}
type Response struct{}

G(func(ctx context.Context, req Request) (Response, error) {
return Response{}, nil
}).String()
}

func TestXML(t *testing.T) {
type Request struct{}
type Response struct {
XMLName xml.Name `xml:"response"`
Message string `xml:"message"`
}

handler := G(func(ctx context.Context, req Request) (Response, error) {
return Response{Message: "hello"}, nil
}).XML()

req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()

if err := handler(w, req); err != nil {
t.Errorf("expected no error, got %v", err)
}

var resp Response
if err := xml.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Errorf("failed to unmarshal response: %v", err)
}

if resp.Message != "hello" {
t.Errorf("expected message %s, got %s", "hello", resp.Message)
}
}

func TestPipe(t *testing.T) {
type Request struct{}
type Response struct{}

var steps []string

middleware1 := func(ctx context.Context, req Request) error {
steps = append(steps, "m1")
return nil
}

middleware2 := func(ctx context.Context, req Request) error {
steps = append(steps, "m2")
return nil
}

handler := G(func(ctx context.Context, req Request) (Response, error) {
steps = append(steps, "handler")
return Response{}, nil
}).Pipe(middleware1, middleware2)

// Pipe returns TypedHandlerFunc, we need to convert it to HandlerFunc to execute or just call it directly if we could.
// But TypedHandlerFunc is a function type, so we can call it.

_, err := handler(context.Background(), Request{})
if err != nil {
t.Errorf("expected no error, got %v", err)
}

expected := []string{"m1", "m2", "handler"}
if len(steps) != len(expected) {
t.Errorf("expected %d steps, got %d", len(expected), len(steps))
}

for i, step := range steps {
if step != expected[i] {
t.Errorf("expected step %d to be %s, got %s", i, expected[i], step)
}
}
}

func TestPipeError(t *testing.T) {
type Request struct{}
type Response struct{}

expectedErr := errors.New("middleware error")

middleware := func(ctx context.Context, req Request) error {
return expectedErr
}

handler := G(func(ctx context.Context, req Request) (Response, error) {
return Response{}, nil
}).Pipe(middleware)

_, err := handler(context.Background(), Request{})
if err != expectedErr {
t.Errorf("expected error %v, got %v", expectedErr, err)
}
}

func TestE(t *testing.T) {
handler := E(func(ctx context.Context) (string, error) {
return "ok", nil
}).String()

req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()

if err := handler(w, req); err != nil {
t.Errorf("expected no error, got %v", err)
}

if w.Body.String() != "ok" {
t.Errorf("expected body %s, got %s", "ok", w.Body.String())
}
}
Loading