TestSharedVariableTimeoutHandling verifies that when the remote backend is down, the shared variable operations fail fast and leave enough time for the actual operation.
(t *testing.T)
| 987 | // TestSharedVariableTimeoutHandling verifies that when the remote backend is down, |
| 988 | // the shared variable operations fail fast and leave enough time for the actual operation. |
| 989 | func TestSharedVariableTimeoutHandling(t *testing.T) { |
| 990 | t.Run("operations respect timeout when remote is down", func(t *testing.T) { |
| 991 | ctx, cancel := context.WithCancel(context.Background()) |
| 992 | defer cancel() |
| 993 | |
| 994 | // Create a connector that simulates a broken Redis connection |
| 995 | connector := NewMockConnector("broken") |
| 996 | registry := &sharedStateRegistry{ |
| 997 | appCtx: ctx, |
| 998 | logger: &log.Logger, |
| 999 | clusterKey: "test", |
| 1000 | connector: connector, |
| 1001 | fallbackTimeout: 1 * time.Second, |
| 1002 | lockTtl: 30 * time.Second, |
| 1003 | lockMaxWait: 200 * time.Millisecond, |
| 1004 | updateMaxWait: 200 * time.Millisecond, |
| 1005 | initializer: util.NewInitializer(ctx, &log.Logger, nil), |
| 1006 | } |
| 1007 | |
| 1008 | // Setup mock to simulate timeout on Lock |
| 1009 | connector.On("Lock", mock.Anything, mock.Anything, mock.Anything). |
| 1010 | Run(func(args mock.Arguments) { |
| 1011 | ctx := args.Get(0).(context.Context) |
| 1012 | // Wait for context to timeout |
| 1013 | <-ctx.Done() |
| 1014 | }). |
| 1015 | Return(nil, context.DeadlineExceeded) |
| 1016 | |
| 1017 | // Background push publishes best-effort for fast propagation. |
| 1018 | connector.On("PublishCounterInt64", mock.Anything, "test/timeout-counter", mock.Anything).Return(nil).Maybe() |
| 1019 | |
| 1020 | // Setup mock for Get to return not found initially |
| 1021 | connector.On("Get", mock.Anything, ConnectorMainIndex, "test/timeout-counter", "value", nil). |
| 1022 | Return(nil, common.NewErrRecordNotFound("test/timeout-counter", "value", "mock")) |
| 1023 | |
| 1024 | // Setup mock for WatchCounterInt64 |
| 1025 | updates := make(chan CounterInt64State, 10) |
| 1026 | cleanup := func() { close(updates) } |
| 1027 | connector.On("WatchCounterInt64", mock.Anything, "test/timeout-counter"). |
| 1028 | Return(updates, cleanup, nil) |
| 1029 | |
| 1030 | // Get a counter variable |
| 1031 | counter := registry.GetCounterInt64("timeout-counter", 100).(*counterInt64) |
| 1032 | time.Sleep(100 * time.Millisecond) // Allow initialization |
| 1033 | |
| 1034 | // Test TryUpdateIfStale with a parent context that has enough time for lock wait + operations |
| 1035 | operationCtx, operationCancel := context.WithTimeout(ctx, 3*time.Second) |
| 1036 | defer operationCancel() |
| 1037 | |
| 1038 | executionCount := atomic.Int32{} |
| 1039 | start := time.Now() |
| 1040 | |
| 1041 | value, err := counter.TryUpdateIfStale(operationCtx, 100*time.Millisecond, func(ctx context.Context) (int64, error) { |
| 1042 | executionCount.Add(1) |
| 1043 | // This simulates the actual operation (e.g., fetching block number) |
| 1044 | select { |
| 1045 | case <-ctx.Done(): |
| 1046 | return 0, ctx.Err() |
nothing calls this directly
no test coverage detected