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  }