-
Notifications
You must be signed in to change notification settings - Fork 4.5k
feat(python-sources): add unit integration testing utilities for simplification #43338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
562be14
927f0f6
a298a11
39714a6
6d16578
5cc3e6a
d5a79b1
f30ddcb
f9dd62f
c9bb9ae
f04513a
476f75b
b1ecee5
840871b
d22df31
d485355
330bf1d
00fc44e
a158d09
58d40b1
ea2dda0
315d2ea
2efec5d
3888783
2747636
8fd4435
c779bf0
82ea347
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Copyright (c) 2024 Airbyte, Inc., all rights reserved. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Copyright (c) 2024 Airbyte, Inc., all rights reserved. | ||
|
||
from pydantic import FilePath | ||
|
||
|
||
def get_unit_test_folder(execution_folder: str) -> FilePath: | ||
path = FilePath(execution_folder) | ||
while path.name != "unit_tests": | ||
if path.name == path.root or path.name == path.drive: | ||
raise ValueError(f"Could not find `unit_tests` folder as a parent of {execution_folder}") | ||
path = path.parent | ||
return path | ||
|
||
|
||
def read_resource_file_contents(resource: str, test_location: str) -> str: | ||
"""Read the contents of a test data file from the test resource folder.""" | ||
file_path = str(get_unit_test_folder(test_location) / "resource" / "http" / "response" / f"{resource}") | ||
with open(file_path) as f: | ||
response = f.read() | ||
return response |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Copyright (c) 2024 Airbyte, Inc., all rights reserved. | ||
|
||
import re | ||
from typing import Any, Mapping | ||
|
||
from requests_mock import Mocker | ||
|
||
|
||
def register_mock_responses(mocker: Mocker, http_calls: list[Mapping[str, Mapping[str, Any]]]) -> None: | ||
"""Register a list of HTTP request-response pairs.""" | ||
for call in http_calls: | ||
request, response = call["request"], call["response"] | ||
matcher = re.compile(request["url"]) if request["is_regex"] else request["url"] | ||
mocker.register_uri(request["method"], matcher, **response) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Copyright (c) 2024 Airbyte, Inc., all rights reserved. | ||
|
||
from typing import Any, List, Mapping, Optional | ||
|
||
from airbyte_cdk import AbstractSource | ||
from airbyte_cdk.test.catalog_builder import CatalogBuilder | ||
from airbyte_cdk.test.entrypoint_wrapper import EntrypointOutput, read | ||
from airbyte_protocol.models import AirbyteStateMessage, ConfiguredAirbyteCatalog, SyncMode | ||
|
||
|
||
def catalog(stream_name: str, sync_mode: SyncMode) -> ConfiguredAirbyteCatalog: | ||
"""Create a catalog with a single stream.""" | ||
return CatalogBuilder().with_stream(stream_name, sync_mode).build() | ||
|
||
|
||
def read_records( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find this interface brittle. If at some point we need more than just the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as the other comment above in |
||
source: AbstractSource, | ||
config: Mapping[str, Any], | ||
stream_name: str, | ||
sync_mode: SyncMode, | ||
state: Optional[List[AirbyteStateMessage]] = None, | ||
expecting_exception: bool = False, | ||
) -> EntrypointOutput: | ||
"""Read records from a stream.""" | ||
_catalog = catalog(stream_name, sync_mode) | ||
return read(source, config, _catalog, state, expecting_exception) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes a ton of sense to me to bring these two methods in the CDK since they're reimplemented by all connectors!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am personally skeptic about this one because of the "brittleness" I mentioned here. I feel like the builder is as readable as this method and I don't know what is the benefit of this method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be fine with a builder. what I like about the current approach is we get to remove the duplicated
_read
static methods which is maybe not related to this specific function, so my badThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These functions are what many tests use, I just moved them. These are provided in a common location for people to use them when they don't need the full builder or an
__init__()
with default arguments. It's difficult to generalize everything, but these little functions come handy in many cases, if they are not handy, people can fall back to the builder.