(t *testing.T)
| 264 | } |
| 265 | |
| 266 | func Test_WatchKey_AlwaysRetry(t *testing.T) { |
| 267 | casBackoffConfig := backoff.Config{ |
| 268 | MinBackoff: 1 * time.Millisecond, |
| 269 | MaxBackoff: 1 * time.Millisecond, |
| 270 | MaxRetries: 5, // CAS should retry, but WatchKey should not |
| 271 | } |
| 272 | |
| 273 | ddbMock := NewDynamodbClientMock() |
| 274 | codecMock := &CodecMock{} |
| 275 | c := NewClientMock(ddbMock, codecMock, TestLogger{}, prometheus.NewPedanticRegistry(), defaultPullTime, casBackoffConfig) |
| 276 | |
| 277 | // Mock Query to always fail |
| 278 | ddbMock.On("Query").Return(map[string]dynamodbItem{}, errors.Errorf("query failed")) |
| 279 | |
| 280 | // WatchKey should not retry on failure (MaxRetries=0), so it should only call Query once |
| 281 | // and then fall back to stale data |
| 282 | staleData := &DescMock{} |
| 283 | staleData.On("Clone").Return(staleData).Once() |
| 284 | |
| 285 | // Set up some stale data first |
| 286 | c.updateStaleData(key, staleData, time.Now()) |
| 287 | |
| 288 | callCount := 0 |
| 289 | c.WatchKey(context.TODO(), key, func(i any) bool { |
| 290 | callCount++ |
| 291 | // Should only be called once with stale data after the first query fails |
| 292 | require.EqualValues(t, staleData, i) |
| 293 | return false // Stop watching |
| 294 | }) |
| 295 | |
| 296 | // Verify that Query was called exactly 11 times (1 initial + 10 retries due to hardcoded limit in WatchKey) |
| 297 | // This confirms WatchKey has its own retry logic separate from backoff MaxRetries |
| 298 | ddbMock.AssertNumberOfCalls(t, "Query", 11) |
| 299 | require.Equal(t, 1, callCount, "Callback should be called once with stale data") |
| 300 | } |
| 301 | |
| 302 | func Test_CAS_UpdateStale(t *testing.T) { |
| 303 | ddbMock := NewDynamodbClientMock() |
nothing calls this directly
no test coverage detected