959 lines
53 KiB
Python
959 lines
53 KiB
Python
#
|
|
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
#
|
|
|
|
from copy import deepcopy
|
|
from unittest.mock import mock_open, patch
|
|
|
|
import pytest
|
|
from airbyte_api_client import ApiException
|
|
from airbyte_api_client.model.airbyte_catalog import AirbyteCatalog
|
|
from airbyte_api_client.model.connection_schedule_data_basic_schedule import ConnectionScheduleDataBasicSchedule
|
|
from airbyte_api_client.model.connection_schedule_type import ConnectionScheduleType
|
|
from airbyte_api_client.model.connection_status import ConnectionStatus
|
|
from airbyte_api_client.model.destination_definition_id_request_body import DestinationDefinitionIdRequestBody
|
|
from airbyte_api_client.model.destination_definition_id_with_workspace_id import DestinationDefinitionIdWithWorkspaceId
|
|
from airbyte_api_client.model.namespace_definition_type import NamespaceDefinitionType
|
|
from airbyte_api_client.model.operation_create import OperationCreate
|
|
from airbyte_api_client.model.operator_type import OperatorType
|
|
from airbyte_api_client.model.resource_requirements import ResourceRequirements
|
|
from airbyte_api_client.model.source_definition_id_request_body import SourceDefinitionIdRequestBody
|
|
from airbyte_api_client.model.source_definition_id_with_workspace_id import SourceDefinitionIdWithWorkspaceId
|
|
from airbyte_api_client.model.web_backend_operation_create_or_update import WebBackendOperationCreateOrUpdate
|
|
from octavia_cli.apply import resources, yaml_loaders
|
|
|
|
|
|
class TestResourceState:
|
|
def test_init(self, mocker):
|
|
mocker.patch.object(resources, "os")
|
|
state = resources.ResourceState("config_path", "workspace_id", "resource_id", 123, "config_hash")
|
|
assert state.configuration_path == "config_path"
|
|
assert state.workspace_id == "workspace_id"
|
|
assert state.resource_id == "resource_id"
|
|
assert state.generation_timestamp == 123
|
|
assert state.configuration_hash == "config_hash"
|
|
assert state.path == resources.os.path.join.return_value
|
|
resources.os.path.dirname.assert_called_with("config_path")
|
|
resources.os.path.join.assert_called_with(resources.os.path.dirname.return_value, "state_workspace_id.yaml")
|
|
|
|
@pytest.fixture
|
|
def state(self):
|
|
return resources.ResourceState("config_path", "workspace_id", "resource_id", 123, "config_hash")
|
|
|
|
def test_as_dict(self, state):
|
|
assert state.as_dict() == {
|
|
"configuration_path": state.configuration_path,
|
|
"resource_id": state.resource_id,
|
|
"generation_timestamp": state.generation_timestamp,
|
|
"configuration_hash": state.configuration_hash,
|
|
"workspace_id": state.workspace_id,
|
|
}
|
|
|
|
def test_save(self, mocker, state):
|
|
mocker.patch.object(resources, "yaml")
|
|
mocker.patch.object(state, "as_dict")
|
|
|
|
expected_content = state.as_dict.return_value
|
|
with patch("builtins.open", mock_open()) as mock_file:
|
|
state._save()
|
|
mock_file.assert_called_with(state.path, "w")
|
|
resources.yaml.dump.assert_called_with(expected_content, mock_file.return_value)
|
|
|
|
def test_create(self, mocker):
|
|
mocker.patch.object(resources.time, "time", mocker.Mock(return_value=0))
|
|
mocker.patch.object(resources.ResourceState, "_save")
|
|
state = resources.ResourceState.create("config_path", "my_hash", "workspace_id", "resource_id")
|
|
assert isinstance(state, resources.ResourceState)
|
|
resources.ResourceState._save.assert_called_once()
|
|
assert state.configuration_path == "config_path"
|
|
assert state.resource_id == "resource_id"
|
|
assert state.generation_timestamp == 0
|
|
assert state.configuration_hash == "my_hash"
|
|
|
|
def test_delete(self, mocker, state):
|
|
mocker.patch.object(resources.os, "remove")
|
|
state.delete()
|
|
resources.os.remove.assert_called_with(state.path)
|
|
|
|
def test_from_file(self, mocker):
|
|
mocker.patch.object(resources, "yaml")
|
|
resources.yaml.safe_load.return_value = {
|
|
"configuration_path": "config_path",
|
|
"resource_id": "resource_id",
|
|
"generation_timestamp": 0,
|
|
"configuration_hash": "my_hash",
|
|
"workspace_id": "workspace_id",
|
|
}
|
|
with patch("builtins.open", mock_open(read_data="data")) as mock_file:
|
|
state = resources.ResourceState.from_file("state_workspace_id.yaml")
|
|
resources.yaml.safe_load.assert_called_with(mock_file.return_value)
|
|
assert isinstance(state, resources.ResourceState)
|
|
assert state.configuration_path == "config_path"
|
|
assert state.resource_id == "resource_id"
|
|
assert state.generation_timestamp == 0
|
|
assert state.configuration_hash == "my_hash"
|
|
|
|
def test__get_path_from_configuration_and_workspace_id(self, mocker):
|
|
mocker.patch.object(resources.os.path, "dirname", mocker.Mock(return_value="my_dir"))
|
|
state_path = resources.ResourceState._get_path_from_configuration_and_workspace_id("config_path", "workspace_id")
|
|
assert state_path == "my_dir/state_workspace_id.yaml"
|
|
resources.os.path.dirname.assert_called_with("config_path")
|
|
|
|
def test_from_configuration_path_and_workspace(self, mocker):
|
|
mocker.patch.object(resources.ResourceState, "_get_path_from_configuration_and_workspace_id")
|
|
mocker.patch.object(resources.ResourceState, "from_file")
|
|
state = resources.ResourceState.from_configuration_path_and_workspace("config_path", "workspace_id")
|
|
assert state == resources.ResourceState.from_file.return_value
|
|
resources.ResourceState.from_file.assert_called_with(
|
|
resources.ResourceState._get_path_from_configuration_and_workspace_id.return_value
|
|
)
|
|
resources.ResourceState._get_path_from_configuration_and_workspace_id.assert_called_with("config_path", "workspace_id")
|
|
|
|
def test_migrate(self, mocker):
|
|
mocker.patch.object(resources.ResourceState, "from_file")
|
|
mocker.patch.object(resources.ResourceState, "create")
|
|
new_state = resources.ResourceState.migrate("old_state_path", "workspace_id")
|
|
resources.ResourceState.from_file.assert_called_with("old_state_path")
|
|
old_state = resources.ResourceState.from_file.return_value
|
|
resources.ResourceState.create.assert_called_with(
|
|
old_state.configuration_path, old_state.configuration_hash, "workspace_id", old_state.resource_id
|
|
)
|
|
old_state.delete.assert_called_once()
|
|
assert new_state == resources.ResourceState.create.return_value
|
|
|
|
|
|
@pytest.fixture
|
|
def local_configuration():
|
|
return {
|
|
"exotic_attribute": "foo",
|
|
"configuration": {"foo": "bar"},
|
|
"resource_name": "bar",
|
|
"definition_id": "bar",
|
|
"definition_image": "fooo",
|
|
"definition_version": "barrr",
|
|
}
|
|
|
|
|
|
class TestBaseResource:
|
|
@pytest.fixture
|
|
def patch_base_class(self, mocker):
|
|
# Mock abstract methods to enable instantiating abstract class
|
|
mocker.patch.object(resources.BaseResource, "__abstractmethods__", set())
|
|
mocker.patch.object(resources.BaseResource, "create_function_name", "create_resource")
|
|
mocker.patch.object(resources.BaseResource, "resource_id_field", "resource_id")
|
|
mocker.patch.object(resources.BaseResource, "update_function_name", "update_resource")
|
|
mocker.patch.object(resources.BaseResource, "get_function_name", "get_resource")
|
|
mocker.patch.object(resources.BaseResource, "resource_type", "universal_resource")
|
|
mocker.patch.object(resources.BaseResource, "api")
|
|
|
|
def test_init_no_remote_resource(self, mocker, patch_base_class, mock_api_client, local_configuration):
|
|
mocker.patch.object(resources.BaseResource, "_get_state_from_file", mocker.Mock(return_value=None))
|
|
mocker.patch.object(resources, "hash_config")
|
|
resource = resources.BaseResource(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
assert resource.APPLY_PRIORITY == 0
|
|
assert resource.workspace_id == "workspace_id"
|
|
assert resource.raw_configuration == local_configuration
|
|
assert resource.configuration_path == "bar.yaml"
|
|
assert resource.api_instance == resource.api.return_value
|
|
resource.api.assert_called_with(mock_api_client)
|
|
assert resource.state == resource._get_state_from_file.return_value
|
|
assert resource.remote_resource is None
|
|
assert resource.was_created is False
|
|
assert resource.local_file_changed is True
|
|
assert resource.resource_id is None
|
|
|
|
def test_init_with_remote_resource_not_changed(self, mocker, patch_base_class, mock_api_client, local_configuration):
|
|
mocker.patch.object(
|
|
resources.BaseResource, "_get_state_from_file", mocker.Mock(return_value=mocker.Mock(configuration_hash="my_hash"))
|
|
)
|
|
mocker.patch.object(resources.BaseResource, "_get_remote_resource", mocker.Mock(return_value={"resource_id": "my_resource_id"}))
|
|
|
|
mocker.patch.object(resources, "hash_config", mocker.Mock(return_value="my_hash"))
|
|
resource = resources.BaseResource(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
assert resource.was_created is True
|
|
assert resource.local_file_changed is False
|
|
assert resource.resource_id == resource.state.resource_id
|
|
|
|
def test_init_with_remote_resource_changed(self, mocker, patch_base_class, mock_api_client, local_configuration):
|
|
mocker.patch.object(
|
|
resources.BaseResource,
|
|
"_get_state_from_file",
|
|
mocker.Mock(return_value=mocker.Mock(configuration_hash="my_state_hash")),
|
|
)
|
|
mocker.patch.object(resources.BaseResource, "_get_remote_resource", mocker.Mock(return_value={"resource_id": "my_resource_id"}))
|
|
mocker.patch.object(resources, "hash_config", mocker.Mock(return_value="my_new_hash"))
|
|
resource = resources.BaseResource(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
assert resource.was_created is True
|
|
assert resource.local_file_changed is True
|
|
assert resource.resource_id == resource.state.resource_id
|
|
|
|
@pytest.fixture
|
|
def resource(self, patch_base_class, mock_api_client, local_configuration):
|
|
return resources.BaseResource(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
|
|
def test_get_remote_resource(self, resource, mocker):
|
|
mocker.patch.object(resource, "_get_fn")
|
|
remote_resource = resource._get_remote_resource()
|
|
assert remote_resource == resource._get_fn.return_value
|
|
resource._get_fn.assert_called_with(resource.api_instance, resource.get_payload)
|
|
|
|
@pytest.mark.parametrize(
|
|
"state_path_is_file, legacy_state_path_is_file, confirm_migration",
|
|
[(True, False, False), (False, True, True), (False, True, False), (False, False, False)],
|
|
)
|
|
def test_get_state_from_file(self, mocker, resource, state_path_is_file, legacy_state_path_is_file, confirm_migration):
|
|
mocker.patch.object(resources, "os")
|
|
mocker.patch.object(resources.click, "confirm", mocker.Mock(return_value=confirm_migration))
|
|
mock_expected_state_path = mocker.Mock(is_file=mocker.Mock(return_value=state_path_is_file))
|
|
mock_expected_legacy_state_path = mocker.Mock(is_file=mocker.Mock(return_value=legacy_state_path_is_file))
|
|
mocker.patch.object(resources, "Path", mocker.Mock(side_effect=[mock_expected_state_path, mock_expected_legacy_state_path]))
|
|
mocker.patch.object(resources, "ResourceState")
|
|
|
|
if legacy_state_path_is_file and not confirm_migration:
|
|
with pytest.raises(resources.InvalidStateError):
|
|
state = resource._get_state_from_file(resource.configuration_path, resource.workspace_id)
|
|
else:
|
|
state = resource._get_state_from_file(resource.configuration_path, resource.workspace_id)
|
|
|
|
resources.os.path.dirname.assert_called_with(resource.configuration_path)
|
|
resources.os.path.join.assert_has_calls(
|
|
[
|
|
mocker.call(resources.os.path.dirname.return_value, f"state_{resource.workspace_id}.yaml"),
|
|
mocker.call(resources.os.path.dirname.return_value, "state.yaml"),
|
|
]
|
|
)
|
|
resources.Path.assert_called_with(resources.os.path.join.return_value)
|
|
mock_expected_state_path.is_file.assert_called_once()
|
|
if state_path_is_file:
|
|
resources.ResourceState.from_file.assert_called_with(mock_expected_state_path)
|
|
assert state == resources.ResourceState.from_file.return_value
|
|
mock_expected_legacy_state_path.is_file.assert_not_called()
|
|
elif legacy_state_path_is_file:
|
|
if confirm_migration:
|
|
mock_expected_legacy_state_path.is_file.assert_called_once()
|
|
resources.ResourceState.migrate.assert_called_with(mock_expected_legacy_state_path, resource.workspace_id)
|
|
assert state == resources.ResourceState.migrate.return_value
|
|
else:
|
|
assert state is None
|
|
|
|
@pytest.mark.parametrize(
|
|
"was_created",
|
|
[True, False],
|
|
)
|
|
def test_get_diff_with_remote_resource(self, patch_base_class, mocker, mock_api_client, local_configuration, was_created):
|
|
mocker.patch.object(resources.BaseResource, "_get_remote_comparable_configuration")
|
|
mocker.patch.object(resources.BaseResource, "was_created", was_created)
|
|
resource = resources.BaseResource(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
mocker.patch.object(resources, "compute_diff")
|
|
if was_created:
|
|
diff = resource.get_diff_with_remote_resource()
|
|
resources.compute_diff.assert_called_with(resource._get_remote_comparable_configuration.return_value, resource.configuration)
|
|
assert diff == resources.compute_diff.return_value.pretty.return_value
|
|
else:
|
|
with pytest.raises(resources.NonExistingResourceError):
|
|
resource.get_diff_with_remote_resource()
|
|
|
|
def test_create_or_update(self, mocker, resource):
|
|
expected_results = {resource.resource_id_field: "resource_id"}
|
|
operation_fn = mocker.Mock(return_value=expected_results)
|
|
mocker.patch.object(resources, "ResourceState")
|
|
payload = "foo"
|
|
result, state = resource._create_or_update(operation_fn, payload)
|
|
assert result == expected_results
|
|
assert state == resources.ResourceState.create.return_value
|
|
resources.ResourceState.create.assert_called_with(
|
|
resource.configuration_path, resource.configuration_hash, resource.workspace_id, "resource_id"
|
|
)
|
|
|
|
@pytest.mark.parametrize(
|
|
"response_status,expected_error",
|
|
[(404, ApiException), (422, resources.InvalidConfigurationError)],
|
|
)
|
|
def test_create_or_update_error(self, mocker, resource, response_status, expected_error):
|
|
operation_fn = mocker.Mock(side_effect=ApiException(status=response_status))
|
|
mocker.patch.object(resources, "ResourceState")
|
|
with pytest.raises(expected_error):
|
|
resource._create_or_update(operation_fn, "foo")
|
|
|
|
def test_create(self, mocker, resource):
|
|
mocker.patch.object(resource, "_create_or_update")
|
|
assert resource.create() == resource._create_or_update.return_value
|
|
resource._create_or_update.assert_called_with(resource._create_fn, resource.create_payload)
|
|
|
|
def test_update(self, mocker, resource):
|
|
mocker.patch.object(resource, "_create_or_update")
|
|
assert resource.update() == resource._create_or_update.return_value
|
|
resource._create_or_update.assert_called_with(resource._update_fn, resource.update_payload)
|
|
|
|
def test_manage(self, mocker, resource):
|
|
mocker.patch.object(resources, "ResourceState")
|
|
remote_resource, new_state = resource.manage("resource_id")
|
|
resources.ResourceState.create.assert_called_with(
|
|
resource.configuration_path, resource.configuration_hash, resource.workspace_id, "resource_id"
|
|
)
|
|
assert new_state == resources.ResourceState.create.return_value
|
|
assert remote_resource == resource.remote_resource
|
|
|
|
@pytest.mark.parametrize(
|
|
"configuration, invalid_keys, expect_error",
|
|
[
|
|
({"valid_key": "foo", "invalidKey": "bar"}, {"invalidKey"}, True),
|
|
({"valid_key": "foo", "invalidKey": "bar", "secondInvalidKey": "bar"}, {"invalidKey", "secondInvalidKey"}, True),
|
|
({"valid_key": "foo", "validKey": "bar"}, {"invalidKey"}, False),
|
|
],
|
|
)
|
|
def test__check_for_invalid_configuration_keys(self, configuration, invalid_keys, expect_error):
|
|
if not expect_error:
|
|
result = resources.BaseResource._check_for_invalid_configuration_keys(configuration, invalid_keys, "Invalid configuration keys")
|
|
assert result is None
|
|
else:
|
|
with pytest.raises(resources.InvalidConfigurationError, match="Invalid configuration keys") as error_info:
|
|
resources.BaseResource._check_for_invalid_configuration_keys(configuration, invalid_keys, "Invalid configuration keys")
|
|
assert all([invalid_key in str(error_info) for invalid_key in invalid_keys])
|
|
|
|
|
|
class TestSourceAndDestination:
|
|
@pytest.fixture
|
|
def patch_source_and_destination(self, mocker):
|
|
mocker.patch.object(resources.SourceAndDestination, "__abstractmethods__", set())
|
|
mocker.patch.object(resources.SourceAndDestination, "api")
|
|
mocker.patch.object(resources.SourceAndDestination, "create_function_name", "create")
|
|
mocker.patch.object(resources.SourceAndDestination, "update_function_name", "update")
|
|
mocker.patch.object(resources.SourceAndDestination, "get_function_name", "get")
|
|
mocker.patch.object(resources.SourceAndDestination, "_get_state_from_file", mocker.Mock(return_value=None))
|
|
mocker.patch.object(resources, "hash_config")
|
|
|
|
def test_init(self, patch_source_and_destination, mocker, mock_api_client, local_configuration):
|
|
assert resources.SourceAndDestination.__base__ == resources.BaseResource
|
|
resource = resources.SourceAndDestination(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
assert resource.definition_id == local_configuration["definition_id"]
|
|
assert resource.definition_image == local_configuration["definition_image"]
|
|
assert resource.definition_version == local_configuration["definition_version"]
|
|
|
|
def test_get_remote_comparable_configuration(self, patch_source_and_destination, mocker, mock_api_client, local_configuration):
|
|
mocker.patch.object(resources.Source, "remote_resource")
|
|
resource = resources.Source(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
assert resource._get_remote_comparable_configuration() == resource.remote_resource.connection_configuration
|
|
|
|
|
|
class TestSource:
|
|
@pytest.mark.parametrize(
|
|
"state",
|
|
[None, resources.ResourceState("config_path", "workspace_id", "resource_id", 123, "abc")],
|
|
)
|
|
def test_init(self, mocker, mock_api_client, local_configuration, state):
|
|
assert resources.Source.__base__ == resources.SourceAndDestination
|
|
mocker.patch.object(resources.Source, "resource_id", "foo")
|
|
source = resources.Source(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
mocker.patch.object(source, "state", state)
|
|
assert source.api == resources.source_api.SourceApi
|
|
assert source.create_function_name == "create_source"
|
|
assert source.resource_id_field == "source_id"
|
|
assert source.update_function_name == "update_source"
|
|
assert source.resource_type == "source"
|
|
assert source.APPLY_PRIORITY == 0
|
|
assert source.create_payload == resources.SourceCreate(
|
|
source.definition_id, source.configuration, source.workspace_id, source.resource_name
|
|
)
|
|
assert source.update_payload == resources.SourceUpdate(
|
|
source_id=source.resource_id, connection_configuration=source.configuration, name=source.resource_name
|
|
)
|
|
if state is None:
|
|
assert source.get_payload is None
|
|
else:
|
|
assert source.get_payload == resources.SourceIdRequestBody(state.resource_id)
|
|
|
|
@pytest.mark.parametrize(
|
|
"resource_id",
|
|
[None, "foo"],
|
|
)
|
|
def test_source_discover_schema_request_body(self, mocker, mock_api_client, resource_id, local_configuration):
|
|
mocker.patch.object(resources, "SourceDiscoverSchemaRequestBody")
|
|
mocker.patch.object(resources.Source, "resource_id", resource_id)
|
|
source = resources.Source(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
if resource_id is None:
|
|
with pytest.raises(resources.NonExistingResourceError):
|
|
source.source_discover_schema_request_body
|
|
resources.SourceDiscoverSchemaRequestBody.assert_not_called()
|
|
else:
|
|
assert source.source_discover_schema_request_body == resources.SourceDiscoverSchemaRequestBody.return_value
|
|
resources.SourceDiscoverSchemaRequestBody.assert_called_with(source.resource_id)
|
|
|
|
def test_catalog(self, mocker, mock_api_client, local_configuration):
|
|
mocker.patch.object(resources.Source, "source_discover_schema_request_body")
|
|
source = resources.Source(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
source.api_instance = mocker.Mock()
|
|
catalog = source.catalog
|
|
assert catalog == source.api_instance.discover_schema_for_source.return_value.catalog
|
|
source.api_instance.discover_schema_for_source.assert_called_with(source.source_discover_schema_request_body)
|
|
|
|
def test_definition(self, mocker, mock_api_client, local_configuration):
|
|
mocker.patch.object(resources.source_definition_api, "SourceDefinitionApi")
|
|
mock_api_instance = resources.source_definition_api.SourceDefinitionApi.return_value
|
|
source = resources.Source(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
assert source.definition == mock_api_instance.get_source_definition.return_value
|
|
resources.source_definition_api.SourceDefinitionApi.assert_called_with(mock_api_client)
|
|
expected_payload = SourceDefinitionIdRequestBody(source_definition_id=source.definition_id)
|
|
mock_api_instance.get_source_definition.assert_called_with(expected_payload)
|
|
|
|
def test_definition_specification(self, mocker, mock_api_client, local_configuration):
|
|
mocker.patch.object(resources.source_definition_specification_api, "SourceDefinitionSpecificationApi")
|
|
mock_api_instance = resources.source_definition_specification_api.SourceDefinitionSpecificationApi.return_value
|
|
source = resources.Source(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
assert source.definition_specification == mock_api_instance.get_source_definition_specification.return_value
|
|
resources.source_definition_specification_api.SourceDefinitionSpecificationApi.assert_called_with(mock_api_client)
|
|
expected_payload = SourceDefinitionIdWithWorkspaceId(source_definition_id=source.definition_id, workspace_id=source.workspace_id)
|
|
mock_api_instance.get_source_definition_specification.assert_called_with(expected_payload)
|
|
|
|
|
|
class TestDestination:
|
|
@pytest.mark.parametrize(
|
|
"state",
|
|
[None, resources.ResourceState("config_path", "workspace_id", "resource_id", 123, "abc")],
|
|
)
|
|
def test_init(self, mocker, mock_api_client, local_configuration, state):
|
|
assert resources.Destination.__base__ == resources.SourceAndDestination
|
|
mocker.patch.object(resources.Destination, "resource_id", "foo")
|
|
destination = resources.Destination(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
mocker.patch.object(destination, "state", state)
|
|
assert destination.api == resources.destination_api.DestinationApi
|
|
assert destination.create_function_name == "create_destination"
|
|
assert destination.resource_id_field == "destination_id"
|
|
assert destination.update_function_name == "update_destination"
|
|
assert destination.resource_type == "destination"
|
|
assert destination.APPLY_PRIORITY == 0
|
|
assert destination.create_payload == resources.DestinationCreate(
|
|
destination.workspace_id, destination.resource_name, destination.definition_id, destination.configuration
|
|
)
|
|
assert destination.update_payload == resources.DestinationUpdate(
|
|
destination_id=destination.resource_id, connection_configuration=destination.configuration, name=destination.resource_name
|
|
)
|
|
if state is None:
|
|
assert destination.get_payload is None
|
|
else:
|
|
assert destination.get_payload == resources.DestinationIdRequestBody(state.resource_id)
|
|
|
|
def test_definition(self, mocker, mock_api_client, local_configuration):
|
|
mocker.patch.object(resources.destination_definition_api, "DestinationDefinitionApi")
|
|
mock_api_instance = resources.destination_definition_api.DestinationDefinitionApi.return_value
|
|
destination = resources.Destination(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
assert destination.definition == mock_api_instance.get_destination_definition.return_value
|
|
resources.destination_definition_api.DestinationDefinitionApi.assert_called_with(mock_api_client)
|
|
expected_payload = DestinationDefinitionIdRequestBody(
|
|
destination_definition_id=destination.definition_id
|
|
)
|
|
mock_api_instance.get_destination_definition.assert_called_with(expected_payload)
|
|
|
|
def test_definition_specification(self, mocker, mock_api_client, local_configuration):
|
|
mocker.patch.object(resources.destination_definition_specification_api, "DestinationDefinitionSpecificationApi")
|
|
mock_api_instance = resources.destination_definition_specification_api.DestinationDefinitionSpecificationApi.return_value
|
|
destination = resources.Destination(mock_api_client, "workspace_id", local_configuration, "bar.yaml")
|
|
assert destination.definition_specification == mock_api_instance.get_destination_definition_specification.return_value
|
|
resources.destination_definition_specification_api.DestinationDefinitionSpecificationApi.assert_called_with(mock_api_client)
|
|
expected_payload = DestinationDefinitionIdWithWorkspaceId(
|
|
destination_definition_id=destination.definition_id, workspace_id=destination.workspace_id
|
|
)
|
|
mock_api_instance.get_destination_definition_specification.assert_called_with(expected_payload)
|
|
|
|
|
|
class TestConnection:
|
|
@pytest.fixture
|
|
def connection_configuration(self):
|
|
return {
|
|
"definition_type": "connection",
|
|
"resource_name": "my_connection",
|
|
"source_configuration_path": "my_source_configuration_path",
|
|
"destination_configuration_path": "my_destination_configuration_path",
|
|
"configuration": {
|
|
"namespace_definition": "customformat",
|
|
"namespace_format": "foo",
|
|
"prefix": "foo",
|
|
"sync_catalog": {
|
|
"streams": [
|
|
{
|
|
"stream": {
|
|
"name": "name_example",
|
|
"json_schema": {},
|
|
"supported_sync_modes": ["incremental"],
|
|
"source_defined_cursor": True,
|
|
"default_cursor_field": ["default_cursor_field"],
|
|
"source_defined_primary_key": [["string_example"]],
|
|
"namespace": "namespace_example",
|
|
},
|
|
"config": {
|
|
"sync_mode": "incremental",
|
|
"cursor_field": ["cursor_field_example"],
|
|
"destination_sync_mode": "append_dedup",
|
|
"primary_key": [["string_example"]],
|
|
"alias_name": "alias_name_example",
|
|
"selected": True,
|
|
},
|
|
}
|
|
]
|
|
},
|
|
"schedule_type": "basic",
|
|
"schedule_data": {"units": 1, "time_unit": "days"},
|
|
"status": "active",
|
|
"resource_requirements": {"cpu_request": "foo", "cpu_limit": "foo", "memory_request": "foo", "memory_limit": "foo"},
|
|
},
|
|
}
|
|
|
|
@pytest.fixture
|
|
def connection_configuration_with_manual_schedule(self, connection_configuration):
|
|
connection_configuration_with_manual_schedule = deepcopy(connection_configuration)
|
|
connection_configuration_with_manual_schedule["configuration"]["schedule_type"] = "manual"
|
|
connection_configuration_with_manual_schedule["configuration"]["schedule_data"] = None
|
|
return connection_configuration_with_manual_schedule
|
|
|
|
@pytest.fixture
|
|
def connection_configuration_with_normalization(self, connection_configuration):
|
|
connection_configuration_with_normalization = deepcopy(connection_configuration)
|
|
connection_configuration_with_normalization["configuration"]["operations"] = [
|
|
{"name": "Normalization", "operator_configuration": {"normalization": {"option": "basic"}, "operator_type": "normalization"}}
|
|
]
|
|
return connection_configuration_with_normalization
|
|
|
|
@pytest.fixture
|
|
def legacy_connection_configurations(self):
|
|
return [
|
|
{
|
|
"definition_type": "connection",
|
|
"resource_name": "my_connection",
|
|
"source_id": "my_source",
|
|
"destination_id": "my_destination",
|
|
"configuration": {
|
|
"namespaceDefinition": "customformat",
|
|
"namespaceFormat": "foo",
|
|
"prefix": "foo",
|
|
"syncCatalog": {
|
|
"streams": [
|
|
{
|
|
"stream": {
|
|
"name": "name_example",
|
|
"json_schema": {},
|
|
"supported_sync_modes": ["incremental"],
|
|
"source_defined_cursor": True,
|
|
"default_cursor_field": ["default_cursor_field"],
|
|
"source_defined_primary_key": [["string_example"]],
|
|
"namespace": "namespace_example",
|
|
},
|
|
"config": {
|
|
"sync_mode": "incremental",
|
|
"cursor_field": ["cursor_field_example"],
|
|
"destination_sync_mode": "append_dedup",
|
|
"primary_key": [["string_example"]],
|
|
"alias_name": "alias_name_example",
|
|
"selected": True,
|
|
},
|
|
}
|
|
]
|
|
},
|
|
"schedule": {"units": 1, "time_unit": "days"},
|
|
"status": "active",
|
|
"resourceRequirements": {"cpu_request": "foo", "cpu_limit": "foo", "memory_request": "foo", "memory_limit": "foo"},
|
|
},
|
|
},
|
|
{
|
|
"definition_type": "connection",
|
|
"resource_name": "my_connection",
|
|
"source_id": "my_source",
|
|
"destination_id": "my_destination",
|
|
"configuration": {
|
|
"namespace_definition": "customformat",
|
|
"namespace_format": "foo",
|
|
"prefix": "foo",
|
|
"sync_catalog": {
|
|
"streams": [
|
|
{
|
|
"stream": {
|
|
"name": "name_example",
|
|
"jsonSchema": {},
|
|
"supportedSyncModes": ["incremental"],
|
|
"sourceDefinedCursor": True,
|
|
"defaultCursorField": ["default_cursor_field"],
|
|
"sourceDefinedPrimary_key": [["string_example"]],
|
|
"namespace": "namespace_example",
|
|
},
|
|
"config": {
|
|
"syncMode": "incremental",
|
|
"cursorField": ["cursor_field_example"],
|
|
"destinationSyncMode": "append_dedup",
|
|
"primaryKey": [["string_example"]],
|
|
"aliasName": "alias_name_example",
|
|
"selected": True,
|
|
},
|
|
}
|
|
]
|
|
},
|
|
"schedule": {"units": 1, "time_unit": "days"},
|
|
"status": "active",
|
|
"resource_requirements": {"cpu_request": "foo", "cpu_limit": "foo", "memory_request": "foo", "memory_limit": "foo"},
|
|
},
|
|
},
|
|
{
|
|
"definition_type": "connection",
|
|
"resource_name": "my_connection",
|
|
"source_id": "my_source",
|
|
"destination_id": "my_destination",
|
|
"configuration": {
|
|
"namespace_definition": "customformat",
|
|
"namespace_format": "foo",
|
|
"prefix": "foo",
|
|
"sync_catalog": {
|
|
"streams": [
|
|
{
|
|
"stream": {},
|
|
"config": {},
|
|
}
|
|
]
|
|
},
|
|
"schedule": {"units": 1, "time_unit": "days"},
|
|
"status": "active",
|
|
"resource_requirements": {"cpu_request": "foo", "cpu_limit": "foo", "memory_request": "foo", "memory_limit": "foo"},
|
|
},
|
|
},
|
|
]
|
|
|
|
@pytest.mark.parametrize(
|
|
"state",
|
|
[None, resources.ResourceState("config_path", "workspace_id", "resource_id", 123, "abc")],
|
|
)
|
|
def test_init(self, mocker, mock_api_client, state, connection_configuration):
|
|
assert resources.Connection.__base__ == resources.BaseResource
|
|
mocker.patch.object(resources.Connection, "resource_id", "foo")
|
|
connection = resources.Connection(mock_api_client, "workspace_id", connection_configuration, "bar.yaml")
|
|
mocker.patch.object(connection, "state", state)
|
|
assert connection.api == resources.web_backend_api.WebBackendApi
|
|
assert connection.create_function_name == "web_backend_create_connection"
|
|
assert connection.update_function_name == "web_backend_update_connection"
|
|
assert connection.resource_id_field == "connection_id"
|
|
assert connection.resource_type == "connection"
|
|
assert connection.APPLY_PRIORITY == 1
|
|
|
|
assert connection.update_payload == resources.WebBackendConnectionUpdate(
|
|
connection_id=connection.resource_id, **connection.configuration
|
|
)
|
|
if state is None:
|
|
assert connection.get_payload is None
|
|
else:
|
|
assert connection.get_payload == resources.WebBackendConnectionRequestBody(
|
|
connection_id=state.resource_id, with_refreshed_catalog=False
|
|
)
|
|
|
|
@pytest.mark.parametrize("file_not_found_error", [False, True])
|
|
def test_source_id(self, mocker, mock_api_client, connection_configuration, file_not_found_error):
|
|
assert resources.Connection.__base__ == resources.BaseResource
|
|
mocker.patch.object(resources.Connection, "resource_id", "foo")
|
|
if file_not_found_error:
|
|
mocker.patch.object(
|
|
resources.ResourceState, "from_configuration_path_and_workspace", mocker.Mock(side_effect=FileNotFoundError())
|
|
)
|
|
else:
|
|
mocker.patch.object(
|
|
resources.ResourceState,
|
|
"from_configuration_path_and_workspace",
|
|
mocker.Mock(return_value=mocker.Mock(resource_id="expected_source_id")),
|
|
)
|
|
|
|
connection = resources.Connection(mock_api_client, "workspace_id", connection_configuration, "bar.yaml")
|
|
if file_not_found_error:
|
|
with pytest.raises(resources.MissingStateError):
|
|
connection.source_id
|
|
else:
|
|
source_id = connection.source_id
|
|
assert source_id == "expected_source_id"
|
|
resources.ResourceState.from_configuration_path_and_workspace.assert_called_with(
|
|
connection_configuration["source_configuration_path"], connection.workspace_id
|
|
)
|
|
|
|
@pytest.mark.parametrize("file_not_found_error", [False, True])
|
|
def test_destination_id(self, mocker, mock_api_client, connection_configuration, file_not_found_error):
|
|
assert resources.Connection.__base__ == resources.BaseResource
|
|
mocker.patch.object(resources.Connection, "resource_id", "foo")
|
|
if file_not_found_error:
|
|
mocker.patch.object(
|
|
resources.ResourceState, "from_configuration_path_and_workspace", mocker.Mock(side_effect=FileNotFoundError())
|
|
)
|
|
else:
|
|
mocker.patch.object(
|
|
resources.ResourceState,
|
|
"from_configuration_path_and_workspace",
|
|
mocker.Mock(return_value=mocker.Mock(resource_id="expected_destination_id")),
|
|
)
|
|
|
|
connection = resources.Connection(mock_api_client, "workspace_id", connection_configuration, "bar.yaml")
|
|
if file_not_found_error:
|
|
with pytest.raises(resources.MissingStateError):
|
|
connection.destination_id
|
|
else:
|
|
destination_id = connection.destination_id
|
|
assert destination_id == "expected_destination_id"
|
|
resources.ResourceState.from_configuration_path_and_workspace.assert_called_with(
|
|
connection_configuration["destination_configuration_path"], connection.workspace_id
|
|
)
|
|
|
|
def test_create_payload_no_normalization(self, mocker, mock_api_client, connection_configuration):
|
|
assert resources.Connection.__base__ == resources.BaseResource
|
|
mocker.patch.object(resources.Connection, "resource_id", "foo")
|
|
mocker.patch.object(resources.Connection, "source_id", "source_id")
|
|
mocker.patch.object(resources.Connection, "destination_id", "destination_id")
|
|
connection = resources.Connection(mock_api_client, "workspace_id", connection_configuration, "bar.yaml")
|
|
assert connection.create_payload == resources.WebBackendConnectionCreate(
|
|
name=connection.resource_name,
|
|
source_id=connection.source_id,
|
|
destination_id=connection.destination_id,
|
|
**connection.configuration,
|
|
)
|
|
assert "operations" not in connection.create_payload
|
|
|
|
def test_create_payload_with_normalization(self, mocker, mock_api_client, connection_configuration_with_normalization):
|
|
assert resources.Connection.__base__ == resources.BaseResource
|
|
mocker.patch.object(resources.Connection, "resource_id", "foo")
|
|
mocker.patch.object(resources.Connection, "source_id", "source_id")
|
|
mocker.patch.object(resources.Connection, "destination_id", "destination_id")
|
|
connection = resources.Connection(mock_api_client, "workspace_id", connection_configuration_with_normalization, "bar.yaml")
|
|
assert connection.create_payload == resources.WebBackendConnectionCreate(
|
|
name=connection.resource_name,
|
|
source_id=connection.source_id,
|
|
destination_id=connection.destination_id,
|
|
**connection.configuration,
|
|
)
|
|
assert isinstance(connection.create_payload["operations"][0], OperationCreate)
|
|
|
|
def test_update_payload_no_normalization(self, mocker, mock_api_client, connection_configuration):
|
|
assert resources.Connection.__base__ == resources.BaseResource
|
|
mocker.patch.object(resources.Connection, "resource_id", "foo")
|
|
mocker.patch.object(resources.Connection, "source_id", "source_id")
|
|
mocker.patch.object(resources.Connection, "destination_id", "destination_id")
|
|
connection = resources.Connection(mock_api_client, "workspace_id", connection_configuration, "bar.yaml")
|
|
assert connection.update_payload == resources.WebBackendConnectionUpdate(
|
|
connection_id=connection.resource_id,
|
|
**connection.configuration,
|
|
)
|
|
assert "operations" not in connection.update_payload
|
|
|
|
def test_update_payload_with_normalization(self, mocker, mock_api_client, connection_configuration_with_normalization):
|
|
assert resources.Connection.__base__ == resources.BaseResource
|
|
mocker.patch.object(resources.Connection, "resource_id", "foo")
|
|
mocker.patch.object(resources.Connection, "source_id", "source_id")
|
|
mocker.patch.object(resources.Connection, "destination_id", "destination_id")
|
|
connection = resources.Connection(mock_api_client, "workspace_id", connection_configuration_with_normalization, "bar.yaml")
|
|
assert connection.update_payload == resources.WebBackendConnectionUpdate(
|
|
connection_id=connection.resource_id,
|
|
**connection.configuration,
|
|
)
|
|
assert isinstance(connection.update_payload["operations"][0], WebBackendOperationCreateOrUpdate)
|
|
|
|
@pytest.mark.parametrize(
|
|
"remote_resource",
|
|
[
|
|
{
|
|
"name": "foo",
|
|
"source_id": "bar",
|
|
"destination_id": "fooo",
|
|
"connection_id": "baar",
|
|
"operation_ids": "foooo",
|
|
"foo": "bar",
|
|
},
|
|
{
|
|
"name": "foo",
|
|
"source_id": "bar",
|
|
"destination_id": "fooo",
|
|
"connection_id": "baar",
|
|
"operation_ids": "foooo",
|
|
"foo": "bar",
|
|
"operations": [],
|
|
},
|
|
{
|
|
"name": "foo",
|
|
"source_id": "bar",
|
|
"destination_id": "fooo",
|
|
"connection_id": "baar",
|
|
"operation_ids": "foooo",
|
|
"foo": "bar",
|
|
"operations": [{"workspace_id": "foo", "operation_id": "foo", "operator_configuration": {"normalization": "foo"}}],
|
|
},
|
|
{
|
|
"name": "foo",
|
|
"source_id": "bar",
|
|
"destination_id": "fooo",
|
|
"connection_id": "baar",
|
|
"operation_ids": "foooo",
|
|
"foo": "bar",
|
|
"operations": [{"workspace_id": "foo", "operation_id": "foo", "operator_configuration": {"dbt": "foo"}}],
|
|
},
|
|
],
|
|
)
|
|
def test_get_remote_comparable_configuration(self, mocker, mock_api_client, connection_configuration, remote_resource):
|
|
mocker.patch.object(
|
|
resources.Connection,
|
|
"remote_resource",
|
|
mocker.Mock(to_dict=mocker.Mock(return_value=remote_resource)),
|
|
)
|
|
resource = resources.Connection(mock_api_client, "workspace_id", connection_configuration, "bar.yaml")
|
|
comparable = resource._get_remote_comparable_configuration()
|
|
resource.remote_resource.to_dict.assert_called_once()
|
|
|
|
assert isinstance(comparable, dict)
|
|
assert all([k not in comparable for k in resource.remote_root_level_keys_to_filter_out_for_comparison])
|
|
if "operations" in remote_resource and "operations" in comparable:
|
|
assert all([k not in comparable["operations"][0] for k in resource.remote_operation_level_keys_to_filter_out])
|
|
if remote_resource["operations"][0]["operator_configuration"].get("normalization") is not None:
|
|
assert "dbt" not in remote_resource["operations"][0]["operator_configuration"]
|
|
if remote_resource["operations"][0]["operator_configuration"].get("dbt") is not None:
|
|
assert "normalization" not in remote_resource["operations"][0]["operator_configuration"]
|
|
if "operations" in remote_resource and len(remote_resource["operations"]) == 0:
|
|
assert "operations" not in comparable
|
|
|
|
def test_create(self, mocker, mock_api_client, connection_configuration):
|
|
mocker.patch.object(resources.Connection, "_create_or_update")
|
|
mocker.patch.object(resources.Connection, "source_id", "source_id")
|
|
mocker.patch.object(resources.Connection, "destination_id", "destination_id")
|
|
resource = resources.Connection(mock_api_client, "workspace_id", connection_configuration, "bar.yaml")
|
|
create_result = resource.create()
|
|
assert create_result == resource._create_or_update.return_value
|
|
resource._create_or_update.assert_called_with(resource._create_fn, resource.create_payload)
|
|
|
|
def test_update(self, mocker, mock_api_client, connection_configuration):
|
|
mocker.patch.object(resources.Connection, "_create_or_update")
|
|
resource = resources.Connection(mock_api_client, "workspace_id", connection_configuration, "bar.yaml")
|
|
resource.state = mocker.Mock(resource_id="foo")
|
|
update_result = resource.update()
|
|
assert update_result == resource._create_or_update.return_value
|
|
resource._create_or_update.assert_called_with(resource._update_fn, resource.update_payload)
|
|
|
|
def test__deserialize_raw_configuration(self, mock_api_client, connection_configuration, connection_configuration_with_manual_schedule):
|
|
resource = resources.Connection(mock_api_client, "workspace_id", connection_configuration, "bar.yaml")
|
|
configuration = resource._deserialize_raw_configuration()
|
|
assert isinstance(configuration["sync_catalog"], AirbyteCatalog)
|
|
assert configuration["namespace_definition"] == NamespaceDefinitionType(
|
|
connection_configuration["configuration"]["namespace_definition"]
|
|
)
|
|
assert configuration["schedule_type"] == ConnectionScheduleType(connection_configuration["configuration"]["schedule_type"])
|
|
assert (
|
|
configuration["schedule_data"].to_dict()
|
|
== ConnectionScheduleDataBasicSchedule(**connection_configuration["configuration"]["schedule_data"]).to_dict()
|
|
)
|
|
assert configuration["resource_requirements"] == ResourceRequirements(
|
|
**connection_configuration["configuration"]["resource_requirements"]
|
|
)
|
|
assert configuration["status"] == ConnectionStatus(connection_configuration["configuration"]["status"])
|
|
assert list(configuration.keys()) == [
|
|
"namespace_definition",
|
|
"namespace_format",
|
|
"prefix",
|
|
"sync_catalog",
|
|
"schedule_type",
|
|
"schedule_data",
|
|
"status",
|
|
"resource_requirements",
|
|
"non_breaking_changes_preference",
|
|
"geography",
|
|
]
|
|
|
|
resource = resources.Connection(mock_api_client, "workspace_id", connection_configuration_with_manual_schedule, "bar.yaml")
|
|
configuration = resource._deserialize_raw_configuration()
|
|
assert configuration["schedule_type"] == ConnectionScheduleType(
|
|
connection_configuration_with_manual_schedule["configuration"]["schedule_type"]
|
|
)
|
|
assert configuration["schedule_data"] is None
|
|
|
|
def test__deserialize_operations(self, mock_api_client, connection_configuration):
|
|
resource = resources.Connection(mock_api_client, "workspace_id", connection_configuration, "bar.yaml")
|
|
operations = [
|
|
{
|
|
"operator_configuration": {"operator_type": "normalization", "normalization": {"option": "basic"}},
|
|
"name": "operation-with-normalization",
|
|
},
|
|
{
|
|
"operator_configuration": {
|
|
"operator_type": "dbt",
|
|
"dbt": {
|
|
"dbt_arguments": "run",
|
|
"docker_image": "fishtownanalytics/dbt:0.19.1",
|
|
"git_repo_branch": "my-branch-name",
|
|
"git_repo_url": "https://github.com/airbytehq/airbyte",
|
|
},
|
|
},
|
|
"name": "operation-with-custom_dbt",
|
|
},
|
|
]
|
|
deserialized_operations = resource._deserialize_operations(operations, OperationCreate)
|
|
assert len(deserialized_operations) == 2
|
|
assert all([isinstance(o, OperationCreate) for o in deserialized_operations])
|
|
assert "normalization" in deserialized_operations[0]["operator_configuration"] and deserialized_operations[0][
|
|
"operator_configuration"
|
|
]["operator_type"] == OperatorType("normalization")
|
|
assert "dbt" in deserialized_operations[1]["operator_configuration"]
|
|
assert deserialized_operations[1]["operator_configuration"]["operator_type"] == OperatorType("dbt")
|
|
|
|
with pytest.raises(ValueError):
|
|
resource._deserialize_operations(
|
|
[
|
|
{
|
|
"operator_configuration": {"operator_type": "not-supported", "normalization": {"option": "basic"}},
|
|
"name": "operation-not-supported",
|
|
},
|
|
],
|
|
OperationCreate,
|
|
)
|
|
|
|
def test__create_configured_catalog(self, mock_api_client, connection_configuration):
|
|
resource = resources.Connection(mock_api_client, "workspace_id", connection_configuration, "bar.yaml")
|
|
created_catalog = resource._create_configured_catalog(connection_configuration["configuration"]["sync_catalog"])
|
|
stream, config = (
|
|
connection_configuration["configuration"]["sync_catalog"]["streams"][0]["stream"],
|
|
connection_configuration["configuration"]["sync_catalog"]["streams"][0]["config"],
|
|
)
|
|
|
|
assert len(created_catalog.streams) == len(connection_configuration["configuration"]["sync_catalog"]["streams"])
|
|
assert created_catalog.streams[0].stream.name == stream["name"]
|
|
assert created_catalog.streams[0].stream.json_schema == stream["json_schema"]
|
|
assert created_catalog.streams[0].stream.supported_sync_modes == stream["supported_sync_modes"]
|
|
assert created_catalog.streams[0].stream.source_defined_cursor == stream["source_defined_cursor"]
|
|
assert created_catalog.streams[0].stream.namespace == stream["namespace"]
|
|
assert created_catalog.streams[0].stream.source_defined_primary_key == stream["source_defined_primary_key"]
|
|
assert created_catalog.streams[0].stream.default_cursor_field == stream["default_cursor_field"]
|
|
|
|
assert created_catalog.streams[0].config.sync_mode == config["sync_mode"]
|
|
assert created_catalog.streams[0].config.cursor_field == config["cursor_field"]
|
|
assert created_catalog.streams[0].config.destination_sync_mode == config["destination_sync_mode"]
|
|
assert created_catalog.streams[0].config.primary_key == config["primary_key"]
|
|
assert created_catalog.streams[0].config.alias_name == config["alias_name"]
|
|
assert created_catalog.streams[0].config.selected == config["selected"]
|
|
|
|
def test__check_for_legacy_connection_configuration_keys(
|
|
self, mock_api_client, connection_configuration, legacy_connection_configurations
|
|
):
|
|
resource = resources.Connection(mock_api_client, "workspace_id", connection_configuration, "bar.yaml")
|
|
assert resource._check_for_legacy_connection_configuration_keys(connection_configuration["configuration"]) is None
|
|
for legacy_configuration in legacy_connection_configurations:
|
|
with pytest.raises(resources.InvalidConfigurationError):
|
|
resource._check_for_legacy_connection_configuration_keys(legacy_configuration["configuration"])
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"local_configuration,resource_to_mock,expected_error",
|
|
[
|
|
({"definition_type": "source"}, "Source", None),
|
|
({"definition_type": "destination"}, "Destination", None),
|
|
({"definition_type": "connection"}, "Connection", None),
|
|
({"definition_type": "not_existing"}, None, NotImplementedError),
|
|
],
|
|
)
|
|
def test_factory(mocker, mock_api_client, local_configuration, resource_to_mock, expected_error):
|
|
mocker.patch.object(resources, "yaml")
|
|
if resource_to_mock is not None:
|
|
mocker.patch.object(resources, resource_to_mock)
|
|
resources.yaml.load.return_value = local_configuration
|
|
with patch("builtins.open", mock_open(read_data="data")) as mock_file:
|
|
if not expected_error:
|
|
resource = resources.factory(mock_api_client, "workspace_id", "my_config.yaml")
|
|
resources.yaml.load.assert_called_with(mock_file.return_value, yaml_loaders.EnvVarLoader)
|
|
resource == getattr(resources, resource_to_mock).return_value
|
|
mock_file.assert_called_with("my_config.yaml", "r")
|
|
else:
|
|
with pytest.raises(expected_error):
|
|
resources.factory(mock_api_client, "workspace_id", "my_config.yaml")
|
|
mock_file.assert_called_with("my_config.yaml", "r")
|