github.com/emcfarlane/larking@v0.0.0-20220605172417-1704b45ee6c3/web_test.go (about) 1 // Copyright 2022 Edward McFarlane. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package larking 6 7 import ( 8 "bytes" 9 "encoding/binary" 10 "io/ioutil" 11 "net/http" 12 "net/http/httptest" 13 "testing" 14 15 "github.com/emcfarlane/larking/testpb" 16 "github.com/google/go-cmp/cmp" 17 "google.golang.org/genproto/googleapis/rpc/status" 18 "google.golang.org/grpc" 19 "google.golang.org/protobuf/encoding/protojson" 20 "google.golang.org/protobuf/proto" 21 "google.golang.org/protobuf/testing/protocmp" 22 ) 23 24 func TestWeb(t *testing.T) { 25 26 // Create test server. 27 ms := &testpb.UnimplementedMessagingServer{} 28 29 o := new(overrides) 30 gs := grpc.NewServer(o.unaryOption(), o.streamOption()) 31 32 testpb.RegisterMessagingServer(gs, ms) 33 h := createGRPCWebHandler(gs) 34 35 type want struct { 36 statusCode int 37 //body []byte // either 38 msg proto.Message // or 39 // TODO: headers, trailers 40 } 41 42 frame := func(b []byte, msb uint8) []byte { 43 head := append([]byte{0 | msb, 0, 0, 0, 0}, b...) 44 binary.BigEndian.PutUint32(head[1:5], uint32(len(b))) 45 return head 46 } 47 deframe := func(b []byte) ([]byte, []byte) { 48 if len(b) < 5 { 49 t.Errorf("invalid deframe") 50 return nil, nil 51 } 52 x := int(binary.BigEndian.Uint32(b[1:5])) 53 b = b[5:] 54 return b[:x], b[x:] 55 } 56 57 // TODO: compare http.Response output 58 tests := []struct { 59 name string 60 req *http.Request 61 in in 62 out out 63 want want 64 }{{ 65 name: "unary proto request", 66 req: func() *http.Request { 67 msg := &testpb.GetMessageRequestOne{Name: "name/hello"} 68 b, err := proto.Marshal(msg) 69 if err != nil { 70 t.Fatal(err) 71 } 72 73 body := bytes.NewReader(frame(b, 0)) 74 req := httptest.NewRequest(http.MethodPost, "/larking.testpb.Messaging/GetMessageOne", body) 75 req.Header.Set("Content-Type", grpcWeb+"+proto") 76 return req 77 }(), 78 in: in{ 79 method: "/larking.testpb.Messaging/GetMessageOne", 80 msg: &testpb.GetMessageRequestOne{Name: "name/hello"}, 81 }, 82 out: out{ 83 msg: &testpb.Message{Text: "hello, world!"}, 84 }, 85 want: want{ 86 statusCode: 200, 87 msg: &testpb.Message{Text: "hello, world!"}, 88 }, 89 }} 90 91 opts := cmp.Options{protocmp.Transform()} 92 for _, tt := range tests { 93 t.Run(tt.name, func(t *testing.T) { 94 o.reset(t, "test", []interface{}{tt.in, tt.out}) 95 96 req := tt.req 97 req.Header["test"] = []string{tt.in.method} 98 99 w := httptest.NewRecorder() 100 //s.gs.ServeHTTP(w, req) 101 h.ServeHTTP(w, req) 102 resp := w.Result() 103 104 t.Log("resp", resp) 105 106 b, err := ioutil.ReadAll(resp.Body) 107 if err != nil { 108 t.Fatal(err) 109 } 110 t.Logf("resp length: %d", len(b)) 111 112 if sc := tt.want.statusCode; sc != resp.StatusCode { 113 t.Errorf("expected %d got %d", tt.want.statusCode, resp.StatusCode) 114 var msg status.Status 115 if err := protojson.Unmarshal(b, &msg); err != nil { 116 t.Error(err, string(b)) 117 return 118 } 119 t.Error("status.code", msg.Code) 120 t.Error("status.message", msg.Message) 121 return 122 } 123 124 //if tt.want.body != nil { 125 // if !bytes.Equal(b, tt.want.body) { 126 // t.Errorf("length %d != %d", len(tt.want.body), len(b)) 127 // t.Errorf("body %s != %s", tt.want.body, b) 128 // } 129 //} 130 if tt.want.msg != nil { 131 b, _ := deframe(b) 132 msg := proto.Clone(tt.want.msg) 133 if err := proto.Unmarshal(b, msg); err != nil { 134 t.Errorf("%v: %X", err, b) 135 return 136 } 137 diff := cmp.Diff(msg, tt.want.msg, opts...) 138 if diff != "" { 139 t.Error(diff) 140 } 141 } 142 }) 143 } 144 }