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 }