github.com/yandex/pandora@v0.5.32/tests/grpc_scenario/main_test.go (about) 1 package grpcscenario 2 3 import ( 4 "context" 5 "log/slog" 6 "net" 7 "os" 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/spf13/afero" 13 "github.com/stretchr/testify/require" 14 "github.com/stretchr/testify/suite" 15 grpcscenario "github.com/yandex/pandora/components/guns/grpc/scenario" 16 "github.com/yandex/pandora/components/providers/scenario" 17 ammo "github.com/yandex/pandora/components/providers/scenario/grpc" 18 "github.com/yandex/pandora/components/providers/scenario/grpc/postprocessor" 19 _import "github.com/yandex/pandora/components/providers/scenario/import" 20 "github.com/yandex/pandora/core" 21 "github.com/yandex/pandora/core/plugin/pluginconfig" 22 "github.com/yandex/pandora/core/warmup" 23 "github.com/yandex/pandora/examples/grpc/server" 24 "go.uber.org/zap" 25 "google.golang.org/grpc" 26 "google.golang.org/grpc/reflection" 27 ) 28 29 var testOnce = &sync.Once{} 30 31 func TestGunSuite(t *testing.T) { 32 suite.Run(t, new(GunSuite)) 33 } 34 35 type GunSuite struct { 36 suite.Suite 37 grpcServer *grpc.Server 38 grpcAddress string 39 fs afero.Fs 40 srv *server.GRPCServer 41 } 42 43 func (s *GunSuite) SetupSuite() { 44 s.fs = afero.NewOsFs() 45 _import.Import(s.fs) 46 testOnce.Do(func() { 47 pluginconfig.AddHooks() 48 }) 49 logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})) 50 port := os.Getenv("PORT") // TODO: how to set free port in CI? 51 if port == "" { 52 port = "8884" 53 } 54 55 s.grpcServer = grpc.NewServer() 56 s.srv = server.NewServer(logger, time.Now().UnixNano()) 57 server.RegisterTargetServiceServer(s.grpcServer, s.srv) 58 59 reflection.Register(s.grpcServer) 60 s.grpcAddress = ":" + port 61 l, err := net.Listen("tcp", s.grpcAddress) 62 s.NoError(err) 63 64 go func() { 65 err = s.grpcServer.Serve(l) 66 s.NoError(err) 67 }() 68 } 69 70 func (s *GunSuite) SetupTest() { 71 _, err := s.srv.Reset(context.Background(), nil) 72 s.NoError(err) 73 } 74 75 func (s *GunSuite) TearDownSuite() { 76 s.grpcServer.Stop() 77 } 78 79 func (s *GunSuite) Test_Scenario() { 80 ctx := context.Background() 81 log := zap.NewNop() 82 cfg := grpcscenario.GunConfig{ 83 Target: s.grpcAddress, 84 Timeout: 0, 85 TLS: false, 86 DialOptions: grpcscenario.GrpcDialOptions{}, 87 AnswLog: grpcscenario.AnswLogConfig{}, 88 } 89 g := grpcscenario.NewGun(cfg) 90 91 sharedDeps, err := g.WarmUp(&warmup.Options{Log: log, Ctx: ctx}) 92 s.NoError(err) 93 94 gunDeps := core.GunDeps{Ctx: ctx, Log: log, PoolID: "test", InstanceID: 1, Shared: sharedDeps} 95 aggr := &Aggregator{} 96 err = g.Bind(aggr, gunDeps) 97 s.NoError(err) 98 99 am := &grpcscenario.Scenario{ 100 Name: "", 101 Calls: []grpcscenario.Call{ 102 { 103 Name: "auth", 104 Preprocessors: nil, 105 Postprocessors: nil, 106 Tag: "auth", 107 Call: "target.TargetService.Auth", 108 Metadata: map[string]string{"metadata": "server.proto"}, 109 Payload: []byte(`{"login": "1", "pass": "1"}`), 110 Sleep: 0, 111 }, 112 { 113 Name: "list", 114 Preprocessors: nil, 115 Postprocessors: nil, 116 Tag: "list", 117 Call: "target.TargetService.List", 118 Metadata: map[string]string{"metadata": "server.proto"}, 119 Payload: []byte(`{"user_id": {{.request.auth.postprocessor.userId}}, "token": "{{.request.auth.postprocessor.token}}"}`), 120 Sleep: 0, 121 }, 122 { 123 Name: "order", 124 Preprocessors: nil, 125 Postprocessors: nil, 126 Tag: "order", 127 Call: "target.TargetService.Order", 128 Metadata: map[string]string{"metadata": "server.proto"}, 129 Payload: []byte(`{"user_id": {{.request.auth.postprocessor.userId}}, "item_id": 1098, "token": "{{.request.auth.postprocessor.token}}"}`), 130 Sleep: 0, 131 }, 132 }, 133 } 134 135 g.Shoot(am) 136 s.Len(aggr.samples, 3) 137 } 138 139 func (s *GunSuite) Test_FullScenario() { 140 ctx := context.Background() 141 log := zap.NewNop() 142 gunConfig := grpcscenario.GunConfig{ 143 Target: s.grpcAddress, 144 Timeout: 0, 145 TLS: false, 146 DialOptions: grpcscenario.GrpcDialOptions{}, 147 AnswLog: grpcscenario.AnswLogConfig{}, 148 } 149 g := grpcscenario.NewGun(gunConfig) 150 151 sharedDeps, err := g.WarmUp(&warmup.Options{Log: log, Ctx: ctx}) 152 s.NoError(err) 153 154 gunDeps := core.GunDeps{Ctx: ctx, Log: log, PoolID: "pool_id", InstanceID: 1, Shared: sharedDeps} 155 aggr := &Aggregator{} 156 err = g.Bind(aggr, gunDeps) 157 s.NoError(err) 158 159 pr, err := ammo.NewProvider(s.fs, scenario.ProviderConfig{File: "testdata/grpc_payload.hcl"}) 160 require.NoError(s.T(), err) 161 go func() { 162 _ = pr.Run(ctx, core.ProviderDeps{Log: log, PoolID: "pool_id"}) 163 }() 164 165 for i := 0; i < 3; i++ { 166 am, ok := pr.Acquire() 167 s.True(ok) 168 g.Shoot(am) 169 } 170 s.Len(aggr.samples, 15) 171 stats, err := s.srv.Stats(context.Background(), nil) 172 require.NoError(s.T(), err) 173 174 s.Assert().Equal(map[int64]uint64{1: 1, 2: 1, 3: 1}, stats.Auth.Code200) 175 s.Assert().Equal(uint64(0), stats.Auth.Code400) 176 s.Assert().Equal(uint64(0), stats.Auth.Code500) 177 s.Assert().Equal(map[int64]uint64{1: 1, 2: 1, 3: 1}, stats.List.Code200) 178 s.Assert().Equal(uint64(0), stats.List.Code400) 179 s.Assert().Equal(uint64(0), stats.List.Code500) 180 s.Assert().Equal(map[int64]uint64{1: 3, 2: 3, 3: 3}, stats.Order.Code200) 181 s.Assert().Equal(uint64(0), stats.Order.Code400) 182 s.Assert().Equal(uint64(0), stats.Order.Code500) 183 184 } 185 186 func (s *GunSuite) Test_ErrorScenario() { 187 ctx := context.Background() 188 log := zap.NewNop() 189 cfg := grpcscenario.GunConfig{ 190 Target: s.grpcAddress, 191 Timeout: 0, 192 TLS: false, 193 DialOptions: grpcscenario.GrpcDialOptions{}, 194 AnswLog: grpcscenario.AnswLogConfig{}, 195 } 196 g := grpcscenario.NewGun(cfg) 197 198 sharedDeps, err := g.WarmUp(&warmup.Options{Log: log, Ctx: ctx}) 199 s.NoError(err) 200 201 gunDeps := core.GunDeps{Ctx: ctx, Log: log, PoolID: "test", InstanceID: 1, Shared: sharedDeps} 202 aggr := &Aggregator{} 203 err = g.Bind(aggr, gunDeps) 204 s.NoError(err) 205 206 am := &grpcscenario.Scenario{ 207 Name: "", 208 Calls: []grpcscenario.Call{ 209 { 210 Name: "auth", 211 Preprocessors: nil, 212 Postprocessors: []grpcscenario.Postprocessor{&postprocessor.AssertResponse{ 213 Payload: nil, 214 StatusCode: 200, 215 }}, 216 Tag: "auth", 217 Call: "target.TargetService.Auth", 218 Metadata: map[string]string{"metadata": "server.proto"}, 219 Payload: []byte(`{"login": "1", "pass": "2"}`), // invalid pass 220 Sleep: 0, 221 }, 222 { 223 Name: "list", 224 Preprocessors: nil, 225 Postprocessors: nil, 226 Tag: "list", 227 Call: "target.TargetService.List", 228 Metadata: map[string]string{"metadata": "server.proto"}, 229 Payload: []byte(`{"user_id": {{.request.auth.postprocessor.userId}}, "token": "{{.request.auth.postprocessor.token}}"}`), 230 Sleep: 0, 231 }, 232 { 233 Name: "order", 234 Preprocessors: nil, 235 Postprocessors: nil, 236 Tag: "order", 237 Call: "target.TargetService.Order", 238 Metadata: map[string]string{"metadata": "server.proto"}, 239 Payload: []byte(`{"user_id": {{.request.auth.postprocessor.userId}}, "item_id": 1098, "token": "{{.request.auth.postprocessor.token}}"}`), 240 Sleep: 0, 241 }, 242 }, 243 } 244 245 g.Shoot(am) 246 s.Len(aggr.samples, 1) 247 } 248 249 type Aggregator struct { 250 samples []core.Sample 251 } 252 253 func (a *Aggregator) Run(ctx context.Context, deps core.AggregatorDeps) error { 254 return nil 255 } 256 257 func (a *Aggregator) Report(s core.Sample) { 258 a.samples = append(a.samples, s) 259 }