github.com/kubeshop/testkube@v1.17.23/pkg/logs/events_test.go (about) 1 package logs 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "testing" 8 "time" 9 10 "github.com/nats-io/nats.go" 11 "github.com/nats-io/nats.go/jetstream" 12 "github.com/stretchr/testify/assert" 13 14 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 15 "github.com/kubeshop/testkube/pkg/event" 16 "github.com/kubeshop/testkube/pkg/event/bus" 17 "github.com/kubeshop/testkube/pkg/logs/adapter" 18 "github.com/kubeshop/testkube/pkg/logs/client" 19 "github.com/kubeshop/testkube/pkg/logs/events" 20 "github.com/kubeshop/testkube/pkg/logs/state" 21 ) 22 23 var waitTime = time.Second 24 25 func TestLogs_EventsFlow(t *testing.T) { 26 t.Parallel() 27 28 t.Run("should remove all adapters when stop event handled", func(t *testing.T) { 29 // given context with 1s deadline 30 ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) 31 defer cancel() 32 33 // and NATS test server with connection 34 ns, nc := bus.TestServerWithConnection() 35 defer ns.Shutdown() 36 37 id := "stop-test" 38 39 // and jetstream configured 40 js, err := jetstream.New(nc) 41 assert.NoError(t, err) 42 43 // and KV store 44 kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: "state-test"}) 45 assert.NoError(t, err) 46 assert.NotNil(t, kv) 47 48 // and logs state manager 49 state := state.NewState(kv) 50 51 logsStream, err := client.NewNatsLogStream(nc) 52 assert.NoError(t, err) 53 54 // and initialized log service 55 log := NewLogsService(nc, js, state, logsStream). 56 WithRandomPort() 57 58 // given example adapters 59 a := NewMockAdapter("aaa") 60 b := NewMockAdapter("bbb") 61 62 // with 4 adapters (the same adapter is added 4 times so it'll receive 4 times more messages) 63 log.AddAdapter(a) 64 log.AddAdapter(b) 65 66 // and log service running 67 go func() { 68 log.Run(ctx) 69 }() 70 71 // and ready to get messages 72 <-log.Ready 73 74 // and logs stream client 75 stream, err := client.NewNatsLogStream(nc) 76 assert.NoError(t, err) 77 78 // and initialized log stream for given ID 79 meta, err := stream.Init(ctx, id) 80 assert.NotEmpty(t, meta.Name) 81 assert.NoError(t, err) 82 83 // when start event triggered 84 _, err = stream.Start(ctx, id) 85 assert.NoError(t, err) 86 87 // and when data pushed to the log stream 88 stream.Push(ctx, id, events.NewLog("hello 1")) 89 stream.Push(ctx, id, events.NewLog("hello 2")) 90 91 // and stop event triggered 92 _, err = stream.Stop(ctx, id) 93 assert.NoError(t, err) 94 95 // cooldown stop time 96 time.Sleep(waitTime) 97 98 // then all adapters should be gracefully stopped 99 assert.Equal(t, 0, log.GetConsumersStats(ctx).Count) 100 }) 101 102 t.Run("should start and stop on test event", func(t *testing.T) { 103 // given context with 1s deadline 104 ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) 105 defer cancel() 106 107 // and NATS test server with connection 108 ns, nc := bus.TestServerWithConnection() 109 defer ns.Shutdown() 110 111 id := "id1" 112 113 // and jetstream configured 114 js, err := jetstream.New(nc) 115 assert.NoError(t, err) 116 117 // and KV store 118 kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: "start-stop-on-test"}) 119 assert.NoError(t, err) 120 assert.NotNil(t, kv) 121 122 // and logs state manager 123 state := state.NewState(kv) 124 125 logsStream, err := client.NewNatsLogStream(nc) 126 assert.NoError(t, err) 127 128 // and initialized log service 129 log := NewLogsService(nc, js, state, logsStream). 130 WithRandomPort() 131 132 // given example adapter 133 a := NewMockAdapter() 134 135 messagesCount := 10000 136 137 // with 4 adapters (the same adapter is added 4 times so it'll receive 4 times more messages) 138 log.AddAdapter(a) 139 140 // and log service running 141 go func() { 142 log.Run(ctx) 143 }() 144 145 // and test event emitter 146 ec, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER) 147 assert.NoError(t, err) 148 eventBus := bus.NewNATSBus(ec) 149 emitter := event.NewEmitter(eventBus, "test-cluster", map[string]string{}) 150 151 // and stream client 152 stream, err := client.NewNatsLogStream(nc) 153 assert.NoError(t, err) 154 155 // and initialized log stream for given ID 156 meta, err := stream.Init(ctx, id) 157 assert.NotEmpty(t, meta.Name) 158 assert.NoError(t, err) 159 160 // and ready to get messages 161 <-log.Ready 162 163 // when start event triggered 164 emitter.Notify(testkube.NewEventStartTest(&testkube.Execution{Id: "id1"})) 165 166 for i := 0; i < messagesCount; i++ { 167 // and when data pushed to the log stream 168 err = stream.Push(ctx, id, events.NewLog("hello")) 169 assert.NoError(t, err) 170 } 171 172 // and wait for message to be propagated 173 emitter.Notify(testkube.NewEventEndTestFailed(&testkube.Execution{Id: "id1"})) 174 175 time.Sleep(waitTime) 176 177 assertMessagesCount(t, a, messagesCount) 178 179 }) 180 181 t.Run("should react on new message and pass data to adapter", func(t *testing.T) { 182 // given context with 1s deadline 183 ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) 184 defer cancel() 185 186 // and NATS test server with connection 187 ns, nc := bus.TestServerWithConnection() 188 defer ns.Shutdown() 189 190 id := "messages-test" 191 192 // and jetstream configured 193 js, err := jetstream.New(nc) 194 assert.NoError(t, err) 195 196 // and KV store 197 kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: "state-test"}) 198 assert.NoError(t, err) 199 assert.NotNil(t, kv) 200 201 // and logs state manager 202 state := state.NewState(kv) 203 204 logsStream, err := client.NewNatsLogStream(nc) 205 assert.NoError(t, err) 206 207 // and initialized log service 208 log := NewLogsService(nc, js, state, logsStream). 209 WithRandomPort() 210 211 // given example adapter 212 a1 := NewMockAdapter() 213 a2 := NewMockAdapter() 214 a3 := NewMockAdapter() 215 a4 := NewMockAdapter() 216 217 messagesCount := 1000 218 219 // with 4 adapters (the same adapter is added 4 times so it'll receive 4 times more messages) 220 log.AddAdapter(a1) 221 log.AddAdapter(a2) 222 log.AddAdapter(a3) 223 log.AddAdapter(a4) 224 225 // and log service running 226 go func() { 227 log.Run(ctx) 228 }() 229 230 // and ready to get messages 231 <-log.Ready 232 233 // and stream client 234 stream, err := client.NewNatsLogStream(nc) 235 assert.NoError(t, err) 236 237 // and initialized log stream for given ID 238 meta, err := stream.Init(ctx, id) 239 assert.NotEmpty(t, meta.Name) 240 assert.NoError(t, err) 241 242 // when start event triggered 243 _, err = stream.Start(ctx, id) 244 assert.NoError(t, err) 245 246 for i := 0; i < messagesCount; i++ { 247 // and when data pushed to the log stream 248 err = stream.Push(ctx, id, events.NewLog("hello")) 249 assert.NoError(t, err) 250 } 251 252 // and wait for message to be propagated 253 _, err = stream.Stop(ctx, id) 254 assert.NoError(t, err) 255 256 // cool down 257 time.Sleep(waitTime) 258 259 // then each adapter should receive messages 260 assertMessagesCount(t, a1, messagesCount) 261 assertMessagesCount(t, a2, messagesCount) 262 assertMessagesCount(t, a3, messagesCount) 263 assertMessagesCount(t, a4, messagesCount) 264 265 }) 266 267 t.Run("can get stats about consumers in given pod", func(t *testing.T) { 268 // given context with 1s deadline 269 ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) 270 defer cancel() 271 272 // and NATS test server with connection 273 ns, nc := bus.TestServerWithConnection() 274 defer ns.Shutdown() 275 276 // and jetstream configured 277 js, err := jetstream.New(nc) 278 assert.NoError(t, err) 279 280 id := "executionid1" 281 282 // and KV store 283 kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: "state-test"}) 284 assert.NoError(t, err) 285 assert.NotNil(t, kv) 286 287 // and logs state manager 288 state := state.NewState(kv) 289 290 logsStream, err := client.NewNatsLogStream(nc) 291 assert.NoError(t, err) 292 293 // and initialized log service 294 log := NewLogsService(nc, js, state, logsStream). 295 WithRandomPort() 296 297 // given example adapters 298 a := NewMockAdapter("aaa") 299 b := NewMockAdapter("bbb") 300 301 // with 4 adapters (the same adapter is added 4 times so it'll receive 4 times more messages) 302 log.AddAdapter(a) 303 log.AddAdapter(b) 304 305 // and log service running 306 go func() { 307 log.Run(ctx) 308 }() 309 310 // and ready to get messages 311 <-log.Ready 312 313 // and logs stream client 314 stream, err := client.NewNatsLogStream(nc) 315 assert.NoError(t, err) 316 317 // and initialized log stream for given ID 318 meta, err := stream.Init(ctx, id) 319 assert.NotEmpty(t, meta.Name) 320 assert.NoError(t, err) 321 322 // when start event triggered 323 _, err = stream.Start(ctx, id) 324 assert.NoError(t, err) 325 326 // then we should have 2 consumers 327 stats := log.GetConsumersStats(ctx) 328 assert.Equal(t, 2, stats.Count) 329 330 stream.Push(ctx, id, events.NewLog("hello 1")) 331 stream.Push(ctx, id, events.NewLog("hello 1")) 332 stream.Push(ctx, id, events.NewLog("hello 1")) 333 334 // when stop event triggered 335 r, err := stream.Stop(ctx, id) 336 assert.NoError(t, err) 337 assert.False(t, r.Error) 338 assert.Equal(t, "stop-queued", string(r.Message)) 339 340 // there will be wait for mess 341 time.Sleep(waitTime) 342 343 // then all adapters should be gracefully stopped 344 assert.Equal(t, 0, log.GetConsumersStats(ctx).Count) 345 }) 346 347 } 348 349 // Mock adapter 350 var _ adapter.Adapter = &MockAdapter{} 351 352 // NewMockAdapter creates new mocked adapter to check amount of messages passed to it 353 func NewMockAdapter(name ...string) *MockAdapter { 354 n := "default" 355 if len(name) > 0 { 356 n = name[0] 357 } 358 359 return &MockAdapter{ 360 Messages: []events.Log{}, 361 name: n, 362 } 363 } 364 365 type MockAdapter struct { 366 lock sync.Mutex 367 Messages []events.Log 368 name string 369 } 370 371 func (s *MockAdapter) Init(ctx context.Context, id string) error { 372 return nil 373 } 374 375 func (s *MockAdapter) Notify(ctx context.Context, id string, e events.Log) error { 376 // don't count finished logs 377 if events.IsFinished(&e) { 378 return nil 379 } 380 s.lock.Lock() 381 defer s.lock.Unlock() 382 383 e.Metadata = map[string]string{"id": id} 384 s.Messages = append(s.Messages, e) 385 return nil 386 } 387 388 func (s *MockAdapter) Stop(ctx context.Context, id string) error { 389 fmt.Printf("stopping %s \n", id) 390 return nil 391 } 392 393 func (s *MockAdapter) Name() string { 394 return s.name 395 } 396 397 func assertMessagesCount(t *testing.T, a *MockAdapter, expectedCount int) { 398 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 399 defer cancel() 400 ticker := time.NewTicker(100 * time.Millisecond) 401 for { 402 403 select { 404 case <-ctx.Done(): 405 t.Errorf("timeout waiting for messages count %d (expected:%d)", len(a.Messages), expectedCount) 406 t.Fail() 407 return 408 case <-ticker.C: 409 if len(a.Messages) == expectedCount { 410 return 411 } 412 } 413 } 414 }