From c850225ee63f268d8824123ba8aae12f58f149ee Mon Sep 17 00:00:00 2001 From: k1LoW Date: Mon, 20 May 2024 15:32:51 +0900 Subject: [PATCH] Add test for connectrpc --- Makefile | 1 + go.mod | 2 + go.sum | 8 ++ grpcstub_test.go | 45 +++++++ testdata/bsr/protobuf/buf.gen.yaml | 3 + testdata/bsr/protobuf/buf.lock | 4 +- .../go/pinger/pingerconnect/pinger.connect.go | 112 ++++++++++++++++++ 7 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 testdata/bsr/protobuf/gen/go/pinger/pingerconnect/pinger.connect.go diff --git a/Makefile b/Makefile index e7b409c..bc3c703 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ testclient: mkdir -p testdata/hello cd testdata/ && protoc --go_out=routeguide --go_opt=paths=source_relative --go-grpc_out=routeguide --go-grpc_opt=paths=source_relative route_guide.proto cd testdata/ && protoc --go_out=hello --go_opt=paths=source_relative --go-grpc_out=hello --go-grpc_opt=paths=source_relative hello.proto + cd testdata/bsr/protobuf && buf mod update && buf generate cert: rm -f testdata/*.pem testdata/*.srl diff --git a/go.mod b/go.mod index 2812833..78e8e16 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module github.com/k1LoW/grpcstub go 1.22.2 require ( + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.1-20240508200655-46a4cf4ba109.1 + connectrpc.com/connect v1.16.2 github.com/bmatcuk/doublestar/v4 v4.6.1 github.com/bufbuild/protocompile v0.13.0 github.com/google/go-cmp v0.6.0 diff --git a/go.sum b/go.sum index 6d717d4..4562645 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,15 @@ +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.1-20240508200655-46a4cf4ba109.1 h1:LEXWFH/xZ5oOWrC3oOtHbUyBdzRWMCPpAQmKC9v05mA= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.1-20240508200655-46a4cf4ba109.1/go.mod h1:XF+P8+RmfdufmIYpGUC+6bF7S+IlmHDEnCrO3OXaUAQ= +connectrpc.com/connect v1.16.2 h1:ybd6y+ls7GOlb7Bh5C8+ghA6SvCBajHwxssO2CGFjqE= +connectrpc.com/connect v1.16.2/go.mod h1:n2kgwskMHXC+lVqb18wngEpF95ldBHXjZYJussz5FRc= github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bufbuild/protocompile v0.13.0 h1:6cwUB0Y2tSvmNxsbunwzmIto3xOlJOV7ALALuVOs92M= github.com/bufbuild/protocompile v0.13.0/go.mod h1:dr++fGGeMPWHv7jPeT06ZKukm45NJscd7rUxQVzEKRk= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/jaswdr/faker v1.19.1 h1:xBoz8/O6r0QAR8eEvKJZMdofxiRH+F0M/7MU9eNKhsM= @@ -61,11 +67,13 @@ golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/grpcstub_test.go b/grpcstub_test.go index 64ebe8e..b36d0ce 100644 --- a/grpcstub_test.go +++ b/grpcstub_test.go @@ -2,17 +2,23 @@ package grpcstub import ( "context" + "crypto/tls" "fmt" + "net/http" "os" "strings" "testing" "time" + "connectrpc.com/connect" "github.com/google/go-cmp/cmp" "github.com/jhump/protoreflect/v2/grpcreflect" + "github.com/k1LoW/grpcstub/testdata/bsr/protobuf/gen/go/pinger" + "github.com/k1LoW/grpcstub/testdata/bsr/protobuf/gen/go/pinger/pingerconnect" "github.com/k1LoW/grpcstub/testdata/hello" "github.com/k1LoW/grpcstub/testdata/routeguide" "github.com/tenntenn/golden" + "golang.org/x/net/http2" "google.golang.org/grpc" "google.golang.org/grpc/codes" healthpb "google.golang.org/grpc/health/grpc_health_v1" @@ -734,3 +740,42 @@ func TestBufProtoRegistry(t *testing.T) { }) }) } + +func TestWithConnectClient(t *testing.T) { + ctx := context.Background() + cacert, err := os.ReadFile("testdata/cacert.pem") + if err != nil { + t.Fatal(err) + } + cert, err := os.ReadFile("testdata/cert.pem") + if err != nil { + t.Fatal(err) + } + key, err := os.ReadFile("testdata/key.pem") + if err != nil { + t.Fatal(err) + } + ts := NewTLSServer(t, "testdata/bsr/protobuf", cacert, cert, key) + ts.Service("pinger.PingerService").Method("Ping").Response(&pinger.PingResponse{ + Message: "hello", + }) + t.Cleanup(func() { + ts.Close() + }) + u := fmt.Sprintf("https://%s", ts.Addr()) + httpClient := &http.Client{ + Transport: &http2.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint: gosec + }, + } + client := pingerconnect.NewPingerServiceClient(httpClient, u, connect.WithGRPC()) + res, err := client.Ping(ctx, connect.NewRequest(&pinger.PingRequest{ + Message: "hello", + })) + if err != nil { + t.Fatal(err) + } + if want := "hello"; res.Msg.GetMessage() != want { + t.Errorf("got %v\nwant %v", res.Msg.GetMessage(), want) + } +} diff --git a/testdata/bsr/protobuf/buf.gen.yaml b/testdata/bsr/protobuf/buf.gen.yaml index d90d178..bfa1a9f 100644 --- a/testdata/bsr/protobuf/buf.gen.yaml +++ b/testdata/bsr/protobuf/buf.gen.yaml @@ -12,3 +12,6 @@ plugins: - name: go-grpc out: gen/go opt: paths=source_relative + - name: connect-go + out: gen/go + opt: paths=source_relative diff --git a/testdata/bsr/protobuf/buf.lock b/testdata/bsr/protobuf/buf.lock index a8680c7..53f9512 100644 --- a/testdata/bsr/protobuf/buf.lock +++ b/testdata/bsr/protobuf/buf.lock @@ -4,5 +4,5 @@ deps: - remote: buf.build owner: bufbuild repository: protovalidate - commit: b983156c5e994cc9892e0ce3e64e17e0 - digest: shake256:fb47a62989d38c2529bcc5cd86ded43d800eb84cee82b42b9e8a9e815d4ee8134a0fb9d0ce8299b27c2d2bbb7d6ade0c4ad5a8a4d467e1e2c7ca619ae9f634e2 + commit: 46a4cf4ba1094a34bcd89a6c67163b4b + digest: shake256:436ce453801917c11bc7b21d66bcfae87da2aceb804a041487be1e51dc9fbc219e61ea6a552db7a7aa6d63bb5efd0f3ed5fe3d4c42d4f750d0eb35f14144e3b6 diff --git a/testdata/bsr/protobuf/gen/go/pinger/pingerconnect/pinger.connect.go b/testdata/bsr/protobuf/gen/go/pinger/pingerconnect/pinger.connect.go new file mode 100644 index 0000000..224d246 --- /dev/null +++ b/testdata/bsr/protobuf/gen/go/pinger/pingerconnect/pinger.connect.go @@ -0,0 +1,112 @@ +// Code generated by protoc-gen-connect-go. DO NOT EDIT. +// +// Source: pinger/pinger.proto + +package pingerconnect + +import ( + connect "connectrpc.com/connect" + context "context" + errors "errors" + pinger "github.com/k1LoW/grpcstub/testdata/bsr/protobuf/gen/go/pinger" + http "net/http" + strings "strings" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect.IsAtLeastVersion1_13_0 + +const ( + // PingerServiceName is the fully-qualified name of the PingerService service. + PingerServiceName = "pinger.PingerService" +) + +// These constants are the fully-qualified names of the RPCs defined in this package. They're +// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. +// +// Note that these are different from the fully-qualified method names used by +// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to +// reflection-formatted method names, remove the leading slash and convert the remaining slash to a +// period. +const ( + // PingerServicePingProcedure is the fully-qualified name of the PingerService's Ping RPC. + PingerServicePingProcedure = "/pinger.PingerService/Ping" +) + +// These variables are the protoreflect.Descriptor objects for the RPCs defined in this package. +var ( + pingerServiceServiceDescriptor = pinger.File_pinger_pinger_proto.Services().ByName("PingerService") + pingerServicePingMethodDescriptor = pingerServiceServiceDescriptor.Methods().ByName("Ping") +) + +// PingerServiceClient is a client for the pinger.PingerService service. +type PingerServiceClient interface { + Ping(context.Context, *connect.Request[pinger.PingRequest]) (*connect.Response[pinger.PingResponse], error) +} + +// NewPingerServiceClient constructs a client for the pinger.PingerService service. By default, it +// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends +// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or +// connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewPingerServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) PingerServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + return &pingerServiceClient{ + ping: connect.NewClient[pinger.PingRequest, pinger.PingResponse]( + httpClient, + baseURL+PingerServicePingProcedure, + connect.WithSchema(pingerServicePingMethodDescriptor), + connect.WithClientOptions(opts...), + ), + } +} + +// pingerServiceClient implements PingerServiceClient. +type pingerServiceClient struct { + ping *connect.Client[pinger.PingRequest, pinger.PingResponse] +} + +// Ping calls pinger.PingerService.Ping. +func (c *pingerServiceClient) Ping(ctx context.Context, req *connect.Request[pinger.PingRequest]) (*connect.Response[pinger.PingResponse], error) { + return c.ping.CallUnary(ctx, req) +} + +// PingerServiceHandler is an implementation of the pinger.PingerService service. +type PingerServiceHandler interface { + Ping(context.Context, *connect.Request[pinger.PingRequest]) (*connect.Response[pinger.PingResponse], error) +} + +// NewPingerServiceHandler builds an HTTP handler from the service implementation. It returns the +// path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewPingerServiceHandler(svc PingerServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { + pingerServicePingHandler := connect.NewUnaryHandler( + PingerServicePingProcedure, + svc.Ping, + connect.WithSchema(pingerServicePingMethodDescriptor), + connect.WithHandlerOptions(opts...), + ) + return "/pinger.PingerService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case PingerServicePingProcedure: + pingerServicePingHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedPingerServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedPingerServiceHandler struct{} + +func (UnimplementedPingerServiceHandler) Ping(context.Context, *connect.Request[pinger.PingRequest]) (*connect.Response[pinger.PingResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("pinger.PingerService.Ping is not implemented")) +}