Skip to content

Commit 2d589ca

Browse files
committed
move tests over
1 parent 04df0a3 commit 2d589ca

File tree

11 files changed

+1407
-38
lines changed

11 files changed

+1407
-38
lines changed

airbyte_cdk/manifest_runner/main.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,13 @@
11
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2-
"""Main entry point for the Airbyte CDK Manifest Runner server."""
3-
4-
import sys
2+
"""Main entry point for the Airbyte Manifest Runner server."""
53

64
import uvicorn
75

86

9-
def check_dependencies() -> bool:
10-
"""Check if all required dependencies are available."""
11-
try:
12-
import fastapi
13-
import uvicorn
14-
15-
return True
16-
except ImportError:
17-
return False
18-
19-
207
def run_server(
218
host: str = "127.0.0.1", port: int = 8000, reload: bool = False, log_level: str = "info"
229
) -> None:
2310
"""Run the FastAPI server."""
24-
if not check_dependencies():
25-
print("❌ Manifest runner dependencies not found. Please install with:")
26-
print(" pip install airbyte-cdk[manifest-runner]")
27-
print(" # or")
28-
print(" poetry install --extras manifest-runner")
29-
sys.exit(1)
3011

3112
print(f"🚀 Starting Airbyte CDK Manifest Runner on {host}:{port}")
3213

airbyte_cdk/manifest_runner/routers/manifest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,4 @@ def full_resolve(request: FullResolveRequest) -> ManifestResponse:
8686
streams.extend(generated_streams_list)
8787

8888
manifest["streams"] = streams
89-
return ManifestResponse(manifest)
89+
return ManifestResponse(manifest=manifest)

poetry.lock

Lines changed: 698 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@ pytest = {version = "^7", optional = true }
8787
orjson = "^3.10.7"
8888
serpyco-rs = "^1.10.2"
8989
sqlalchemy = {version = "^2.0,!=2.0.36", optional = true }
90-
fastapi = { version = "^0.104.0", optional = true }
91-
uvicorn = { version = "^0.24.0", optional = true }
90+
fastapi = { version = ">=0.116.1", extras = ["standard"], optional = true }
9291
xmltodict = ">=0.13,<0.15"
9392
anyascii = "^0.3.2"
9493
whenever = "^0.6.16"
@@ -123,7 +122,7 @@ file-based = ["avro", "fastavro", "pyarrow", "unstructured", "pdf2image", "pdfmi
123122
vector-db-based = ["langchain", "openai", "cohere", "tiktoken"]
124123
sql = ["sqlalchemy"]
125124
dev = ["pytest"]
126-
manifest-runner = ["fastapi", "uvicorn"]
125+
manifest-runner = ["fastapi"]
127126

128127
[tool.poetry.scripts]
129128

unit_tests/manifest_runner/__init__.py

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Tests for the manifest_runner package."""
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
from unittest.mock import Mock, patch
2+
3+
import pytest
4+
5+
from airbyte_cdk.manifest_runner.manifest_runner.runner import ManifestRunner
6+
7+
8+
class TestManifestRunner:
9+
"""Test cases for the ManifestRunner class."""
10+
11+
@pytest.fixture
12+
def mock_source(self):
13+
"""Create a mock ManifestDeclarativeSource."""
14+
return Mock()
15+
16+
@pytest.fixture
17+
def manifest_runner(self, mock_source):
18+
"""Create a ManifestRunner instance with mocked source."""
19+
return ManifestRunner(mock_source)
20+
21+
@pytest.fixture
22+
def sample_config(self):
23+
"""Sample configuration for testing."""
24+
return {"api_key": "test_key", "base_url": "https://api.example.com"}
25+
26+
@pytest.fixture
27+
def sample_catalog(self):
28+
"""Sample configured catalog for testing."""
29+
from airbyte_cdk.models.airbyte_protocol import (
30+
AirbyteStream,
31+
ConfiguredAirbyteCatalog,
32+
ConfiguredAirbyteStream,
33+
DestinationSyncMode,
34+
SyncMode,
35+
)
36+
37+
return ConfiguredAirbyteCatalog(
38+
streams=[
39+
ConfiguredAirbyteStream(
40+
stream=AirbyteStream(
41+
name="test_stream",
42+
json_schema={"type": "object"},
43+
supported_sync_modes=[SyncMode.full_refresh],
44+
),
45+
sync_mode=SyncMode.full_refresh,
46+
destination_sync_mode=DestinationSyncMode.overwrite,
47+
)
48+
]
49+
)
50+
51+
@pytest.fixture
52+
def sample_state(self):
53+
"""Sample state messages for testing."""
54+
return []
55+
56+
@patch("airbyte_cdk.manifest_runner.manifest_runner.runner.TestReader")
57+
def test_test_read_success(
58+
self, mock_test_reader_class, manifest_runner, sample_config, sample_catalog
59+
):
60+
"""Test successful test_read execution with various parameters and state messages."""
61+
from airbyte_cdk.models.airbyte_protocol import (
62+
AirbyteStateMessage,
63+
AirbyteStateType,
64+
)
65+
66+
# Mock the TestReader instance and its run_test_read method
67+
mock_test_reader_instance = Mock()
68+
mock_test_reader_class.return_value = mock_test_reader_instance
69+
70+
# Mock the StreamRead return value
71+
mock_stream_read = Mock()
72+
mock_test_reader_instance.run_test_read.return_value = mock_stream_read
73+
74+
# Test with state messages and various parameter values
75+
state_messages = [
76+
AirbyteStateMessage(
77+
type=AirbyteStateType.STREAM,
78+
stream={
79+
"stream_descriptor": {"name": "test_stream"},
80+
"stream_state": {"cursor": "2023-01-01"},
81+
},
82+
)
83+
]
84+
85+
record_limit = 50
86+
page_limit = 3
87+
slice_limit = 7
88+
89+
# Execute test_read
90+
result = manifest_runner.test_read(
91+
config=sample_config,
92+
catalog=sample_catalog,
93+
state=state_messages,
94+
record_limit=record_limit,
95+
page_limit=page_limit,
96+
slice_limit=slice_limit,
97+
)
98+
99+
# Verify TestReader was initialized with correct parameters
100+
mock_test_reader_class.assert_called_once_with(
101+
max_pages_per_slice=page_limit,
102+
max_slices=slice_limit,
103+
max_record_limit=record_limit,
104+
)
105+
106+
# Verify run_test_read was called with correct parameters including state
107+
mock_test_reader_instance.run_test_read.assert_called_once_with(
108+
source=manifest_runner._source,
109+
config=sample_config,
110+
configured_catalog=sample_catalog,
111+
state=state_messages,
112+
)
113+
114+
# Verify the result is returned correctly
115+
assert result == mock_stream_read
116+
117+
@patch("airbyte_cdk.manifest_runner.manifest_runner.runner.TestReader")
118+
def test_test_read_exception_handling(
119+
self,
120+
mock_test_reader_class,
121+
manifest_runner,
122+
sample_config,
123+
sample_catalog,
124+
sample_state,
125+
):
126+
"""Test that exceptions from TestReader are properly propagated."""
127+
mock_test_reader_instance = Mock()
128+
mock_test_reader_class.return_value = mock_test_reader_instance
129+
130+
# Make run_test_read raise an exception
131+
mock_test_reader_instance.run_test_read.side_effect = Exception("Test error")
132+
133+
# Verify the exception is propagated
134+
with pytest.raises(Exception, match="Test error"):
135+
manifest_runner.test_read(
136+
config=sample_config,
137+
catalog=sample_catalog,
138+
state=sample_state,
139+
record_limit=100,
140+
page_limit=5,
141+
slice_limit=10,
142+
)
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
from unittest.mock import Mock, patch
2+
3+
from airbyte_cdk.manifest_runner.manifest_runner.utils import (
4+
SHOULD_MIGRATE_KEY,
5+
SHOULD_NORMALIZE_KEY,
6+
build_catalog,
7+
build_source,
8+
)
9+
10+
11+
class TestManifestUtils:
12+
"""Test cases for the manifest_runner utils module."""
13+
14+
def test_build_catalog_creates_correct_structure(self):
15+
"""Test that build_catalog creates a properly structured ConfiguredAirbyteCatalog."""
16+
stream_name = "test_stream"
17+
catalog = build_catalog(stream_name)
18+
19+
# Verify catalog structure
20+
assert len(catalog.streams) == 1
21+
22+
configured_stream = catalog.streams[0]
23+
assert configured_stream.stream.name == stream_name
24+
assert configured_stream.stream.json_schema == {}
25+
26+
# Verify sync modes
27+
from airbyte_cdk.models.airbyte_protocol import DestinationSyncMode, SyncMode
28+
29+
assert SyncMode.full_refresh in configured_stream.stream.supported_sync_modes
30+
assert SyncMode.incremental in configured_stream.stream.supported_sync_modes
31+
assert configured_stream.sync_mode == SyncMode.incremental
32+
assert configured_stream.destination_sync_mode == DestinationSyncMode.overwrite
33+
34+
@patch("airbyte_cdk.manifest_runner.manifest_runner.utils.ManifestDeclarativeSource")
35+
@patch("airbyte_cdk.manifest_runner.manifest_runner.utils.ModelToComponentFactory")
36+
def test_build_source_creates_manifest_declarative_source(
37+
self, mock_component_factory_class, mock_source_class
38+
):
39+
"""Test that build_source creates a ManifestDeclarativeSource with correct parameters."""
40+
# Setup mocks
41+
mock_component_factory = Mock()
42+
mock_component_factory_class.return_value = mock_component_factory
43+
mock_source = Mock()
44+
mock_source_class.return_value = mock_source
45+
46+
# Test with complex manifest and config structures
47+
manifest = {
48+
"version": "0.1.0",
49+
"definitions": {"selector": {"extractor": {"field_path": ["data"]}}},
50+
"streams": [
51+
{
52+
"name": "users",
53+
"primary_key": "id",
54+
"retriever": {
55+
"requester": {
56+
"url_base": "https://api.example.com",
57+
"path": "/users",
58+
}
59+
},
60+
}
61+
],
62+
"check": {"stream_names": ["users"]},
63+
}
64+
65+
config = {
66+
"api_key": "sk-test-123",
67+
"base_url": "https://api.example.com",
68+
"timeout": 30,
69+
}
70+
71+
# Call build_source
72+
result = build_source(manifest, config)
73+
74+
# Verify ModelToComponentFactory was created with correct parameters
75+
mock_component_factory_class.assert_called_once_with(
76+
emit_connector_builder_messages=True,
77+
limit_pages_fetched_per_slice=None,
78+
limit_slices_fetched=None,
79+
disable_retries=True,
80+
disable_cache=True,
81+
)
82+
83+
# Verify ManifestDeclarativeSource was created with correct parameters
84+
mock_source_class.assert_called_once_with(
85+
source_config=manifest,
86+
config=config,
87+
normalize_manifest=False, # Default when flag not set
88+
migrate_manifest=False, # Default when flag not set
89+
emit_connector_builder_messages=True,
90+
component_factory=mock_component_factory,
91+
)
92+
93+
assert result == mock_source
94+
95+
@patch("airbyte_cdk.manifest_runner.manifest_runner.utils.ManifestDeclarativeSource")
96+
@patch("airbyte_cdk.manifest_runner.manifest_runner.utils.ModelToComponentFactory")
97+
def test_build_source_with_normalize_flag(
98+
self, mock_component_factory_class, mock_source_class
99+
):
100+
"""Test build_source when normalize flag is set."""
101+
mock_component_factory = Mock()
102+
mock_component_factory_class.return_value = mock_component_factory
103+
mock_source = Mock()
104+
mock_source_class.return_value = mock_source
105+
106+
manifest = {"streams": [{"name": "test_stream"}], SHOULD_NORMALIZE_KEY: True}
107+
config = {"api_key": "test_key"}
108+
109+
build_source(manifest, config)
110+
111+
# Verify normalize_manifest is True
112+
call_args = mock_source_class.call_args[1]
113+
assert call_args["normalize_manifest"] is True
114+
assert call_args["migrate_manifest"] is False
115+
116+
@patch("airbyte_cdk.manifest_runner.manifest_runner.utils.ManifestDeclarativeSource")
117+
@patch("airbyte_cdk.manifest_runner.manifest_runner.utils.ModelToComponentFactory")
118+
def test_build_source_with_migrate_flag(self, mock_component_factory_class, mock_source_class):
119+
"""Test build_source when migrate flag is set."""
120+
mock_component_factory = Mock()
121+
mock_component_factory_class.return_value = mock_component_factory
122+
mock_source = Mock()
123+
mock_source_class.return_value = mock_source
124+
125+
manifest = {"streams": [{"name": "test_stream"}], SHOULD_MIGRATE_KEY: True}
126+
config = {"api_key": "test_key"}
127+
128+
build_source(manifest, config)
129+
130+
# Verify migrate_manifest is True
131+
call_args = mock_source_class.call_args[1]
132+
assert call_args["normalize_manifest"] is False
133+
assert call_args["migrate_manifest"] is True
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Tests for the routers package."""

0 commit comments

Comments
 (0)