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 }