diff --git a/transport/grpc/client_test.go b/transport/grpc/client_test.go index 9349a16b4..89122cbf2 100644 --- a/transport/grpc/client_test.go +++ b/transport/grpc/client_test.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "testing" + "time" "github.com/go-kratos/kratos/v2/middleware" "github.com/go-kratos/kratos/v2/registry" @@ -18,6 +19,13 @@ func TestWithEndpoint(t *testing.T) { assert.Equal(t, v, o.endpoint) } +func TestWithTimeout(t *testing.T) { + o := &clientOptions{} + v := time.Duration(123) + WithTimeout(v)(o) + assert.Equal(t, v, o.timeout) +} + func TestWithMiddleware(t *testing.T) { o := &clientOptions{} v := []middleware.Middleware{ @@ -50,6 +58,25 @@ func TestWithTLSConfig(t *testing.T) { assert.Equal(t, v, o.tlsConf) } +func EmptyMiddleware() middleware.Middleware { + return func(handler middleware.Handler) middleware.Handler { + return func(ctx context.Context, req interface{}) (reply interface{}, err error) { + return handler(ctx, req) + } + } +} + +func TestUnaryClientInterceptor(t *testing.T) { + f := unaryClientInterceptor([]middleware.Middleware{EmptyMiddleware()}, time.Duration(100)) + req := &struct{}{} + resp := &struct{}{} + + err := f(context.TODO(), "hello", req, resp, &grpc.ClientConn{}, func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, opts ...grpc.CallOption) error { + return nil + }) + assert.NoError(t, err) +} + func TestWithUnaryInterceptor(t *testing.T) { o := &clientOptions{} v := []grpc.UnaryClientInterceptor{ @@ -74,3 +101,12 @@ func TestWithOptions(t *testing.T) { WithOptions(v...)(o) assert.Equal(t, v, o.grpcOpts) } + +func TestDial(t *testing.T) { + o := &clientOptions{} + v := []grpc.DialOption{ + grpc.EmptyDialOption{}, + } + WithOptions(v...)(o) + assert.Equal(t, v, o.grpcOpts) +} diff --git a/transport/grpc/resolver/direct/builder_test.go b/transport/grpc/resolver/direct/builder_test.go new file mode 100644 index 000000000..8db9cddda --- /dev/null +++ b/transport/grpc/resolver/direct/builder_test.go @@ -0,0 +1,37 @@ +package direct + +import ( + "github.com/stretchr/testify/assert" + "google.golang.org/grpc/resolver" + "google.golang.org/grpc/serviceconfig" + "testing" +) + +func TestDirectBuilder_Scheme(t *testing.T) { + b := NewBuilder() + assert.Equal(t, "direct", b.Scheme()) +} + +type mockConn struct { +} + +func (m *mockConn) UpdateState(resolver.State) error { + return nil +} + +func (m *mockConn) ReportError(error) {} + +func (m *mockConn) NewAddress(addresses []resolver.Address) {} + +func (m *mockConn) NewServiceConfig(serviceConfig string) {} + +func (m *mockConn) ParseServiceConfig(serviceConfigJSON string) *serviceconfig.ParseResult { + return nil +} + +func TestDirectBuilder_Build(t *testing.T) { + b := NewBuilder() + r, err := b.Build(resolver.Target{}, &mockConn{}, resolver.BuildOptions{}) + assert.NoError(t, err) + r.ResolveNow(resolver.ResolveNowOptions{}) +} diff --git a/transport/grpc/resolver/direct/resolver_test.go b/transport/grpc/resolver/direct/resolver_test.go new file mode 100644 index 000000000..477177347 --- /dev/null +++ b/transport/grpc/resolver/direct/resolver_test.go @@ -0,0 +1 @@ +package direct diff --git a/transport/grpc/resolver/discovery/builder_test.go b/transport/grpc/resolver/discovery/builder_test.go new file mode 100644 index 000000000..c68120c5d --- /dev/null +++ b/transport/grpc/resolver/discovery/builder_test.go @@ -0,0 +1,81 @@ +package discovery + +import ( + "context" + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/registry" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc/resolver" + "google.golang.org/grpc/serviceconfig" + "testing" + "time" +) + +type mockLogger struct { + level log.Level + key string + val string +} + +func (l *mockLogger) Log(level log.Level, keyvals ...interface{}) error { + l.level = level + l.key = keyvals[0].(string) + l.val = keyvals[1].(string) + return nil +} + +func TestWithLogger(t *testing.T) { + b := &builder{} + WithLogger(&mockLogger{})(b) +} + +func TestWithInsecure(t *testing.T) { + b := &builder{} + WithInsecure(true)(b) + assert.True(t, b.insecure) +} + +func TestWithTimeout(t *testing.T) { + o := &builder{} + v := time.Duration(123) + WithTimeout(v)(o) + assert.Equal(t, v, o.timeout) +} + +type mockDiscovery struct { +} + +func (m *mockDiscovery) GetService(ctx context.Context, serviceName string) ([]*registry.ServiceInstance, error) { + return nil, nil +} +func (m *mockDiscovery) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) { + return &testWatch{}, nil +} + +func TestBuilder_Scheme(t *testing.T) { + b := NewBuilder(&mockDiscovery{}) + assert.Equal(t, "discovery", b.Scheme()) +} + +type mockConn struct { +} + +func (m *mockConn) UpdateState(resolver.State) error { + return nil +} + +func (m *mockConn) ReportError(error) {} + +func (m *mockConn) NewAddress(addresses []resolver.Address) {} + +func (m *mockConn) NewServiceConfig(serviceConfig string) {} + +func (m *mockConn) ParseServiceConfig(serviceConfigJSON string) *serviceconfig.ParseResult { + return nil +} + +func TestBuilder_Build(t *testing.T) { + b := NewBuilder(&mockDiscovery{}) + _, err := b.Build(resolver.Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "gprc://authority/endpoint"}, &mockConn{}, resolver.BuildOptions{}) + assert.NoError(t, err) +} diff --git a/transport/grpc/resolver/discovery/resolver.go b/transport/grpc/resolver/discovery/resolver.go index 76fd812a9..f801e0d22 100644 --- a/transport/grpc/resolver/discovery/resolver.go +++ b/transport/grpc/resolver/discovery/resolver.go @@ -36,7 +36,7 @@ func (r *discoveryResolver) watch() { if errors.Is(err, context.Canceled) { return } - r.log.Errorf("[resovler] Failed to watch discovery endpoint: %v", err) + r.log.Errorf("[resolver] Failed to watch discovery endpoint: %v", err) time.Sleep(time.Second) continue } @@ -49,7 +49,7 @@ func (r *discoveryResolver) update(ins []*registry.ServiceInstance) { for _, in := range ins { endpoint, err := endpoint.ParseEndpoint(in.Endpoints, "grpc", !r.insecure) if err != nil { - r.log.Errorf("[resovler] Failed to parse discovery endpoint: %v", err) + r.log.Errorf("[resolver] Failed to parse discovery endpoint: %v", err) continue } if endpoint == "" { @@ -63,7 +63,7 @@ func (r *discoveryResolver) update(ins []*registry.ServiceInstance) { addrs = append(addrs, addr) } if len(addrs) == 0 { - r.log.Warnf("[resovler] Zero endpoint found,refused to write, instances: %v", ins) + r.log.Warnf("[resolver] Zero endpoint found,refused to write, instances: %v", ins) return } r.cc.UpdateState(resolver.State{Addresses: addrs}) diff --git a/transport/grpc/resolver/discovery/resolver_test.go b/transport/grpc/resolver/discovery/resolver_test.go index 06e7a09fc..2492b1775 100644 --- a/transport/grpc/resolver/discovery/resolver_test.go +++ b/transport/grpc/resolver/discovery/resolver_test.go @@ -2,11 +2,13 @@ package discovery import ( "context" + "errors" "testing" "time" "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/registry" + "github.com/stretchr/testify/assert" "google.golang.org/grpc/resolver" ) @@ -21,30 +23,57 @@ func (t *testClientConn) UpdateState(s resolver.State) error { } type testWatch struct { + err error } func (m *testWatch) Next() ([]*registry.ServiceInstance, error) { time.Sleep(time.Millisecond * 200) ins := []*registry.ServiceInstance{ { - ID: "mock_ID", - Name: "mock_Name", - Version: "mock_Version", + ID: "mock_ID", + Name: "mock_Name", + Version: "mock_Version", + Endpoints: []string{"grpc://127.0.0.1?isSecure=true"}, + }, + { + ID: "mock_ID2", + Name: "mock_Name2", + Version: "mock_Version2", + Endpoints: []string{""}, }, } - return ins, nil + return ins, m.err } // Watch creates a watcher according to the service name. func (m *testWatch) Stop() error { - return nil + return m.err } func TestWatch(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) r := &discoveryResolver{ - w: &testWatch{}, + w: &testWatch{}, + cc: &testClientConn{te: t}, + log: log.NewHelper(log.DefaultLogger), + ctx: ctx, + cancel: cancel, + insecure: false, + } + go func() { + time.Sleep(time.Second * 2) + r.Close() + }() + r.watch() + t.Log("watch goroutine exited after 2 second") +} + +func TestWatchError(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + + r := &discoveryResolver{ + w: &testWatch{err: errors.New("bad")}, cc: &testClientConn{te: t}, log: log.NewHelper(log.DefaultLogger), ctx: ctx, @@ -55,6 +84,31 @@ func TestWatch(t *testing.T) { r.Close() }() r.watch() - t.Log("watch goroutine exited after 2 second") } + +func TestWatchContextCancel(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + + r := &discoveryResolver{ + w: &testWatch{err: context.Canceled}, + cc: &testClientConn{te: t}, + log: log.NewHelper(log.DefaultLogger), + ctx: ctx, + cancel: cancel, + } + go func() { + time.Sleep(time.Second * 2) + r.Close() + }() + r.watch() + t.Log("watch goroutine exited after 2 second") +} + +func TestParseAttributes(t *testing.T) { + a := parseAttributes(map[string]string{"a": "b"}) + assert.Equal(t, "b", a.Value("a").(string)) + x := a.WithValues("qq", "ww") + assert.Equal(t, "ww", x.Value("qq").(string)) + assert.Nil(t, x.Value("notfound")) +} diff --git a/transport/grpc/server_test.go b/transport/grpc/server_test.go index 5dd287ca6..1e9d6c327 100644 --- a/transport/grpc/server_test.go +++ b/transport/grpc/server_test.go @@ -3,8 +3,10 @@ package grpc import ( "context" "crypto/tls" + "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/middleware" "google.golang.org/grpc" + "net/url" "strings" "testing" "time" @@ -17,7 +19,9 @@ type testKey struct{} func TestServer(t *testing.T) { ctx := context.Background() ctx = context.WithValue(ctx, testKey{}, "test") - srv := NewServer() + srv := NewServer(Middleware([]middleware.Middleware{ + func(middleware.Handler) middleware.Handler { return nil }, + }...)) if e, err := srv.Endpoint(); err != nil || e == nil || strings.HasSuffix(e.Host, ":0") { t.Fatal(e, err) @@ -69,16 +73,35 @@ func TestTimeout(t *testing.T) { } func TestMiddleware(t *testing.T) { - o := &clientOptions{} + o := &Server{} v := []middleware.Middleware{ func(middleware.Handler) middleware.Handler { return nil }, } - WithMiddleware(v...)(o) + Middleware(v...)(o) assert.Equal(t, v, o.middleware) } +type mockLogger struct { + level log.Level + key string + val string +} + +func (l *mockLogger) Log(level log.Level, keyvals ...interface{}) error { + l.level = level + l.key = keyvals[0].(string) + l.val = keyvals[1].(string) + return nil +} + func TestLogger(t *testing.T) { - //todo + o := &Server{} + v := &mockLogger{} + Logger(v)(o) + o.log.Log(log.LevelWarn, "foo", "bar") + assert.Equal(t, "foo", v.key) + assert.Equal(t, "bar", v.val) + assert.Equal(t, log.LevelWarn, v.level) } func TestTLSConfig(t *testing.T) { @@ -110,3 +133,23 @@ func TestOptions(t *testing.T) { Options(v...)(o) assert.Equal(t, v, o.grpcOpts) } + +type testResp struct { + Data string +} + +func TestServer_unaryServerInterceptor(t *testing.T) { + u, err := url.Parse("grpc://hello/world") + assert.NoError(t, err) + srv := &Server{ctx: context.Background(), + endpoint: u, + middleware: []middleware.Middleware{EmptyMiddleware()}, + timeout: time.Duration(10), + } + req := &struct{}{} + rv, err := srv.unaryServerInterceptor()(context.TODO(), req, &grpc.UnaryServerInfo{}, func(ctx context.Context, req interface{}) (i interface{}, e error) { + return &testResp{Data: "hi"}, nil + }) + assert.NoError(t, err) + assert.Equal(t, "hi", rv.(*testResp).Data) +} diff --git a/transport/grpc/transport_test.go b/transport/grpc/transport_test.go index 5d19ff515..6cbb4c260 100644 --- a/transport/grpc/transport_test.go +++ b/transport/grpc/transport_test.go @@ -29,6 +29,8 @@ func TestTransport_RequestHeader(t *testing.T) { v.Set("a", "1") o := &Transport{reqHeader: v} assert.Equal(t, "1", o.RequestHeader().Get("a")) + assert.Equal(t, "", o.RequestHeader().Get("notfound")) + } func TestTransport_ReplyHeader(t *testing.T) { diff --git a/transport/http/calloption_test.go b/transport/http/calloption_test.go new file mode 100644 index 000000000..fa81e5aed --- /dev/null +++ b/transport/http/calloption_test.go @@ -0,0 +1,68 @@ +package http + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEmptyCallOptions(t *testing.T) { + assert.NoError(t, EmptyCallOption{}.before(&callInfo{})) + EmptyCallOption{}.after(&callInfo{}, &csAttempt{}) +} + +func TestContentType(t *testing.T) { + assert.Equal(t, "aaa", ContentType("aaa").(ContentTypeCallOption).ContentType) +} + +func TestContentTypeCallOption_before(t *testing.T) { + c := &callInfo{} + err := ContentType("aaa").before(c) + assert.NoError(t, err) + assert.Equal(t, "aaa", c.contentType) +} + +func TestDefaultCallInfo(t *testing.T) { + path := "hi" + rv := defaultCallInfo(path) + assert.Equal(t, path, rv.pathTemplate) + assert.Equal(t, path, rv.operation) + assert.Equal(t, "application/json", rv.contentType) +} + +func TestOperation(t *testing.T) { + assert.Equal(t, "aaa", Operation("aaa").(OperationCallOption).Operation) +} + +func TestOperationCallOption_before(t *testing.T) { + c := &callInfo{} + err := Operation("aaa").before(c) + assert.NoError(t, err) + assert.Equal(t, "aaa", c.operation) +} + +func TestPathTemplate(t *testing.T) { + assert.Equal(t, "aaa", PathTemplate("aaa").(PathTemplateCallOption).Pattern) +} + +func TestPathTemplateCallOption_before(t *testing.T) { + c := &callInfo{} + err := PathTemplate("aaa").before(c) + assert.NoError(t, err) + assert.Equal(t, "aaa", c.pathTemplate) +} + +func TestHeader(t *testing.T) { + h := http.Header{"A": []string{"123"}} + assert.Equal(t, "123", Header(&h).(HeaderCallOption).header.Get("A")) +} + +func TestHeaderCallOption_after(t *testing.T) { + h := http.Header{"A": []string{"123"}} + c := &callInfo{} + cs := &csAttempt{res: &http.Response{Header: h}} + o := Header(&h) + o.after(c, cs) + assert.Equal(t, &h, o.(HeaderCallOption).header) +} diff --git a/transport/http/client_test.go b/transport/http/client_test.go index f95d42fca..8aaee2b51 100644 --- a/transport/http/client_test.go +++ b/transport/http/client_test.go @@ -3,6 +3,7 @@ package http import ( "bytes" "context" + "crypto/tls" "encoding/json" "io/ioutil" nethttp "net/http" @@ -38,10 +39,25 @@ func TestWithTimeout(t *testing.T) { assert.Equal(t, co.timeout, ov) } +func TestWithBlock(t *testing.T) { + o := WithBlock() + co := &clientOptions{} + o(co) + assert.True(t, co.block) +} + func TestWithBalancer(t *testing.T) { } +func TestWithTLSConfig(t *testing.T) { + ov := &tls.Config{} + o := WithTLSConfig(ov) + co := &clientOptions{} + o(co) + assert.Same(t, ov, co.tlsConf) +} + func TestWithUserAgent(t *testing.T) { ov := "kratos" o := WithUserAgent(ov) diff --git a/transport/http/context_test.go b/transport/http/context_test.go new file mode 100644 index 000000000..d02cfda64 --- /dev/null +++ b/transport/http/context_test.go @@ -0,0 +1 @@ +package http diff --git a/transport/http/router_test.go b/transport/http/router_test.go index 074df92ea..b2b50f65b 100644 --- a/transport/http/router_test.go +++ b/transport/http/router_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/stretchr/testify/assert" "log" "net/http" "strings" @@ -166,3 +167,17 @@ func testRoute(t *testing.T, srv *Server) { t.Fatal("cors failed") } } + +func TestRouter_Group(t *testing.T) { + r := &Router{} + rr := r.Group("a", func(http.Handler) http.Handler { return nil }) + assert.Equal(t, "a", rr.prefix) +} + +func TestHandle(t *testing.T) { + r := newRouter("/", NewServer()) + h := func(i Context) error { + return nil + } + r.GET("/get", h) +} diff --git a/transport/http/server_test.go b/transport/http/server_test.go index 9c726cf50..2fa62c283 100644 --- a/transport/http/server_test.go +++ b/transport/http/server_test.go @@ -9,6 +9,7 @@ import ( "github.com/go-kratos/kratos/v2/middleware" "io/ioutil" "net/http" + "net/url" "strings" "testing" "time" @@ -181,13 +182,23 @@ func TestLogger(t *testing.T) { //todo } +func TestEndpoint(t *testing.T) { + u, err := url.Parse("http://hello/world") + assert.NoError(t, err) + o := &Server{} + Endpoint(u)(o) + assert.Equal(t, "hello", o.endpoint.Host) + assert.Equal(t, "http", o.endpoint.Scheme) + +} + func TestMiddleware(t *testing.T) { - o := &clientOptions{} + o := &Server{} v := []middleware.Middleware{ func(middleware.Handler) middleware.Handler { return nil }, } - WithMiddleware(v...)(o) - assert.Equal(t, v, o.middleware) + Middleware(v...)(o) + assert.Equal(t, v, o.ms) } func TestRequestDecoder(t *testing.T) {