github.com/abemedia/go-don@v0.2.2-0.20240329015135-be88e32bb73b/internal/test/encoding.go (about)

     1  package test
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"net/http"
     7  	"reflect"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/abemedia/go-don"
    12  	"github.com/abemedia/go-don/encoding"
    13  	_ "github.com/abemedia/go-don/encoding/text" // default encoding
    14  	"github.com/abemedia/go-don/pkg/httptest"
    15  	"github.com/google/go-cmp/cmp"
    16  	"github.com/google/go-cmp/cmp/cmpopts"
    17  )
    18  
    19  type EncodingOptions[T any] struct {
    20  	Mime   string
    21  	Parsed T
    22  	Raw    string
    23  }
    24  
    25  func Encoding[T any](t *testing.T, opt EncodingOptions[T]) {
    26  	t.Helper()
    27  	t.Run("Decode", func(t *testing.T) {
    28  		t.Helper()
    29  		Decode(t, opt)
    30  	})
    31  	t.Run("Encode", func(t *testing.T) {
    32  		t.Helper()
    33  		Encode(t, opt)
    34  	})
    35  }
    36  
    37  func Decode[T any](t *testing.T, opt EncodingOptions[T]) {
    38  	t.Helper()
    39  
    40  	var diff string
    41  
    42  	api := don.New(nil)
    43  	api.Post("/", don.H(func(ctx context.Context, req T) (any, error) {
    44  		diff = cmp.Diff(opt.Parsed, req, ignoreUnexported[T]())
    45  		return nil, nil
    46  	}))
    47  
    48  	ctx := httptest.NewRequest(http.MethodPost, "/", opt.Raw, map[string]string{"Content-Type": opt.Mime})
    49  	api.RequestHandler()(ctx)
    50  
    51  	if diff != "" {
    52  		t.Error(diff)
    53  	}
    54  
    55  	if ctx.Response.StatusCode() != http.StatusNoContent {
    56  		t.Errorf("expected success status: %v", &ctx.Response)
    57  	}
    58  }
    59  
    60  func Encode[T any](t *testing.T, opt EncodingOptions[T]) {
    61  	t.Helper()
    62  
    63  	api := don.New(nil)
    64  	api.Post("/", don.H(func(ctx context.Context, req any) (T, error) {
    65  		return opt.Parsed, nil
    66  	}))
    67  
    68  	ctx := httptest.NewRequest(http.MethodPost, "/", "", map[string]string{"Accept": opt.Mime})
    69  	api.RequestHandler()(ctx)
    70  
    71  	if diff := cmp.Diff(opt.Raw, string(ctx.Response.Body()), ignoreUnexported[T]()); diff != "" {
    72  		t.Error(diff)
    73  	}
    74  
    75  	if ctx.Response.StatusCode() != http.StatusOK {
    76  		t.Errorf("expected success status: %v", &ctx.Response)
    77  	}
    78  }
    79  
    80  func ignoreUnexported[T any]() cmp.Option {
    81  	t := reflect.TypeOf(*new(T))
    82  	for t.Kind() == reflect.Pointer {
    83  		t = t.Elem()
    84  	}
    85  	if t.Kind() != reflect.Struct {
    86  		return nil
    87  	}
    88  	return cmpopts.IgnoreUnexported(reflect.New(t).Elem().Interface())
    89  }
    90  
    91  func BenchmarkEncoding[T any](b *testing.B, opt EncodingOptions[T]) {
    92  	b.Run("Decode", func(b *testing.B) {
    93  		b.Helper()
    94  		BenchmarkDecode(b, opt)
    95  	})
    96  	b.Run("Encode", func(b *testing.B) {
    97  		b.Helper()
    98  		BenchmarkEncode(b, opt)
    99  	})
   100  }
   101  
   102  func BenchmarkDecode[T any](b *testing.B, opt EncodingOptions[T]) {
   103  	b.Helper()
   104  
   105  	dec := encoding.GetDecoder(opt.Mime)
   106  	if dec == nil {
   107  		b.Fatal("decoder not found")
   108  	}
   109  
   110  	rd := strings.NewReader(opt.Raw)
   111  	ctx := httptest.NewRequest("POST", "/", "", nil)
   112  	ctx.Request.SetBodyStream(rd, len(opt.Raw))
   113  
   114  	v := new(T)
   115  	if val := reflect.ValueOf(v).Elem(); val.Kind() == reflect.Pointer {
   116  		val.Set(reflect.New(val.Type().Elem()))
   117  	}
   118  
   119  	for i := 0; i < b.N; i++ {
   120  		rd.Seek(0, io.SeekStart) //nolint:errcheck
   121  		dec(ctx, v)              //nolint:errcheck
   122  	}
   123  }
   124  
   125  func BenchmarkEncode[T any](b *testing.B, opt EncodingOptions[T]) {
   126  	b.Helper()
   127  
   128  	enc := encoding.GetEncoder(opt.Mime)
   129  	if enc == nil {
   130  		b.Fatal("encoder not found")
   131  	}
   132  
   133  	ctx := httptest.NewRequest("POST", "/", "", nil)
   134  
   135  	for i := 0; i < b.N; i++ {
   136  		ctx.Response.ResetBody()
   137  		enc(ctx, opt.Parsed) //nolint:errcheck
   138  	}
   139  }