From 46da092899603ddb74746d7e1ae8e6ca26baa78e Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Sat, 16 Apr 2022 16:35:46 +0300 Subject: [PATCH 1/7] client: implement Call and Stream methods for noop Signed-off-by: Vasiliy Tolstov --- client/noop.go | 262 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) diff --git a/client/noop.go b/client/noop.go index c427f334..bdded98e 100644 --- a/client/noop.go +++ b/client/noop.go @@ -2,6 +2,8 @@ package client import ( "context" + "fmt" + "time" "go.unistack.org/micro/v3/broker" "go.unistack.org/micro/v3/codec" @@ -181,6 +183,133 @@ func (n *noopClient) String() string { } func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error { + // make a copy of call opts + callOpts := n.opts.CallOptions + for _, opt := range opts { + opt(&callOpts) + } + + // check if we already have a deadline + d, ok := ctx.Deadline() + if !ok { + var cancel context.CancelFunc + // no deadline so we create a new one + ctx, cancel = context.WithTimeout(ctx, callOpts.RequestTimeout) + defer cancel() + } else { + // got a deadline so no need to setup context + // but we need to set the timeout we pass along + opt := WithRequestTimeout(time.Until(d)) + opt(&callOpts) + } + + // should we noop right here? + select { + case <-ctx.Done(): + return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) + default: + } + + // make copy of call method + hcall := n.call + + // wrap the call in reverse + for i := len(callOpts.CallWrappers); i > 0; i-- { + hcall = callOpts.CallWrappers[i-1](hcall) + } + + // use the router passed as a call option, or fallback to the rpc clients router + if callOpts.Router == nil { + callOpts.Router = n.opts.Router + } + + if callOpts.Selector == nil { + callOpts.Selector = n.opts.Selector + } + + // inject proxy address + // TODO: don't even bother using Lookup/Select in this case + if len(n.opts.Proxy) > 0 { + callOpts.Address = []string{n.opts.Proxy} + } + + // 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 + } + + // return errors.New("go.micro.client", "request timeout", 408) + call := func(i int) error { + // call backoff first. Someone may want an initial start delay + t, err := callOpts.Backoff(ctx, req, i) + if err != nil { + return errors.InternalServerError("go.micro.client", err.Error()) + } + + // only sleep if greater than 0 + if t.Seconds() > 0 { + time.Sleep(t) + } + + node := next() + + // make the call + err = hcall(ctx, node, req, rsp, callOpts) + // record the result of the call to inform future routing decisions + if verr := n.opts.Selector.Record(node, err); verr != nil { + return verr + } + + // try and transform the error to a go-micro error + if verr, ok := err.(*errors.Error); ok { + return verr + } + + return err + } + + ch := make(chan error, callOpts.Retries) + var gerr error + + for i := 0; i <= callOpts.Retries; i++ { + go func() { + ch <- call(i) + }() + + select { + case <-ctx.Done(): + return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) + case err := <-ch: + // if the call succeeded lets bail early + if err == nil { + return nil + } + + retry, rerr := callOpts.Retry(ctx, req, i, err) + if rerr != nil { + return rerr + } + + if !retry { + return err + } + + gerr = err + } + } + + return gerr +} + +func (n *noopClient) call(ctx context.Context, addr string, req Request, rsp interface{}, opts CallOptions) error { return nil } @@ -194,6 +323,139 @@ func (n *noopClient) NewMessage(topic string, msg interface{}, opts ...MessageOp } func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) { + // make a copy of call opts + callOpts := n.opts.CallOptions + for _, o := range opts { + o(&callOpts) + } + + // check if we already have a deadline + d, ok := ctx.Deadline() + if !ok && callOpts.StreamTimeout > time.Duration(0) { + var cancel context.CancelFunc + // no deadline so we create a new one + ctx, cancel = context.WithTimeout(ctx, callOpts.StreamTimeout) + defer cancel() + } else { + // got a deadline so no need to setup context + // but we need to set the timeout we pass along + o := WithStreamTimeout(time.Until(d)) + o(&callOpts) + } + + // should we noop right here? + select { + case <-ctx.Done(): + return nil, errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) + default: + } + + /* + // make copy of call method + hstream := h.stream + // wrap the call in reverse + for i := len(callOpts.CallWrappers); i > 0; i-- { + hstream = callOpts.CallWrappers[i-1](hstream) + } + */ + + // use the router passed as a call option, or fallback to the rpc clients router + if callOpts.Router == nil { + callOpts.Router = n.opts.Router + } + + if callOpts.Selector == nil { + callOpts.Selector = n.opts.Selector + } + + // inject proxy address + // TODO: don't even bother using Lookup/Select in this case + if len(n.opts.Proxy) > 0 { + callOpts.Address = []string{n.opts.Proxy} + } + + // 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 + } + + call := func(i int) (Stream, error) { + // call backoff first. Someone may want an initial start delay + t, cerr := callOpts.Backoff(ctx, req, i) + if cerr != nil { + return nil, errors.InternalServerError("go.micro.client", cerr.Error()) + } + + // only sleep if greater than 0 + if t.Seconds() > 0 { + time.Sleep(t) + } + + node := next() + + stream, cerr := n.stream(ctx, node, req, callOpts) + + // record the result of the call to inform future routing decisions + if verr := n.opts.Selector.Record(node, cerr); verr != nil { + return nil, verr + } + + // try and transform the error to a go-micro error + if verr, ok := cerr.(*errors.Error); ok { + return nil, verr + } + + return stream, cerr + } + + type response struct { + stream Stream + err error + } + + ch := make(chan response, callOpts.Retries) + var grr error + + for i := 0; i <= callOpts.Retries; i++ { + go func() { + s, cerr := call(i) + ch <- response{s, cerr} + }() + + select { + case <-ctx.Done(): + return nil, errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) + case rsp := <-ch: + // if the call succeeded lets bail early + if rsp.err == nil { + return rsp.stream, nil + } + + retry, rerr := callOpts.Retry(ctx, req, i, err) + if rerr != nil { + return nil, rerr + } + + if !retry { + return nil, rsp.err + } + + grr = rsp.err + } + } + + return nil, grr +} + +func (n *noopClient) stream(ctx context.Context, addr string, req Request, opts CallOptions) (Stream, error) { return &noopStream{}, nil } From aa2b5ddaad12cf60315fdd194c349f64b39f014a Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Sat, 16 Apr 2022 16:36:15 +0300 Subject: [PATCH 2/7] client: add backoff tests Signed-off-by: Vasiliy Tolstov --- client/backoff.go | 2 +- client/backoff_test.go | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/client/backoff.go b/client/backoff.go index d548b724..7eaacf7e 100644 --- a/client/backoff.go +++ b/client/backoff.go @@ -19,7 +19,7 @@ func BackoffExp(_ context.Context, _ Request, attempts int) (time.Duration, erro // BackoffInterval specifies randomization interval for backoff func func BackoffInterval(min time.Duration, max time.Duration) BackoffFunc { return func(_ context.Context, _ Request, attempts int) (time.Duration, error) { - td := time.Duration(time.Duration(math.Pow(float64(attempts), math.E)) * time.Millisecond * 100) + td := time.Duration(math.Pow(float64(attempts), math.E)) * time.Millisecond * 100 if td < min { return min, nil } else if td > max { diff --git a/client/backoff_test.go b/client/backoff_test.go index 2ab854f7..59bb7a41 100644 --- a/client/backoff_test.go +++ b/client/backoff_test.go @@ -6,7 +6,7 @@ import ( "time" ) -func TestBackoff(t *testing.T) { +func TestBackoffExp(t *testing.T) { results := []time.Duration{ 0 * time.Second, 100 * time.Millisecond, @@ -32,3 +32,25 @@ func TestBackoff(t *testing.T) { } } } + +func TestBackoffInterval(t *testing.T) { + min := 100 * time.Millisecond + max := 300 * time.Millisecond + + r := &testRequest{ + service: "test", + method: "test", + } + + fn := BackoffInterval(min, max) + for i := 0; i < 5; i++ { + d, err := fn(context.TODO(), r, i) + if err != nil { + t.Fatal(err) + } + + if d < min || d > max { + t.Fatalf("Expected %v < %v < %v", min, d, max) + } + } +} From 955953b519fd053d7052a56e83511c74e1afaf21 Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Sat, 16 Apr 2022 16:36:34 +0300 Subject: [PATCH 3/7] client: fix lint Signed-off-by: Vasiliy Tolstov --- client/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/client.go b/client/client.go index 71bedfd9..57a4f2b7 100644 --- a/client/client.go +++ b/client/client.go @@ -11,7 +11,7 @@ import ( var ( // DefaultClient is the global default client - DefaultClient Client = NewClient() + DefaultClient = NewClient() // DefaultContentType is the default content-type if not specified DefaultContentType = "application/json" // DefaultBackoff is the default backoff function for retries (minimum 10 millisecond and maximum 5 second) From 00439e23f3d95f993fc506c649bea6285245ddf8 Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Sat, 16 Apr 2022 16:36:43 +0300 Subject: [PATCH 4/7] add client call options tests Signed-off-by: Vasiliy Tolstov --- client/client_call_options_test.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 client/client_call_options_test.go diff --git a/client/client_call_options_test.go b/client/client_call_options_test.go new file mode 100644 index 00000000..2cffdf36 --- /dev/null +++ b/client/client_call_options_test.go @@ -0,0 +1,26 @@ +package client + +import ( + "context" + "testing" + "time" +) + +func TestNewClientCallOptions(t *testing.T) { + var flag bool + w := func(fn CallFunc) CallFunc { + flag = true + return fn + } + c := NewClientCallOptions(NewClient(), + WithAddress("127.0.0.1"), + WithCallWrapper(w), + WithRequestTimeout(1*time.Millisecond), + WithRetries(0), + WithBackoff(BackoffInterval(10*time.Millisecond, 100*time.Millisecond)), + ) + _ = c.Call(context.TODO(), c.NewRequest("service", "endpoint", nil), nil) + if !flag { + t.Fatalf("NewClientCallOptions not works") + } +} From fb4d7471978afa4692d2f1278a2e07a7507e321f Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Sun, 17 Apr 2022 11:41:49 +0300 Subject: [PATCH 5/7] server: fix race Signed-off-by: Vasiliy Tolstov --- server/noop.go | 5 +++-- server/server.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/server/noop.go b/server/noop.go index bd4d3f69..a89c03d1 100644 --- a/server/noop.go +++ b/server/noop.go @@ -281,14 +281,15 @@ func (n *noopServer) Deregister() error { if sb.Options().Context != nil { cx = sb.Options().Context } - + + ncx := cx wg.Add(1) go func(s broker.Subscriber) { defer wg.Done() if config.Logger.V(logger.InfoLevel) { config.Logger.Infof(n.opts.Context, "unsubscribing from topic: %s", s.Topic()) } - if err := s.Unsubscribe(cx); err != nil { + if err := s.Unsubscribe(ncx); err != nil { if config.Logger.V(logger.ErrorLevel) { config.Logger.Errorf(n.opts.Context, "unsubscribing from topic: %s err: %v", s.Topic(), err) } diff --git a/server/server.go b/server/server.go index 9dfa9b58..ef710d79 100644 --- a/server/server.go +++ b/server/server.go @@ -11,7 +11,7 @@ import ( ) // DefaultServer default server -var DefaultServer Server = NewServer() +var DefaultServer = NewServer() var ( // DefaultAddress will be used if no address passed, use secure localhost From 11b614f2df0c479514bb1eb4a4fbde8cd4863a2a Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Sun, 17 Apr 2022 13:10:49 +0300 Subject: [PATCH 6/7] client: add retry func tests Signed-off-by: Vasiliy Tolstov --- client/retry_test.go | 70 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 client/retry_test.go diff --git a/client/retry_test.go b/client/retry_test.go new file mode 100644 index 00000000..621c427d --- /dev/null +++ b/client/retry_test.go @@ -0,0 +1,70 @@ +package client + +import ( + "context" + "fmt" + "testing" + + "go.unistack.org/micro/v3/errors" +) + +func TestRetryAlways(t *testing.T) { + tests := []error{ + nil, + errors.InternalServerError("test", "%s", "test"), + fmt.Errorf("test"), + } + + for _, e := range tests { + ok, er := RetryAlways(context.TODO(), nil, 1, e) + if !ok || er != nil { + t.Fatal("RetryAlways not works properly") + } + } +} + +func TestRetryNever(t *testing.T) { + tests := []error{ + nil, + errors.InternalServerError("test", "%s", "test"), + fmt.Errorf("test"), + } + + for _, e := range tests { + ok, er := RetryNever(context.TODO(), nil, 1, e) + if ok || er != nil { + t.Fatal("RetryNever not works properly") + } + } +} + +func TestRetryOnError(t *testing.T) { + tests := []error{ + fmt.Errorf("test"), + errors.NotFound("test", "%s", "test"), + errors.Timeout("test", "%s", "test"), + } + + for i, e := range tests { + ok, er := RetryOnError(context.TODO(), nil, 1, e) + if i == 2 && (!ok || er != nil) { + t.Fatal("RetryOnError not works properly") + } + } +} + +func TestRetryOnErrors(t *testing.T) { + tests := []error{ + fmt.Errorf("test"), + errors.NotFound("test", "%s", "test"), + errors.Timeout("test", "%s", "test"), + } + + fn := RetryOnErrors(404) + for i, e := range tests { + ok, er := fn(context.TODO(), nil, 1, e) + if i == 1 && (!ok || er != nil) { + t.Fatal("RetryOnErrors not works properly") + } + } +} From 8b1a579c9d6cc54b9746f92477a06e49f9ebd97b Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Sun, 17 Apr 2022 16:25:42 +0300 Subject: [PATCH 7/7] add context tests Signed-off-by: Vasiliy Tolstov --- broker/context_test.go | 57 ++++++++++++++++++ client/context_test.go | 57 ++++++++++++++++++ codec/context_test.go | 35 +++++++++++ config/context_test.go | 73 +++++++++++++++++++++++ context_test.go | 24 ++++++++ flow/context_test.go | 35 +++++++++++ logger/context_test.go | 35 +++++++++++ metadata/context_test.go | 125 +++++++++++++++++++++++++++++++++++++++ meter/context_test.go | 36 +++++++++++ register/context_test.go | 36 +++++++++++ router/context_test.go | 36 +++++++++++ server/context_test.go | 46 ++++++++++++++ server/noop.go | 2 +- server/noop_test.go | 2 + store/context_test.go | 36 +++++++++++ tracer/context_test.go | 25 ++++++++ 16 files changed, 659 insertions(+), 1 deletion(-) create mode 100644 broker/context_test.go create mode 100644 client/context_test.go create mode 100644 codec/context_test.go create mode 100644 config/context_test.go create mode 100644 context_test.go create mode 100644 flow/context_test.go create mode 100644 logger/context_test.go create mode 100644 metadata/context_test.go create mode 100644 meter/context_test.go create mode 100644 register/context_test.go create mode 100644 router/context_test.go create mode 100644 server/context_test.go create mode 100644 store/context_test.go create mode 100644 tracer/context_test.go diff --git a/broker/context_test.go b/broker/context_test.go new file mode 100644 index 00000000..65337cfe --- /dev/null +++ b/broker/context_test.go @@ -0,0 +1,57 @@ +package broker + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), brokerKey{}, NewBroker()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewBroker()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} + +func TestSetSubscribeOption(t *testing.T) { + type key struct{} + o := SetSubscribeOption(key{}, "test") + opts := &SubscribeOptions{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetSubscribeOption not works") + } +} + +func TestSetPublishOption(t *testing.T) { + type key struct{} + o := SetPublishOption(key{}, "test") + opts := &PublishOptions{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetPublishOption not works") + } +} + +func TestSetOption(t *testing.T) { + type key struct{} + o := SetOption(key{}, "test") + opts := &Options{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetOption not works") + } +} diff --git a/client/context_test.go b/client/context_test.go new file mode 100644 index 00000000..3f29ace5 --- /dev/null +++ b/client/context_test.go @@ -0,0 +1,57 @@ +package client + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), clientKey{}, NewClient()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewClient()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} + +func TestSetPublishOption(t *testing.T) { + type key struct{} + o := SetPublishOption(key{}, "test") + opts := &PublishOptions{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetPublishOption not works") + } +} + +func TestSetCallOption(t *testing.T) { + type key struct{} + o := SetCallOption(key{}, "test") + opts := &CallOptions{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetCallOption not works") + } +} + +func TestSetOption(t *testing.T) { + type key struct{} + o := SetOption(key{}, "test") + opts := &Options{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetOption not works") + } +} diff --git a/codec/context_test.go b/codec/context_test.go new file mode 100644 index 00000000..1e9e7c5c --- /dev/null +++ b/codec/context_test.go @@ -0,0 +1,35 @@ +package codec + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), codecKey{}, NewCodec()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewCodec()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} + +func TestSetOption(t *testing.T) { + type key struct{} + o := SetOption(key{}, "test") + opts := &Options{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetOption not works") + } +} diff --git a/config/context_test.go b/config/context_test.go new file mode 100644 index 00000000..3d11c5aa --- /dev/null +++ b/config/context_test.go @@ -0,0 +1,73 @@ +package config + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), configKey{}, NewConfig()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewConfig()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} + +func TestSetOption(t *testing.T) { + type key struct{} + o := SetOption(key{}, "test") + opts := &Options{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetOption not works") + } +} + + +func TestSetSaveOption(t *testing.T) { + type key struct{} + o := SetSaveOption(key{}, "test") + opts := &SaveOptions{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetSaveOption not works") + } +} + + + +func TestSetLoadOption(t *testing.T) { + type key struct{} + o := SetLoadOption(key{}, "test") + opts := &LoadOptions{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetLoadOption not works") + } +} + + + +func TestSetWatchOption(t *testing.T) { + type key struct{} + o := SetWatchOption(key{}, "test") + opts := &WatchOptions{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetWatchOption not works") + } +} diff --git a/context_test.go b/context_test.go new file mode 100644 index 00000000..2329568d --- /dev/null +++ b/context_test.go @@ -0,0 +1,24 @@ +package micro + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), serviceKey{}, NewService()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewService()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} diff --git a/flow/context_test.go b/flow/context_test.go new file mode 100644 index 00000000..b767789f --- /dev/null +++ b/flow/context_test.go @@ -0,0 +1,35 @@ +package flow + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), flowKey{}, NewFlow()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewFlow()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} + +func TestSetOption(t *testing.T) { + type key struct{} + o := SetOption(key{}, "test") + opts := &Options{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetOption not works") + } +} diff --git a/logger/context_test.go b/logger/context_test.go new file mode 100644 index 00000000..20ed4131 --- /dev/null +++ b/logger/context_test.go @@ -0,0 +1,35 @@ +package logger + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), loggerKey{}, NewLogger()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewLogger()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} + +func TestSetOption(t *testing.T) { + type key struct{} + o := SetOption(key{}, "test") + opts := &Options{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetOption not works") + } +} diff --git a/metadata/context_test.go b/metadata/context_test.go new file mode 100644 index 00000000..a07dfd13 --- /dev/null +++ b/metadata/context_test.go @@ -0,0 +1,125 @@ +package metadata + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), mdKey{}, &rawMetadata{New(0)}) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), New(0)) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} + +func TestFromIncomingContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), mdIncomingKey{}, &rawMetadata{New(0)}) + + c, ok := FromIncomingContext(ctx) + if c == nil || !ok { + t.Fatal("FromIncomingContext not works") + } +} + +func TestFromOutgoingContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), mdOutgoingKey{}, &rawMetadata{New(0)}) + + c, ok := FromOutgoingContext(ctx) + if c == nil || !ok { + t.Fatal("FromOutgoingContext not works") + } +} + + +func TestSetIncomingContext(t *testing.T) { + md := New(1) + md.Set("key", "val") + ctx := context.WithValue(context.TODO(), mdIncomingKey{}, &rawMetadata{}) + if !SetIncomingContext(ctx, md) { + t.Fatal("SetIncomingContext not works") + } + md, ok := FromIncomingContext(ctx) + if md == nil || !ok { + t.Fatal("SetIncomingContext not works") + } else if v, ok := md.Get("key"); !ok || v != "val" { + t.Fatal("SetIncomingContext not works") + } +} + +func TestSetOutgoingContext(t *testing.T) { + md := New(1) + md.Set("key", "val") + ctx := context.WithValue(context.TODO(), mdOutgoingKey{}, &rawMetadata{}) + if !SetOutgoingContext(ctx, md) { + t.Fatal("SetOutgoingContext not works") + } + md, ok := FromOutgoingContext(ctx) + if md == nil || !ok { + t.Fatal("SetOutgoingContext not works") + } else if v, ok := md.Get("key"); !ok || v != "val" { + t.Fatal("SetOutgoingContext not works") + } +} + + +func TestNewIncomingContext(t *testing.T) { + md := New(1) + md.Set("key", "val") + ctx := NewIncomingContext(context.TODO(), md) + + c, ok := FromIncomingContext(ctx) + if c == nil || !ok { + t.Fatal("NewIncomingContext not works") + } +} + +func TestNewOutgoingContext(t *testing.T) { + md := New(1) + md.Set("key", "val") + ctx := NewOutgoingContext(context.TODO(), md) + + c, ok := FromOutgoingContext(ctx) + if c == nil || !ok { + t.Fatal("NewOutgoingContext not works") + } +} + + +func TestAppendIncomingContext(t *testing.T) { + md := New(1) + md.Set("key1", "val1") + ctx := AppendIncomingContext(context.TODO(), "key2","val2") + + nmd, ok := FromIncomingContext(ctx) + if nmd == nil || !ok { + t.Fatal("AppendIncomingContext not works") + } + if v, ok := nmd.Get("key2"); !ok || v != "val2"{ + t.Fatal("AppendIncomingContext not works") + } +} + +func TestAppendOutgoingContext(t *testing.T) { + md := New(1) + md.Set("key1", "val1") + ctx := AppendOutgoingContext(context.TODO(), "key2","val2") + + nmd, ok := FromOutgoingContext(ctx) + if nmd == nil || !ok { + t.Fatal("AppendOutgoingContext not works") + } + if v, ok := nmd.Get("key2"); !ok || v != "val2"{ + t.Fatal("AppendOutgoingContext not works") + } +} \ No newline at end of file diff --git a/meter/context_test.go b/meter/context_test.go new file mode 100644 index 00000000..3bca807b --- /dev/null +++ b/meter/context_test.go @@ -0,0 +1,36 @@ +package meter + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), meterKey{}, NewMeter()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewMeter()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} + +func TestSetOption(t *testing.T) { + type key struct{} + o := SetOption(key{}, "test") + opts := &Options{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetOption not works") + } +} + diff --git a/register/context_test.go b/register/context_test.go new file mode 100644 index 00000000..d35ad88d --- /dev/null +++ b/register/context_test.go @@ -0,0 +1,36 @@ +package register + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), registerKey{}, NewRegister()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewRegister()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} + +func TestSetOption(t *testing.T) { + type key struct{} + o := SetOption(key{}, "test") + opts := &Options{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetOption not works") + } +} + diff --git a/router/context_test.go b/router/context_test.go new file mode 100644 index 00000000..286a775b --- /dev/null +++ b/router/context_test.go @@ -0,0 +1,36 @@ +package router + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), routerKey{}, NewRouter()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewRouter()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} + +func TestSetOption(t *testing.T) { + type key struct{} + o := SetOption(key{}, "test") + opts := &Options{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetOption not works") + } +} + diff --git a/server/context_test.go b/server/context_test.go new file mode 100644 index 00000000..7afbdb3f --- /dev/null +++ b/server/context_test.go @@ -0,0 +1,46 @@ +package server + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), serverKey{}, NewServer()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewServer()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} + +func TestSetOption(t *testing.T) { + type key struct{} + o := SetOption(key{}, "test") + opts := &Options{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetOption not works") + } +} + +func TestSetSubscriberOption(t *testing.T) { + type key struct{} + o := SetSubscriberOption(key{}, "test") + opts := &SubscriberOptions{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetSubscriberOption not works") + } +} diff --git a/server/noop.go b/server/noop.go index a89c03d1..8334cd2e 100644 --- a/server/noop.go +++ b/server/noop.go @@ -281,7 +281,7 @@ func (n *noopServer) Deregister() error { if sb.Options().Context != nil { cx = sb.Options().Context } - + ncx := cx wg.Add(1) go func(s broker.Subscriber) { diff --git a/server/noop_test.go b/server/noop_test.go index b343e08d..bb9e74b5 100644 --- a/server/noop_test.go +++ b/server/noop_test.go @@ -10,6 +10,7 @@ import ( "go.unistack.org/micro/v3/codec" "go.unistack.org/micro/v3/metadata" "go.unistack.org/micro/v3/server" + "go.unistack.org/micro/v3/logger" ) type TestHandler struct { @@ -50,6 +51,7 @@ func TestNoopSub(t *testing.T) { t.Fatal(err) } + logger.DefaultLogger.Init(logger.WithLevel(logger.ErrorLevel)) s := server.NewServer( server.Broker(b), server.Codec("application/octet-stream", codec.NewCodec()), diff --git a/store/context_test.go b/store/context_test.go new file mode 100644 index 00000000..6e081906 --- /dev/null +++ b/store/context_test.go @@ -0,0 +1,36 @@ +package store + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(), storeKey{}, NewStore()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewStore()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} + +func TestSetOption(t *testing.T) { + type key struct{} + o := SetOption(key{}, "test") + opts := &Options{} + o(opts) + + if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { + t.Fatal("SetOption not works") + } +} + diff --git a/tracer/context_test.go b/tracer/context_test.go new file mode 100644 index 00000000..bbbf3440 --- /dev/null +++ b/tracer/context_test.go @@ -0,0 +1,25 @@ +package tracer + +import ( + "context" + "testing" +) + +func TestFromContext(t *testing.T) { + ctx := context.WithValue(context.TODO(),tracerKey{}, NewTracer()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("FromContext not works") + } +} + +func TestNewContext(t *testing.T) { + ctx := NewContext(context.TODO(), NewTracer()) + + c, ok := FromContext(ctx) + if c == nil || !ok { + t.Fatal("NewContext not works") + } +} +