diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4f47f8e..f35965c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,8 @@ jobs: poetry run pre-commit run -a test: + env: + AIRTABLE_INTEGRATION_TEST_TOKEN: "${{secrets.AIRTABLE_INTEGRATION_TEST_TOKEN}}" runs-on: ubuntu-latest strategy: matrix: diff --git a/pyproject.toml b/pyproject.toml index 3e1e200..9180fe3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,15 @@ addopts = [ "--cov-report=lcov", ] +# Singer SDK's deprecation warning +filterwarnings = [ + "ignore:pkg_resources is deprecated as an API:DeprecationWarning", + "ignore:.*pkg_resources.declare_namespace.*:DeprecationWarning", + "ignore:jsonschema.RefResolver:DeprecationWarning", + "ignore:Importing Validator directly:DeprecationWarning", +] + + [tool.poetry.scripts] # CLI declaration tap-airtable = 'tap_airtable.tap:TapAirtable.cli' diff --git a/tap_airtable/tests/test_tap.py b/tap_airtable/tests/test_tap.py deleted file mode 100644 index b62077c..0000000 --- a/tap_airtable/tests/test_tap.py +++ /dev/null @@ -1,5 +0,0 @@ -# TODO: Implement tests - - -def test_import() -> None: - from tap_airtable.tap import TapAirtable # noqa: F401 diff --git a/tap_airtable/tests/__init__.py b/tests/__init__.py similarity index 100% rename from tap_airtable/tests/__init__.py rename to tests/__init__.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..5bb3155 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,80 @@ +import os + +import dotenv +import pytest + +from tap_airtable.client import AirtableClient +from tap_airtable.entities import AirtableBase, AirtableField, AirtableTable +from tap_airtable.tap import TapAirtable + +test_base_foo = AirtableBase( + "foo", + "foo", + tables=[ + AirtableTable( + id="table_1", + name="table_1", + fields=[ + AirtableField("singleLineText", "field1", "field1"), + AirtableField("email", "email", "email"), + ], + ), + AirtableTable( + id="table_2", + name="table_2", + fields=[ + AirtableField("dateTime", "created_at", "created_at"), + AirtableField("number", "number", "number"), + AirtableField("singleLineText", "name", "name"), + ], + ), + ], +) + +test_base_bar = AirtableBase( + "bar", + "bar", + tables=[ + AirtableTable( + id="bar_table", + name="bar_table", + fields=[ + AirtableField("singleLineText", "field1", "field1"), + AirtableField("singleLineText", "field2", "field2"), + AirtableField("singleLineText", "field3", "field3"), + AirtableField("singleLineText", "field4", "field4"), + ], + ) + ], +) + + +@pytest.fixture() +def _patch_client(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr("tap_airtable.client.AirtableClient.get_bases", lambda _, __: [test_base_foo, test_base_bar]) + + +@pytest.fixture(name="integration_test_token", scope="session") +def integration_test_token_fixture() -> str: + try: + dotenv.load_dotenv() + token = os.environ["AIRTABLE_INTEGRATION_TEST_TOKEN"] + except KeyError: + pytest.skip("Integration tests cannot run without AIRTABLE_INTEGRATION_TEST_TOKEN variable.") + return token + + +@pytest.fixture() +def integration_test_tap(integration_test_token: str) -> TapAirtable: + return TapAirtable( + config={ + "token": integration_test_token, + "base_ids": ["appwtyvkFPMTyR40b"], + "table_mapping": {"tblWqSoZj2A4uxTSR": "renamed_food"}, + } + ) + + +@pytest.fixture() +def integration_test_client(integration_test_token: str) -> AirtableClient: + return AirtableClient(integration_test_token) diff --git a/tests/test_integration.py b/tests/test_integration.py new file mode 100644 index 0000000..7130d53 --- /dev/null +++ b/tests/test_integration.py @@ -0,0 +1,19 @@ +from tap_airtable.client import AirtableClient +from tap_airtable.tap import TapAirtable + + +def test_discover_streams_integration(integration_test_tap: TapAirtable) -> None: + streams = integration_test_tap.discover_streams() + + assert streams[0].name == "teams" + assert streams[1].name == "renamed_food" + assert streams[2].name == "faker_users" + assert streams[3].name == "faker_cars" + + assert {stream.base_id for stream in streams} == {"appwtyvkFPMTyR40b"} + + +def test_client_get_records(integration_test_client: AirtableClient) -> None: + records = list(integration_test_client.get_records("appwtyvkFPMTyR40b", "tbl3SsrPtyxu3SiX6")) + assert len(records) == 3 + assert set(records[0]["fields"].keys()) == {"Name", "Priority", "Flagged"} diff --git a/tests/test_tap.py b/tests/test_tap.py new file mode 100644 index 0000000..f3c9d02 --- /dev/null +++ b/tests/test_tap.py @@ -0,0 +1,42 @@ +import pytest + +from tap_airtable.tap import TapAirtable + + +@pytest.mark.usefixtures("_patch_client") +def test_discover_streams() -> None: + tap = TapAirtable(config={"token": "token"}) + streams = tap.discover_streams() + assert len(streams) == 3 + assert streams[0].name == "table_1" + assert streams[0].base_id == "foo" + assert streams[0].original_airtable_table.name == "table_1" + + assert streams[1].name == "table_2" + assert streams[1].base_id == "foo" + assert streams[1].original_airtable_table.name == "table_2" + + assert streams[2].name == "bar_table" + assert streams[2].base_id == "bar" + assert streams[2].original_airtable_table.name == "bar_table" + + +@pytest.mark.usefixtures("_patch_client") +def test_discover_streams_mapping() -> None: + tap = TapAirtable( + config={ + "token": "token", + "table_mapping": {"table_1": "Table 1", "table_2": "Table 2", "bar_table": "products"}, + } + ) + + streams = tap.discover_streams() + + assert streams[0].original_airtable_table.name == "Table 1" + assert streams[0].name == "table_1" # is slugified + + assert streams[1].original_airtable_table.name == "Table 2" + assert streams[1].name == "table_2" # slugified + + assert streams[2].original_airtable_table.name == "products" + assert streams[2].name == "products"