(t *testing.T)
| 391 | } |
| 392 | |
| 393 | func TestRetries(t *testing.T) { |
| 394 | t.Parallel() |
| 395 | |
| 396 | const maxAttempts = 3 |
| 397 | ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) |
| 398 | store, pubsub := dbtestutil.NewDB(t) |
| 399 | logger := testutil.Logger(t) |
| 400 | |
| 401 | // GIVEN: a mock HTTP server which will receive webhooksand a map to track the dispatch attempts |
| 402 | |
| 403 | receivedMap := syncmap.New[uuid.UUID, int]() |
| 404 | // Mock server to simulate webhook endpoint. |
| 405 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 406 | var payload dispatch.WebhookPayload |
| 407 | err := json.NewDecoder(r.Body).Decode(&payload) |
| 408 | assert.NoError(t, err) |
| 409 | |
| 410 | count, _ := receivedMap.LoadOrStore(payload.MsgID, 0) |
| 411 | count++ |
| 412 | receivedMap.Store(payload.MsgID, count) |
| 413 | |
| 414 | // Let the request succeed if this is its last attempt. |
| 415 | if count == maxAttempts { |
| 416 | w.WriteHeader(http.StatusOK) |
| 417 | _, err = w.Write([]byte("noted.")) |
| 418 | assert.NoError(t, err) |
| 419 | return |
| 420 | } |
| 421 | |
| 422 | w.WriteHeader(http.StatusInternalServerError) |
| 423 | _, err = w.Write([]byte("retry again later...")) |
| 424 | assert.NoError(t, err) |
| 425 | })) |
| 426 | defer server.Close() |
| 427 | |
| 428 | endpoint, err := url.Parse(server.URL) |
| 429 | require.NoError(t, err) |
| 430 | |
| 431 | method := database.NotificationMethodWebhook |
| 432 | cfg := defaultNotificationsConfig(method) |
| 433 | cfg.Webhook = codersdk.NotificationsWebhookConfig{ |
| 434 | Endpoint: *serpent.URLOf(endpoint), |
| 435 | } |
| 436 | |
| 437 | cfg.MaxSendAttempts = maxAttempts |
| 438 | |
| 439 | // Tune intervals low to speed up test. |
| 440 | cfg.StoreSyncInterval = serpent.Duration(time.Millisecond * 100) |
| 441 | cfg.RetryInterval = serpent.Duration(time.Second) // query uses second-precision |
| 442 | cfg.FetchInterval = serpent.Duration(time.Millisecond * 100) |
| 443 | |
| 444 | handler := newDispatchInterceptor(dispatch.NewWebhookHandler(cfg.Webhook, logger.Named("webhook"))) |
| 445 | |
| 446 | // Intercept calls to submit the buffered updates to the store. |
| 447 | storeInterceptor := &syncInterceptor{Store: store} |
| 448 | |
| 449 | mgr, err := notifications.NewManager(cfg, storeInterceptor, pubsub, defaultHelpers(), createMetrics(), logger.Named("manager")) |
| 450 | require.NoError(t, err) |
nothing calls this directly
no test coverage detected