add tests
This commit is contained in:
parent
9d402178c2
commit
c2769a233e
|
@ -6,7 +6,6 @@ import (
|
|||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -14,17 +13,16 @@ import (
|
|||
"syscall"
|
||||
|
||||
"gitea.dwysokinski.me/Kichiyaki/grpc-g2a/cmd/internal"
|
||||
"gitea.dwysokinski.me/Kichiyaki/grpc-g2a/internal/greeter"
|
||||
"gitea.dwysokinski.me/Kichiyaki/grpc-g2a/proto"
|
||||
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
|
||||
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
|
||||
grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
_ "google.golang.org/grpc/encoding/gzip"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
|
@ -101,9 +99,7 @@ func newServer(address string, logger *zap.Logger) (*server, error) {
|
|||
),
|
||||
)
|
||||
|
||||
proto.RegisterGreeterServer(srv, &greeterServer{
|
||||
logger: logger,
|
||||
})
|
||||
proto.RegisterGreeterServer(srv, greeter.NewServer(logger))
|
||||
|
||||
return &server{
|
||||
lis: lis,
|
||||
|
@ -150,91 +146,3 @@ func createPanicHandler(logger *zap.Logger) grpc_recovery.RecoveryHandlerFunc {
|
|||
return status.Errorf(codes.Internal, "internal server error")
|
||||
}
|
||||
}
|
||||
|
||||
type greeterServer struct {
|
||||
proto.UnimplementedGreeterServer
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (g *greeterServer) SayHello(ctx context.Context, r *proto.HelloRequest) (*proto.HelloReply, error) {
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if ok {
|
||||
g.logger.Debug("received metadata", zap.Any("metadata", md))
|
||||
}
|
||||
return &proto.HelloReply{
|
||||
Message: buildHelloMsg(r.GetName()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *greeterServer) SayHelloToJames(_ context.Context, r *proto.HelloRequest) (*proto.HelloReply, error) {
|
||||
name := r.GetName()
|
||||
|
||||
if name != "James" {
|
||||
st, err := status.
|
||||
New(codes.InvalidArgument, "invalid name").
|
||||
WithDetails(&errdetails.BadRequest{
|
||||
FieldViolations: []*errdetails.BadRequest_FieldViolation{
|
||||
{
|
||||
Field: "name",
|
||||
Description: "must be James",
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, status.New(codes.Internal, "internal server error").Err()
|
||||
}
|
||||
return nil, st.Err()
|
||||
}
|
||||
|
||||
return &proto.HelloReply{
|
||||
Message: buildHelloMsg(name),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *greeterServer) SayHelloServerStream(r *proto.MultiHelloRequest, stream proto.Greeter_SayHelloServerStreamServer) error {
|
||||
for _, n := range r.GetNames() {
|
||||
if err := stream.Send(&proto.HelloReply{
|
||||
Message: buildHelloMsg(n),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *greeterServer) SayHelloClientStream(stream proto.Greeter_SayHelloClientStreamServer) error {
|
||||
var messages []string
|
||||
for {
|
||||
r, err := stream.Recv()
|
||||
if errors.Is(err, io.EOF) {
|
||||
return stream.SendAndClose(&proto.MultiHelloReply{
|
||||
Messages: messages,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("stream returned error: %w", err)
|
||||
}
|
||||
|
||||
messages = append(messages, buildHelloMsg(r.GetName()))
|
||||
}
|
||||
}
|
||||
|
||||
func (g *greeterServer) SayHelloBidirectionalStream(stream proto.Greeter_SayHelloBidirectionalStreamServer) error {
|
||||
for {
|
||||
r, err := stream.Recv()
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("stream returned error: %w", err)
|
||||
}
|
||||
|
||||
if err = stream.Send(&proto.HelloReply{Message: buildHelloMsg(r.GetName())}); err != nil {
|
||||
return fmt.Errorf("couldn't send reply: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildHelloMsg(name string) string {
|
||||
return "Hello " + name + "!"
|
||||
}
|
||||
|
|
6
go.mod
6
go.mod
|
@ -5,17 +5,21 @@ go 1.20
|
|||
require (
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
|
||||
github.com/stretchr/testify v1.8.2
|
||||
go.uber.org/zap v1.24.0
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f
|
||||
google.golang.org/grpc v1.54.0
|
||||
google.golang.org/protobuf v1.30.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
11
go.sum
11
go.sum
|
@ -35,8 +35,10 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vb
|
|||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
|
@ -47,11 +49,16 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
|||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
|
@ -133,11 +140,13 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
|||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
package greeter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"gitea.dwysokinski.me/Kichiyaki/grpc-g2a/proto"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
proto.UnimplementedGreeterServer
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func NewServer(logger *zap.Logger) *Server {
|
||||
return &Server{logger: logger}
|
||||
}
|
||||
|
||||
func (s *Server) SayHello(ctx context.Context, r *proto.HelloRequest) (*proto.HelloReply, error) {
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if ok {
|
||||
s.logger.Debug("received metadata", zap.Any("metadata", md))
|
||||
}
|
||||
return &proto.HelloReply{
|
||||
Message: buildHelloMsg(r.GetName()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SayHelloToJames(_ context.Context, r *proto.HelloRequest) (*proto.HelloReply, error) {
|
||||
name := r.GetName()
|
||||
|
||||
if name != "James" {
|
||||
st, err := status.
|
||||
New(codes.InvalidArgument, "invalid name").
|
||||
WithDetails(&errdetails.BadRequest{
|
||||
FieldViolations: []*errdetails.BadRequest_FieldViolation{
|
||||
{
|
||||
Field: "name",
|
||||
Description: "must be James",
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, status.New(codes.Internal, "internal server error").Err()
|
||||
}
|
||||
return nil, st.Err()
|
||||
}
|
||||
|
||||
return &proto.HelloReply{
|
||||
Message: buildHelloMsg(name),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SayHelloServerStream(r *proto.MultiHelloRequest, stream proto.Greeter_SayHelloServerStreamServer) error {
|
||||
for _, n := range r.GetNames() {
|
||||
if err := stream.Send(&proto.HelloReply{
|
||||
Message: buildHelloMsg(n),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) SayHelloClientStream(stream proto.Greeter_SayHelloClientStreamServer) error {
|
||||
var messages []string
|
||||
for {
|
||||
r, err := stream.Recv()
|
||||
if errors.Is(err, io.EOF) {
|
||||
return stream.SendAndClose(&proto.MultiHelloReply{
|
||||
Messages: messages,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("stream returned error: %w", err)
|
||||
}
|
||||
|
||||
messages = append(messages, buildHelloMsg(r.GetName()))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) SayHelloBidirectionalStream(stream proto.Greeter_SayHelloBidirectionalStreamServer) error {
|
||||
for {
|
||||
r, err := stream.Recv()
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("stream returned error: %w", err)
|
||||
}
|
||||
|
||||
if err = stream.Send(&proto.HelloReply{Message: buildHelloMsg(r.GetName())}); err != nil {
|
||||
return fmt.Errorf("couldn't send reply: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildHelloMsg(name string) string {
|
||||
return "Hello " + name + "!"
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package greeter_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"gitea.dwysokinski.me/Kichiyaki/grpc-g2a/internal/greeter"
|
||||
"gitea.dwysokinski.me/Kichiyaki/grpc-g2a/proto"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/test/bufconn"
|
||||
)
|
||||
|
||||
func TestServer_SayHello(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "James",
|
||||
expected: "Hello James!",
|
||||
},
|
||||
{
|
||||
name: "Robert",
|
||||
expected: "Hello Robert!",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
conn := newServer(t)
|
||||
|
||||
reply, err := conn.SayHello(context.Background(), &proto.HelloRequest{Name: tt.name})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, reply.GetMessage())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_SayHelloBidirectionalStream(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
names := make([]string, 20)
|
||||
for i := range names {
|
||||
names[i] = uuid.NewString()
|
||||
}
|
||||
|
||||
client := newServer(t)
|
||||
|
||||
streamClient, err := client.SayHelloBidirectionalStream(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, n := range names {
|
||||
err = streamClient.Send(&proto.HelloRequest{
|
||||
Name: n,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.NoError(t, streamClient.CloseSend())
|
||||
|
||||
replies := make([]*proto.HelloReply, 0, len(names))
|
||||
for {
|
||||
reply, err := streamClient.Recv()
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
replies = append(replies, reply)
|
||||
}
|
||||
|
||||
require.Len(t, replies, len(names))
|
||||
for _, n := range names {
|
||||
found := false
|
||||
for _, r := range replies {
|
||||
if r.GetMessage() == "Hello "+n+"!" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "name="+n)
|
||||
}
|
||||
}
|
||||
|
||||
func newServer(t *testing.T) proto.GreeterClient {
|
||||
buffer := 101024 * 1024
|
||||
lis := bufconn.Listen(buffer)
|
||||
|
||||
server := grpc.NewServer()
|
||||
proto.RegisterGreeterServer(server, greeter.NewServer(zap.NewNop()))
|
||||
go func() {
|
||||
require.NoError(t, server.Serve(lis))
|
||||
}()
|
||||
t.Cleanup(func() {
|
||||
server.GracefulStop()
|
||||
})
|
||||
|
||||
conn, err := grpc.Dial(
|
||||
"",
|
||||
grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) {
|
||||
return lis.Dial()
|
||||
}),
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, conn.Close())
|
||||
})
|
||||
|
||||
return proto.NewGreeterClient(conn)
|
||||
}
|
Loading…
Reference in New Issue