TestIntegrationCircuitBreaker validates that the circuit breaker opens after consecutive failures and that the corresponding metrics are exposed.
(t *testing.T)
| 429 | // TestIntegrationCircuitBreaker validates that the circuit breaker opens after |
| 430 | // consecutive failures and that the corresponding metrics are exposed. |
| 431 | func TestIntegrationCircuitBreaker(t *testing.T) { |
| 432 | t.Parallel() |
| 433 | |
| 434 | ctx := testutil.Context(t, testutil.WaitLong) |
| 435 | |
| 436 | // Create prometheus registry and metrics. |
| 437 | registry := prometheus.NewRegistry() |
| 438 | metrics := aibridge.NewMetrics(registry) |
| 439 | |
| 440 | // Set up mock OpenAI server that always returns 503 Service Unavailable. |
| 441 | mockOpenAI := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 442 | w.Header().Set("Content-Type", "application/json") |
| 443 | // Disable SDK retries. |
| 444 | w.Header().Set("x-should-retry", "false") |
| 445 | w.WriteHeader(http.StatusServiceUnavailable) |
| 446 | _, _ = w.Write([]byte(`{"error":{"message":"Service Unavailable.","type":"cf_service_unavailable","code":503}}`)) |
| 447 | })) |
| 448 | t.Cleanup(mockOpenAI.Close) |
| 449 | |
| 450 | // Set up mock Anthropic server that always returns 529 Overloaded. |
| 451 | mockAnthropic := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 452 | w.Header().Set("Content-Type", "application/json") |
| 453 | // Anthropic uses 529 for overloaded errors. |
| 454 | w.WriteHeader(529) |
| 455 | _, _ = w.Write([]byte(`{"type":"error","error":{"type":"overloaded_error","message":"Overloaded"}}`)) |
| 456 | })) |
| 457 | t.Cleanup(mockAnthropic.Close) |
| 458 | |
| 459 | // Database and coderd setup. |
| 460 | db, ps := dbtestutil.NewDB(t) |
| 461 | client, _, api, firstUser := coderdenttest.NewWithAPI(t, &coderdenttest.Options{ |
| 462 | Options: &coderdtest.Options{ |
| 463 | Database: db, |
| 464 | Pubsub: ps, |
| 465 | }, |
| 466 | }) |
| 467 | |
| 468 | userClient, _ := coderdtest.CreateAnotherUser(t, client, firstUser.OrganizationID) |
| 469 | |
| 470 | // Create an API token for the user. |
| 471 | apiKey, err := userClient.CreateToken(ctx, "me", codersdk.CreateTokenRequest{ |
| 472 | TokenName: fmt.Sprintf("test-key-%d", time.Now().UnixNano()), |
| 473 | Lifetime: time.Hour, |
| 474 | Scope: codersdk.APIKeyScopeCoderAll, |
| 475 | }) |
| 476 | require.NoError(t, err) |
| 477 | |
| 478 | // Create aibridge client. |
| 479 | aiBridgeClient, err := api.AGPL.CreateInMemoryAIBridgeServer(ctx) |
| 480 | require.NoError(t, err) |
| 481 | |
| 482 | logger := testutil.Logger(t) |
| 483 | |
| 484 | // Create providers with circuit breaker configured to open after 2 failures. |
| 485 | cbConfig := &config.CircuitBreaker{ |
| 486 | FailureThreshold: 2, |
| 487 | Interval: time.Minute, |
| 488 | Timeout: time.Minute, |
nothing calls this directly
no test coverage detected