go.uber.org/yarpc@v1.72.1/encoding/json/observability_test.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package json_test
    22  
    23  import (
    24  	"context"
    25  	"fmt"
    26  	nethttp "net/http"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  	"go.uber.org/net/metrics"
    33  	"go.uber.org/yarpc"
    34  	"go.uber.org/yarpc/api/transport"
    35  	"go.uber.org/yarpc/encoding/json"
    36  	"go.uber.org/yarpc/internal/testutils"
    37  	"go.uber.org/yarpc/transport/http"
    38  	"go.uber.org/zap"
    39  	"go.uber.org/zap/zapcore"
    40  )
    41  
    42  const (
    43  	_serverName = "json-server"
    44  	_clientName = "json-client"
    45  )
    46  
    47  func TestJsonMetrics(t *testing.T) {
    48  	client, clientMetricsRoot, serverMetricsRoot, cleanup := initClientAndServer(t)
    49  	defer cleanup()
    50  
    51  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    52  	defer cancel()
    53  
    54  	var resp requestResponse
    55  	err := client.Call(ctx, "test", &requestResponse{Val: "test body"}, &resp)
    56  	assert.NoError(t, err, "unexpected call error")
    57  
    58  	t.Run("counters", func(t *testing.T) {
    59  		wantCounters := []testutils.CounterAssertion{
    60  			{Name: "calls", Value: 1},
    61  			{Name: "panics"},
    62  			{Name: "successes", Value: 1},
    63  		}
    64  
    65  		testutils.AssertClientAndServerCounters(t, wantCounters, clientMetricsRoot, serverMetricsRoot)
    66  	})
    67  	t.Run("inbound histograms", func(t *testing.T) {
    68  		wantHistograms := []testutils.HistogramAssertion{
    69  			{Name: "caller_failure_latency_ms"},
    70  			{Name: "request_payload_size_bytes", Value: []int64{32}},
    71  			{Name: "response_payload_size_bytes", Value: []int64{32}},
    72  			{Name: "server_failure_latency_ms"},
    73  			{Name: "success_latency_ms", IgnoreValueCompare: true, ValueLength: 1},
    74  			{Name: "timeout_ttl_ms"},
    75  			{Name: "ttl_ms", Value: []int64{1000}},
    76  		}
    77  		testutils.AssertClientAndServerHistograms(t, wantHistograms, clientMetricsRoot, serverMetricsRoot)
    78  	})
    79  }
    80  
    81  func initClientAndServer(t *testing.T) (json.Client, *metrics.Root, *metrics.Root, func()) {
    82  	clientMetricsRoot, serverMetricsRoot := metrics.New(), metrics.New()
    83  
    84  	serverAddr, cleanupServer := newServer(t, zapcore.NewNopCore(), serverMetricsRoot)
    85  	client, cleanupClient := newClient(t, serverAddr, zapcore.NewNopCore(), clientMetricsRoot)
    86  
    87  	return client, clientMetricsRoot, serverMetricsRoot, func() {
    88  		cleanupServer()
    89  		cleanupClient()
    90  	}
    91  }
    92  
    93  type requestResponse struct {
    94  	Val string
    95  }
    96  
    97  func newServer(t *testing.T, loggerCore zapcore.Core, metricsRoot *metrics.Root) (addr string, cleanup func()) {
    98  	mux := nethttp.NewServeMux()
    99  	mux.HandleFunc("/test", func(w nethttp.ResponseWriter, r *nethttp.Request) {
   100  		w.Write([]byte("healthy"))
   101  	})
   102  	inbound := http.NewTransport().NewInbound("127.0.0.1:0")
   103  	dispatcher := yarpc.NewDispatcher(yarpc.Config{
   104  		Name:     _serverName,
   105  		Inbounds: yarpc.Inbounds{inbound},
   106  		Logging:  yarpc.LoggingConfig{Zap: zap.New(loggerCore)},
   107  		Metrics:  yarpc.MetricsConfig{Metrics: metricsRoot.Scope()},
   108  	})
   109  	require.NoError(t, dispatcher.Start(), "could not start server dispatcher")
   110  	dispatcher.Register(json.Procedure("test", func(ctx context.Context, req *requestResponse) (*requestResponse, error) {
   111  		return &requestResponse{Val: req.Val}, nil
   112  	}))
   113  	addr = inbound.Addr().String()
   114  	cleanup = func() { assert.NoError(t, dispatcher.Stop(), "could not stop dispatcher") }
   115  	return addr, cleanup
   116  }
   117  
   118  func newClient(t *testing.T, serverAddr string, loggerCore zapcore.Core, metricsRoot *metrics.Root) (json.Client, func()) {
   119  	dispatcher := yarpc.NewDispatcher(yarpc.Config{
   120  		Name: _clientName,
   121  		Outbounds: map[string]transport.Outbounds{
   122  			_serverName: {
   123  				ServiceName: _serverName,
   124  				Unary:       http.NewTransport().NewSingleOutbound(fmt.Sprintf("http://%s", serverAddr)),
   125  			},
   126  		},
   127  		Logging: yarpc.LoggingConfig{Zap: zap.New(loggerCore)},
   128  		Metrics: yarpc.MetricsConfig{Metrics: metricsRoot.Scope()},
   129  	})
   130  
   131  	client := json.New(dispatcher.ClientConfig(_serverName))
   132  	require.NoError(t, dispatcher.Start(), "could not start client dispatcher")
   133  
   134  	cleanup := func() { assert.NoError(t, dispatcher.Stop(), "could not stop dispatcher") }
   135  	return client, cleanup
   136  }