Improvements #130

Merged
vtolstov merged 14 commits from improvements into v3 2022-05-03 16:08:51 +03:00
16 changed files with 373 additions and 41 deletions

View File

@ -3,10 +3,27 @@ package api
import ( import (
"strings" "strings"
"testing" "testing"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/server"
) )
func TestDecode(t *testing.T) {
md := metadata.New(0)
md.Set("host", "localhost", "method", "GET", "path", "/")
ep := Decode(md)
if md == nil {
t.Fatalf("failed to decode md %#+v", md)
} else if len(ep.Host) != 1 || len(ep.Method) != 1 || len(ep.Path) != 1 {
t.Fatalf("ep invalid after decode %#+v", ep)
}
if ep.Host[0] != "localhost" || ep.Method[0] != "GET" || ep.Path[0] != "/" {
t.Fatalf("ep invalid after decode %#+v", ep)
}
}
//nolint:gocyclo //nolint:gocyclo
func TestEncoding(t *testing.T) { func TestEncode(t *testing.T) {
testData := []*Endpoint{ testData := []*Endpoint{
nil, nil,
{ {
@ -150,3 +167,79 @@ func TestValidate(t *testing.T) {
t.Fatalf("invalid pcre %v", epPcreInvalid.Path[0]) t.Fatalf("invalid pcre %v", epPcreInvalid.Path[0])
} }
} }
func TestWithEndpoint(t *testing.T) {
ep := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test/{id}"},
}
o := WithEndpoint(ep)
opts := server.NewHandlerOptions(o)
if opts.Metadata == nil {
t.Fatalf("WithEndpoint not works %#+v", opts)
}
md, ok := opts.Metadata[ep.Name]
if !ok {
t.Fatalf("WithEndpoint not works %#+v", opts)
}
if v, ok := md.Get("Endpoint"); !ok || v != "Foo.Bar" {
t.Fatalf("WithEndpoint not works %#+v", md)
}
}
func TestValidateNilErr(t *testing.T) {
var ep *Endpoint
if err := Validate(ep); err == nil {
t.Fatalf("Validate not works")
}
}
func TestValidateMissingNameErr(t *testing.T) {
ep := &Endpoint{}
if err := Validate(ep); err == nil {
t.Fatalf("Validate not works")
}
}
func TestValidateMissingHandlerErr(t *testing.T) {
ep := &Endpoint{Name: "test"}
if err := Validate(ep); err == nil {
t.Fatalf("Validate not works")
}
}
func TestValidateRegexpStartErr(t *testing.T) {
ep := &Endpoint{Name: "test", Handler: "test"}
ep.Path = []string{"^/"}
if err := Validate(ep); err == nil {
t.Fatalf("Validate not works")
}
}
func TestValidateRegexpEndErr(t *testing.T) {
ep := &Endpoint{Name: "test", Handler: "test", Path: []string{""}}
ep.Path[0] = "/$"
if err := Validate(ep); err == nil {
t.Fatalf("Validate not works")
}
}
func TestValidateRegexpNonErr(t *testing.T) {
ep := &Endpoint{Name: "test", Handler: "test", Path: []string{""}}
ep.Path[0] = "^/(.*)$"
if err := Validate(ep); err != nil {
t.Fatalf("Validate not works")
}
}
func TestValidateRegexpErr(t *testing.T) {
ep := &Endpoint{Name: "test", Handler: "test", Path: []string{""}}
ep.Path[0] = "^/(.$"
if err := Validate(ep); err == nil {
t.Fatalf("Validate not works")
}
}

View File

@ -7,16 +7,31 @@ import (
func TestFromContext(t *testing.T) { func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), brokerKey{}, NewBroker()) ctx := context.WithValue(context.TODO(), brokerKey{}, NewBroker())
c, ok := FromContext(ctx) c, ok := FromContext(ctx)
if c == nil || !ok { if c == nil || !ok {
t.Fatal("FromContext not works") t.Fatal("FromContext not works")
} }
} }
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewContext(t *testing.T) { func TestNewContext(t *testing.T) {
ctx := NewContext(context.TODO(), NewBroker()) ctx := NewContext(context.TODO(), NewBroker())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewBroker())
c, ok := FromContext(ctx) c, ok := FromContext(ctx)
if c == nil || !ok { if c == nil || !ok {
t.Fatal("NewContext not works") t.Fatal("NewContext not works")

View File

@ -14,9 +14,25 @@ func TestFromContext(t *testing.T) {
} }
} }
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewContext(t *testing.T) { func TestNewContext(t *testing.T) {
ctx := NewContext(context.TODO(), NewClient()) ctx := NewContext(context.TODO(), NewClient())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewClient())
c, ok := FromContext(ctx) c, ok := FromContext(ctx)
if c == nil || !ok { if c == nil || !ok {
t.Fatal("NewContext not works") t.Fatal("NewContext not works")

View File

@ -9,6 +9,7 @@ import (
"go.unistack.org/micro/v3/codec" "go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/errors" "go.unistack.org/micro/v3/errors"
"go.unistack.org/micro/v3/metadata" "go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/selector"
) )
// DefaultCodecs will be used to encode/decode data // DefaultCodecs will be used to encode/decode data
@ -233,18 +234,7 @@ func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opt
callOpts.Address = []string{n.opts.Proxy} callOpts.Address = []string{n.opts.Proxy}
} }
// lookup the route to send the reques to var next selector.Next
// TODO apply any filtering here
routes, err := n.opts.Lookup(ctx, req, callOpts)
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
// balance the list of nodes
next, err := callOpts.Selector.Select(routes)
if err != nil {
return err
}
// return errors.New("go.micro.client", "request timeout", 408) // return errors.New("go.micro.client", "request timeout", 408)
call := func(i int) error { call := func(i int) error {
@ -259,6 +249,22 @@ func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opt
time.Sleep(t) time.Sleep(t)
} }
if next == nil {
var routes []string
// lookup the route to send the reques to
// TODO apply any filtering here
routes, err = n.opts.Lookup(ctx, req, callOpts)
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
// balance the list of nodes
next, err = callOpts.Selector.Select(routes)
if err != nil {
return err
}
}
node := next() node := next()
// make the call // make the call
@ -323,6 +329,8 @@ func (n *noopClient) NewMessage(topic string, msg interface{}, opts ...MessageOp
} }
func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) { func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) {
var err error
// make a copy of call opts // make a copy of call opts
callOpts := n.opts.CallOptions callOpts := n.opts.CallOptions
for _, o := range opts { for _, o := range opts {
@ -374,18 +382,7 @@ func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption
callOpts.Address = []string{n.opts.Proxy} callOpts.Address = []string{n.opts.Proxy}
} }
// lookup the route to send the reques to var next selector.Next
// TODO apply any filtering here
routes, err := n.opts.Lookup(ctx, req, callOpts)
if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error())
}
// balance the list of nodes
next, err := callOpts.Selector.Select(routes)
if err != nil {
return nil, err
}
call := func(i int) (Stream, error) { call := func(i int) (Stream, error) {
// call backoff first. Someone may want an initial start delay // call backoff first. Someone may want an initial start delay
@ -399,6 +396,22 @@ func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption
time.Sleep(t) time.Sleep(t)
} }
if next == nil {
var routes []string
// lookup the route to send the reques to
// TODO apply any filtering here
routes, err = n.opts.Lookup(ctx, req, callOpts)
if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error())
}
// balance the list of nodes
next, err = callOpts.Selector.Select(routes)
if err != nil {
return nil, err
}
}
node := next() node := next()
stream, cerr := n.stream(ctx, node, req, callOpts) stream, cerr := n.stream(ctx, node, req, callOpts)

View File

@ -5,6 +5,24 @@ import (
"testing" "testing"
) )
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewConfig())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestFromContext(t *testing.T) { func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), configKey{}, NewConfig()) ctx := context.WithValue(context.TODO(), configKey{}, NewConfig())

View File

@ -5,6 +5,24 @@ import (
"testing" "testing"
) )
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewFlow())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestFromContext(t *testing.T) { func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), flowKey{}, NewFlow()) ctx := context.WithValue(context.TODO(), flowKey{}, NewFlow())

View File

@ -5,6 +5,24 @@ import (
"testing" "testing"
) )
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewLogger())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestFromContext(t *testing.T) { func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), loggerKey{}, NewLogger()) ctx := context.WithValue(context.TODO(), loggerKey{}, NewLogger())

View File

@ -5,6 +5,24 @@ import (
"testing" "testing"
) )
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, New(0))
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestFromContext(t *testing.T) { func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), mdKey{}, &rawMetadata{New(0)}) ctx := context.WithValue(context.TODO(), mdKey{}, &rawMetadata{New(0)})

View File

@ -76,16 +76,23 @@ func (md Metadata) Get(key string) (string, bool) {
} }
// Set is used to store value in metadata // Set is used to store value in metadata
func (md Metadata) Set(key, val string) { func (md Metadata) Set(kv ...string) {
md[textproto.CanonicalMIMEHeaderKey(key)] = val if len(kv)%2 == 1 {
kv = kv[:len(kv)-1]
}
for idx := 0; idx < len(kv); idx += 2 {
md[textproto.CanonicalMIMEHeaderKey(kv[idx])] = kv[idx+1]
}
} }
// Del is used to remove value from metadata // Del is used to remove value from metadata
func (md Metadata) Del(key string) { func (md Metadata) Del(keys ...string) {
// fast path for _, key := range keys {
delete(md, key) // fast path
// slow path delete(md, key)
delete(md, textproto.CanonicalMIMEHeaderKey(key)) // slow path
delete(md, textproto.CanonicalMIMEHeaderKey(key))
}
} }
// Copy makes a copy of the metadata // Copy makes a copy of the metadata
@ -129,13 +136,6 @@ func Pairs(kv ...string) (Metadata, bool) {
return nil, false return nil, false
} }
md := New(len(kv) / 2) md := New(len(kv) / 2)
var k string md.Set(kv...)
for i, v := range kv {
if i%2 == 0 {
k = v
continue
}
md.Set(k, v)
}
return md, true return md, true
} }

View File

@ -5,6 +5,21 @@ import (
"testing" "testing"
) )
func TestMetadataSetMultiple(t *testing.T) {
md := New(4)
md.Set("key1", "val1", "key2", "val2", "key3")
if v, ok := md.Get("key1"); !ok || v != "val1" {
t.Fatalf("invalid kv %#+v", md)
}
if v, ok := md.Get("key2"); !ok || v != "val2" {
t.Fatalf("invalid kv %#+v", md)
}
if _, ok := md.Get("key3"); ok {
t.Fatalf("invalid kv %#+v", md)
}
}
func TestAppend(t *testing.T) { func TestAppend(t *testing.T) {
ctx := context.Background() ctx := context.Background()
ctx = AppendIncomingContext(ctx, "key1", "val1", "key2", "val2") ctx = AppendIncomingContext(ctx, "key1", "val1", "key2", "val2")

View File

@ -5,6 +5,24 @@ import (
"testing" "testing"
) )
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewMeter())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestFromContext(t *testing.T) { func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), meterKey{}, NewMeter()) ctx := context.WithValue(context.TODO(), meterKey{}, NewMeter())

View File

@ -5,6 +5,24 @@ import (
"testing" "testing"
) )
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewRegister())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestFromContext(t *testing.T) { func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), registerKey{}, NewRegister()) ctx := context.WithValue(context.TODO(), registerKey{}, NewRegister())

View File

@ -5,6 +5,24 @@ import (
"testing" "testing"
) )
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewRouter())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestFromContext(t *testing.T) { func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), routerKey{}, NewRouter()) ctx := context.WithValue(context.TODO(), routerKey{}, NewRouter())

View File

@ -5,6 +5,24 @@ import (
"testing" "testing"
) )
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewServer())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestFromContext(t *testing.T) { func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), serverKey{}, NewServer()) ctx := context.WithValue(context.TODO(), serverKey{}, NewServer())

View File

@ -5,6 +5,24 @@ import (
"testing" "testing"
) )
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewStore())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestFromContext(t *testing.T) { func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), storeKey{}, NewStore()) ctx := context.WithValue(context.TODO(), storeKey{}, NewStore())

View File

@ -5,6 +5,24 @@ import (
"testing" "testing"
) )
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewTracer())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestFromContext(t *testing.T) { func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), tracerKey{}, NewTracer()) ctx := context.WithValue(context.TODO(), tracerKey{}, NewTracer())