github.com/goravel/framework@v1.13.9/grpc/application_test.go (about)

     1  package grpc
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"google.golang.org/grpc"
    13  	"google.golang.org/grpc/metadata"
    14  
    15  	configmock "github.com/goravel/framework/contracts/config/mocks"
    16  )
    17  
    18  type contextKey int
    19  
    20  const server contextKey = 0
    21  const client contextKey = 1
    22  
    23  func TestRun(t *testing.T) {
    24  	var (
    25  		app        *Application
    26  		mockConfig *configmock.Config
    27  		name       = "test"
    28  	)
    29  
    30  	beforeEach := func() {
    31  		mockConfig = &configmock.Config{}
    32  
    33  		app = NewApplication(mockConfig)
    34  		app.UnaryServerInterceptors([]grpc.UnaryServerInterceptor{
    35  			serverInterceptor,
    36  		})
    37  		app.UnaryClientInterceptorGroups(map[string][]grpc.UnaryClientInterceptor{
    38  			"test": {
    39  				clientInterceptor,
    40  			},
    41  		})
    42  		RegisterTestServiceServer(app.Server(), &TestController{})
    43  	}
    44  
    45  	tests := []struct {
    46  		name      string
    47  		setup     func()
    48  		expectErr bool
    49  	}{
    50  		{
    51  			name: "success",
    52  			setup: func() {
    53  				host := "127.0.0.1:3030"
    54  				mockConfig.On("GetString", fmt.Sprintf("grpc.clients.%s.host", name)).Return(host).Once()
    55  				mockConfig.On("Get", fmt.Sprintf("grpc.clients.%s.interceptors", name)).Return([]string{"test"}).Once()
    56  
    57  				go func() {
    58  					assert.Nil(t, app.Run(host))
    59  				}()
    60  
    61  				time.Sleep(1 * time.Second)
    62  				client, err := app.Client(context.Background(), name)
    63  				assert.Nil(t, err)
    64  				testServiceClient := NewTestServiceClient(client)
    65  				res, err := testServiceClient.Get(context.Background(), &TestRequest{
    66  					Name: "success",
    67  				})
    68  
    69  				assert.Equal(t, &TestResponse{Code: http.StatusOK, Message: "Goravel: server: goravel-server, client: goravel-client"}, res)
    70  				assert.Nil(t, err)
    71  			},
    72  		},
    73  		{
    74  			name: "success when host with port",
    75  			setup: func() {
    76  				mockConfig.On("GetString", "grpc.host").Return("127.0.0.1:3032").Once()
    77  				go func() {
    78  					assert.Nil(t, app.Run())
    79  				}()
    80  				time.Sleep(1 * time.Second)
    81  			},
    82  		},
    83  		{
    84  			name: "error when host is empty",
    85  			setup: func() {
    86  				mockConfig.On("GetString", "grpc.host").Return("").Once()
    87  				assert.EqualError(t, app.Run(), "host can't be empty")
    88  			},
    89  		},
    90  		{
    91  			name: "error when port is empty",
    92  			setup: func() {
    93  				mockConfig.On("GetString", "grpc.host").Return("127.0.0.1").Once()
    94  				mockConfig.On("GetString", "grpc.port").Return("").Once()
    95  				assert.EqualError(t, app.Run(), "port can't be empty")
    96  			},
    97  		},
    98  		{
    99  			name: "error when request name = error",
   100  			setup: func() {
   101  				host := "127.0.0.1:3033"
   102  				mockConfig.On("GetString", fmt.Sprintf("grpc.clients.%s.host", name)).Return(host).Once()
   103  				mockConfig.On("Get", fmt.Sprintf("grpc.clients.%s.interceptors", name)).Return([]string{"test"}).Once()
   104  
   105  				go func() {
   106  					assert.Nil(t, app.Run(host))
   107  				}()
   108  
   109  				time.Sleep(1 * time.Second)
   110  				client, err := app.Client(context.Background(), "test")
   111  				assert.Nil(t, err)
   112  				testServiceClient := NewTestServiceClient(client)
   113  				res, err := testServiceClient.Get(context.Background(), &TestRequest{
   114  					Name: "error",
   115  				})
   116  
   117  				assert.Nil(t, res)
   118  				assert.EqualError(t, err, "rpc error: code = Unknown desc = error")
   119  			},
   120  		},
   121  	}
   122  
   123  	for _, test := range tests {
   124  		t.Run(test.name, func(t *testing.T) {
   125  			beforeEach()
   126  			test.setup()
   127  			mockConfig.AssertExpectations(t)
   128  		})
   129  	}
   130  }
   131  
   132  func TestClient(t *testing.T) {
   133  	var (
   134  		app        *Application
   135  		mockConfig *configmock.Config
   136  		name       = "user"
   137  		host       = "127.0.0.1:3030"
   138  	)
   139  
   140  	beforeEach := func() {
   141  		mockConfig = &configmock.Config{}
   142  		app = NewApplication(mockConfig)
   143  	}
   144  
   145  	tests := []struct {
   146  		name      string
   147  		setup     func()
   148  		expectErr bool
   149  	}{
   150  		{
   151  			name: "success",
   152  			setup: func() {
   153  				mockConfig.On("GetString", fmt.Sprintf("grpc.clients.%s.host", name)).Return(host).Once()
   154  				mockConfig.On("Get", fmt.Sprintf("grpc.clients.%s.interceptors", name)).Return([]string{"trace"}).Once()
   155  				app.UnaryClientInterceptorGroups(map[string][]grpc.UnaryClientInterceptor{
   156  					"trace": {opentracingClient},
   157  				})
   158  			},
   159  		},
   160  		{
   161  			name: "success when interceptors is empty",
   162  			setup: func() {
   163  				mockConfig.On("GetString", fmt.Sprintf("grpc.clients.%s.host", name)).Return(host).Once()
   164  				mockConfig.On("Get", fmt.Sprintf("grpc.clients.%s.interceptors", name)).Return([]string{"trace"}).Once()
   165  				app.UnaryClientInterceptorGroups(map[string][]grpc.UnaryClientInterceptor{})
   166  			},
   167  		},
   168  		{
   169  			name: "error when host is empty",
   170  			setup: func() {
   171  				mockConfig.On("GetString", fmt.Sprintf("grpc.clients.%s.host", name)).Return("").Once()
   172  			},
   173  			expectErr: true,
   174  		},
   175  		{
   176  			name: "error when host doesn't have port and port is empty",
   177  			setup: func() {
   178  				mockConfig.On("GetString", fmt.Sprintf("grpc.clients.%s.host", name)).Return("127.0.0.1").Once()
   179  				mockConfig.On("GetString", fmt.Sprintf("grpc.clients.%s.port", name)).Return("").Once()
   180  			},
   181  			expectErr: true,
   182  		},
   183  		{
   184  			name: "error when interceptors isn't []string",
   185  			setup: func() {
   186  				mockConfig.On("GetString", fmt.Sprintf("grpc.clients.%s.host", name)).Return(host).Once()
   187  				mockConfig.On("Get", fmt.Sprintf("grpc.clients.%s.interceptors", name)).Return("trace").Once()
   188  			},
   189  			expectErr: true,
   190  		},
   191  	}
   192  
   193  	for _, test := range tests {
   194  		t.Run(test.name, func(t *testing.T) {
   195  			beforeEach()
   196  			test.setup()
   197  			client, err := app.Client(context.Background(), name)
   198  			if !test.expectErr {
   199  				assert.NotNil(t, client, test.name)
   200  			}
   201  			assert.Equal(t, test.expectErr, err != nil, test.name)
   202  			mockConfig.AssertExpectations(t)
   203  		})
   204  	}
   205  }
   206  
   207  func opentracingClient(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
   208  	return nil
   209  }
   210  
   211  func serverInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
   212  	md, ok := metadata.FromIncomingContext(ctx)
   213  	if !ok {
   214  		md = metadata.New(nil)
   215  	}
   216  
   217  	ctx = context.WithValue(ctx, server, "goravel-server")
   218  	if len(md["client"]) > 0 {
   219  		ctx = context.WithValue(ctx, client, md["client"][0])
   220  	}
   221  
   222  	return handler(ctx, req)
   223  }
   224  
   225  func clientInterceptor(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
   226  	md, ok := metadata.FromOutgoingContext(ctx)
   227  	if !ok {
   228  		md = metadata.New(nil)
   229  	} else {
   230  		md = md.Copy()
   231  	}
   232  
   233  	md["client"] = []string{"goravel-client"}
   234  
   235  	if err := invoker(metadata.NewOutgoingContext(ctx, md), method, req, reply, cc, opts...); err != nil {
   236  		return err
   237  	}
   238  
   239  	return nil
   240  }
   241  
   242  type TestController struct {
   243  }
   244  
   245  func (r *TestController) Get(ctx context.Context, req *TestRequest) (*TestResponse, error) {
   246  	if req.GetName() == "success" {
   247  		return &TestResponse{
   248  			Code:    http.StatusOK,
   249  			Message: fmt.Sprintf("Goravel: server: %s, client: %s", ctx.Value(server), ctx.Value(client)),
   250  		}, nil
   251  	} else {
   252  		return nil, errors.New("error")
   253  	}
   254  }