77
88from __future__ import annotations
99
10+ import re
1011from typing import Literal
12+ from urllib .parse import parse_qs , urlparse
1113
1214import pytest
15+ import responses
1316from airbyte ._util import api_util , text_util
1417from airbyte ._util .api_util import (
1518 CLOUD_API_ROOT ,
@@ -305,22 +308,28 @@ def test_check_connector(
305308 pytest .fail (f"API call failed: { e } " )
306309
307310
311+ @responses .activate
308312def test_404_error_includes_request_url_context () -> None :
309313 """Test that 404 API errors include the full request URL in context.
310314
311315 This test validates that when an API call fails with a 404, the error
312316 includes the full request URL that was attempted. This helps debug URL
313317 construction issues like those seen with custom API roots.
314318
315- Uses httpbin.org which returns 404 for /sources endpoint, allowing us
316- to verify the error context contains the expected URL information .
319+ The HTTP layer is mocked with `responses` so the assertions exercise the
320+ SDK's real URL construction without depending on a live external service .
317321 """
318- from urllib .parse import parse_qs , urlparse
319-
320- api_root = "https://httpbin.org"
322+ api_root = "https://api.airbyte.example"
321323 workspace_id = "00000000-0000-0000-0000-000000000000"
322324 fake_bearer_token = SecretString ("fake-token-for-testing" )
323325
326+ responses .add (
327+ responses .GET ,
328+ re .compile (r"https://api\.airbyte\.example/sources.*" ),
329+ json = {"message" : "Not Found" },
330+ status = 404 ,
331+ )
332+
324333 with pytest .raises (AirbyteError ) as exc_info :
325334 api_util .list_sources (
326335 workspace_id = workspace_id ,
@@ -348,8 +357,8 @@ def test_404_error_includes_request_url_context() -> None:
348357 request_url = str (error .context ["request_url" ])
349358 parsed = urlparse (request_url )
350359
351- assert parsed .netloc == "httpbin.org " , (
352- f"Expected host 'httpbin.org ', got '{ parsed .netloc } '"
360+ assert parsed .netloc == "api.airbyte.example " , (
361+ f"Expected host 'api.airbyte.example ', got '{ parsed .netloc } '"
353362 )
354363 assert parsed .path .endswith ("/sources" ), (
355364 f"Expected path ending with '/sources', got '{ parsed .path } '"
@@ -363,20 +372,29 @@ def test_404_error_includes_request_url_context() -> None:
363372 )
364373
365374
375+ @responses .activate
366376def test_url_construction_with_path_prefix () -> None :
367377 """Test that the SDK correctly preserves path prefixes in the base URL.
368378
369- This test uses httpbin.org/anything which echoes back the request details,
370- allowing us to verify the actual URL that was constructed and sent.
371-
372379 This test validates that when using a custom API root with a path prefix
373380 (like https://host/api/public/v1), the SDK correctly appends endpoints
374381 to that path rather than replacing it.
382+
383+ The HTTP layer is mocked with `responses` so we can inspect the URL the
384+ SDK actually sent, verifying path-prefix preservation without depending
385+ on a live external service.
375386 """
376- base_url_with_path = "https://httpbin.org/anything /api/public/v1"
387+ base_url_with_path = "https://api.airbyte.example /api/public/v1"
377388 fake_bearer_token = SecretString ("fake-token-for-testing" )
378389 fake_workspace_id = "00000000-0000-0000-0000-000000000000"
379390
391+ responses .add (
392+ responses .GET ,
393+ re .compile (r"https://api\.airbyte\.example/api/public/v1/sources.*" ),
394+ json = {"data" : [], "next" : None , "previous" : None },
395+ status = 200 ,
396+ )
397+
380398 result = api_util .list_sources (
381399 workspace_id = fake_workspace_id ,
382400 api_root = base_url_with_path ,
@@ -385,10 +403,11 @@ def test_url_construction_with_path_prefix() -> None:
385403 bearer_token = fake_bearer_token ,
386404 )
387405
388- print (f"\n Base URL with path prefix: { base_url_with_path } " )
389- print (f"Result type: { type (result )} " )
390- print (f"Result: { result } " )
406+ assert result == [], f"Expected empty list, but got: { result } "
391407
392- assert result == [], (
393- f"Expected empty list from httpbin (no real sources), but got: { result } "
408+ assert len (responses .calls ) == 1 , "Expected exactly one request to be sent"
409+ sent_url = responses .calls [0 ].request .url
410+ sent_path = urlparse (sent_url ).path
411+ assert sent_path == "/api/public/v1/sources" , (
412+ f"Expected path '/api/public/v1/sources', got '{ sent_path } '"
394413 )
0 commit comments