TestProxy_HotReloadRoutingCRUD drives the proxy through a CRUD-style sequence of provider changes and asserts on routing after each Reload via real HTTPS requests. Hostnames are .invalid (RFC 2606) so a request that escapes the MITM path fails fast via DNS rather than reaching a real upstream.
(t *testing.T)
| 390 | // Hostnames are .invalid (RFC 2606) so a request that escapes the MITM |
| 391 | // path fails fast via DNS rather than reaching a real upstream. |
| 392 | func TestProxy_HotReloadRoutingCRUD(t *testing.T) { |
| 393 | t.Parallel() |
| 394 | |
| 395 | h := newReloadTestHarness(t) |
| 396 | |
| 397 | // InitialEmptyRouter: no Reload has been called and no provider |
| 398 | // hosts are configured, so any host falls through to the tunneled |
| 399 | // middleware. |
| 400 | h.expectNotRouted(t, "https://alpha.invalid/v1/messages") |
| 401 | |
| 402 | // CreateProvider. |
| 403 | h.store.set([]rawProvider{ |
| 404 | {name: "alpha", baseURL: "https://alpha.invalid/v1"}, |
| 405 | }) |
| 406 | require.NoError(t, h.srv.Reload(t.Context())) |
| 407 | h.expectRoutedTo(t, "https://alpha.invalid/v1/messages", "/api/v2/aibridge/alpha/v1/messages") |
| 408 | h.expectProviderStatus(t, "alpha", "enabled") |
| 409 | |
| 410 | // UpdateProviderName: the same BaseURL with a new name must route |
| 411 | // under the new name on the next Reload. The renamed provider must |
| 412 | // not leave a stale alpha series behind. |
| 413 | h.store.set([]rawProvider{ |
| 414 | {name: "alpha-v2", baseURL: "https://alpha.invalid/v1"}, |
| 415 | }) |
| 416 | require.NoError(t, h.srv.Reload(t.Context())) |
| 417 | h.expectRoutedTo(t, "https://alpha.invalid/v1/messages", "/api/v2/aibridge/alpha-v2/v1/messages") |
| 418 | h.expectProviderStatus(t, "alpha-v2", "enabled") |
| 419 | h.expectProviderAbsent(t, "alpha") |
| 420 | |
| 421 | // UpdateProviderBaseURLHost: moving the provider to a new host must |
| 422 | // start MITM'ing the new host and stop MITM'ing the old one. |
| 423 | h.store.set([]rawProvider{ |
| 424 | {name: "alpha-v2", baseURL: "https://alpha-new.invalid/v1"}, |
| 425 | }) |
| 426 | require.NoError(t, h.srv.Reload(t.Context())) |
| 427 | h.expectRoutedTo(t, "https://alpha-new.invalid/v1/messages", "/api/v2/aibridge/alpha-v2/v1/messages") |
| 428 | h.expectNotRouted(t, "https://alpha.invalid/v1/messages") |
| 429 | h.expectProviderStatus(t, "alpha-v2", "enabled") |
| 430 | |
| 431 | // AddSecondProvider: a second provider added in the same Reload must |
| 432 | // route independently from the first. |
| 433 | h.store.set([]rawProvider{ |
| 434 | {name: "alpha-v2", baseURL: "https://alpha-new.invalid/v1"}, |
| 435 | {name: "beta", baseURL: "https://beta.invalid/v1"}, |
| 436 | }) |
| 437 | require.NoError(t, h.srv.Reload(t.Context())) |
| 438 | h.expectRoutedTo(t, "https://alpha-new.invalid/v1/messages", "/api/v2/aibridge/alpha-v2/v1/messages") |
| 439 | h.expectRoutedTo(t, "https://beta.invalid/v1/chat/completions", "/api/v2/aibridge/beta/v1/chat/completions") |
| 440 | h.expectProviderStatus(t, "alpha-v2", "enabled") |
| 441 | h.expectProviderStatus(t, "beta", "enabled") |
| 442 | |
| 443 | // DeleteOneProvider: removing alpha must keep beta routed and stop |
| 444 | // routing alpha. The deleted name disappears from provider_info. |
| 445 | h.store.set([]rawProvider{ |
| 446 | {name: "beta", baseURL: "https://beta.invalid/v1"}, |
| 447 | }) |
| 448 | require.NoError(t, h.srv.Reload(t.Context())) |
| 449 | h.expectRoutedTo(t, "https://beta.invalid/v1/chat/completions", "/api/v2/aibridge/beta/v1/chat/completions") |
nothing calls this directly
no test coverage detected