-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathtest_bigquery_adapter.py
More file actions
264 lines (208 loc) · 10.5 KB
/
test_bigquery_adapter.py
File metadata and controls
264 lines (208 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
from unittest.mock import MagicMock, patch
import pytest
from intugle.adapters.types.bigquery.bigquery import (
BIGQUERY_AVAILABLE,
BigQueryAdapter,
can_handle_bigquery,
)
from intugle.adapters.types.bigquery.models import BigQueryConfig, BigQueryConnectionConfig
@pytest.fixture
def mock_bigquery_client():
"""Mock BigQuery client."""
with patch("intugle.adapters.types.bigquery.bigquery.bigquery") as mock_bq:
mock_client = MagicMock()
mock_bq.Client.return_value = mock_client
yield mock_client
@pytest.fixture
def mock_settings():
"""Mock settings with BigQuery configuration."""
with patch("intugle.adapters.types.bigquery.bigquery.settings") as mock_settings:
mock_settings.PROFILES = {
"bigquery": {
"project_id": "test-project",
"dataset": "test_dataset",
"name": "test_bigquery_source",
"location": "US",
}
}
yield mock_settings
@pytest.fixture
def bigquery_config():
"""Create a test BigQuery config."""
return BigQueryConfig(identifier="test_table", type="bigquery")
@pytest.mark.skipif(not BIGQUERY_AVAILABLE, reason="BigQuery dependencies not installed")
class TestBigQueryAdapterContract:
"""Test that BigQueryAdapter implements the Adapter interface correctly."""
def test_implements_required_methods(self, mock_settings, mock_bigquery_client):
"""Test that all abstract methods are implemented."""
adapter = BigQueryAdapter()
# Check all abstract methods exist
assert hasattr(adapter, "profile")
assert hasattr(adapter, "column_profile")
assert hasattr(adapter, "load")
assert hasattr(adapter, "execute")
assert hasattr(adapter, "to_df")
assert hasattr(adapter, "to_df_from_query")
assert hasattr(adapter, "create_table_from_query")
assert hasattr(adapter, "create_new_config_from_etl")
assert hasattr(adapter, "intersect_count")
assert hasattr(adapter, "get_composite_key_uniqueness")
assert hasattr(adapter, "intersect_composite_keys_count")
assert callable(adapter.profile)
assert callable(adapter.column_profile)
def test_inherits_from_adapter(self, mock_settings, mock_bigquery_client):
"""Test that BigQueryAdapter inherits from Adapter base class."""
from intugle.adapters.adapter import Adapter
adapter = BigQueryAdapter()
assert isinstance(adapter, Adapter)
def test_singleton_pattern(self, mock_settings, mock_bigquery_client):
"""Test that BigQueryAdapter follows singleton pattern."""
# Reset singleton
BigQueryAdapter._instance = None
BigQueryAdapter._initialized = False
adapter1 = BigQueryAdapter()
adapter2 = BigQueryAdapter()
assert adapter1 is adapter2
def test_registered_in_factory(self):
"""Test that BigQueryAdapter is registered in the adapter factory."""
from intugle.adapters.factory import AdapterFactory
factory = AdapterFactory()
assert "bigquery" in factory.dataframe_funcs
@pytest.mark.skipif(not BIGQUERY_AVAILABLE, reason="BigQuery dependencies not installed")
class TestBigQuerySpecificBehavior:
"""Test BigQuery-specific functionality."""
def test_database_and_schema_properties(self, mock_settings, mock_bigquery_client):
"""Test that database and schema properties work correctly."""
BigQueryAdapter._instance = None
BigQueryAdapter._initialized = False
adapter = BigQueryAdapter()
assert adapter.database == "test-project"
assert adapter.schema == "test_dataset"
adapter.database = "new-project"
adapter.schema = "new_dataset"
assert adapter.database == "new-project"
assert adapter.schema == "new_dataset"
def test_check_data_validates_bigquery_config(self, mock_settings, mock_bigquery_client, bigquery_config):
"""Test that check_data validates BigQuery config correctly."""
BigQueryAdapter._instance = None
BigQueryAdapter._initialized = False
adapter = BigQueryAdapter()
validated = adapter.check_data(bigquery_config)
assert isinstance(validated, BigQueryConfig)
assert validated.identifier == "test_table"
assert validated.type == "bigquery"
def test_check_data_rejects_invalid_config(self, mock_settings, mock_bigquery_client):
"""Test that check_data rejects invalid configs."""
BigQueryAdapter._instance = None
BigQueryAdapter._initialized = False
adapter = BigQueryAdapter()
with pytest.raises(TypeError, match="Input must be a BigQuery config"):
adapter.check_data({"invalid": "config"})
def test_create_new_config_from_etl(self, mock_settings, mock_bigquery_client):
"""Test creating a new config from ETL name."""
BigQueryAdapter._instance = None
BigQueryAdapter._initialized = False
adapter = BigQueryAdapter()
config = adapter.create_new_config_from_etl("etl_table")
assert isinstance(config, BigQueryConfig)
assert config.identifier == "etl_table"
assert config.type == "bigquery"
def test_get_details_returns_config_dict(self, mock_settings, mock_bigquery_client, bigquery_config):
"""Test that get_details returns config as dictionary."""
BigQueryAdapter._instance = None
BigQueryAdapter._initialized = False
adapter = BigQueryAdapter()
details = adapter.get_details(bigquery_config)
assert isinstance(details, dict)
assert details["identifier"] == "test_table"
assert details["type"] == "bigquery"
def test_get_fqn_creates_fully_qualified_name(self, mock_settings, mock_bigquery_client):
"""Test that _get_fqn creates proper fully qualified names."""
BigQueryAdapter._instance = None
BigQueryAdapter._initialized = False
adapter = BigQueryAdapter()
# Simple table name
fqn = adapter._get_fqn("test_table")
assert fqn == "`test-project.test_dataset.test_table`"
# Dataset.table format
fqn = adapter._get_fqn("other_dataset.test_table")
assert fqn == "`test-project.other_dataset.test_table`"
# Full project.dataset.table format
fqn = adapter._get_fqn("other-project.other_dataset.test_table")
assert fqn == "`other-project.other_dataset.test_table`"
def test_client_property_exists(self, mock_settings, mock_bigquery_client):
"""Test that the BigQuery client is initialized."""
BigQueryAdapter._instance = None
BigQueryAdapter._initialized = False
adapter = BigQueryAdapter()
assert adapter.client is not None
@pytest.mark.skipif(not BIGQUERY_AVAILABLE, reason="BigQuery dependencies not installed")
class TestCanHandleBigQuery:
"""Test the can_handle_bigquery function."""
def test_handles_bigquery_config_object(self, bigquery_config):
"""Test that it accepts valid BigQuery config objects."""
assert can_handle_bigquery(bigquery_config) is True
def test_handles_valid_bigquery_dict(self):
"""Test that it accepts valid BigQuery config dictionaries."""
config_dict = {"identifier": "test_table", "type": "bigquery"}
assert can_handle_bigquery(config_dict) is True
def test_rejects_non_bigquery_configs(self):
"""Test that it rejects non-BigQuery configs."""
assert can_handle_bigquery({"type": "postgres"}) is False
assert can_handle_bigquery({"invalid": "config"}) is False
assert can_handle_bigquery("not a config") is False
assert can_handle_bigquery(None) is False
@pytest.mark.skipif(not BIGQUERY_AVAILABLE, reason="BigQuery dependencies not installed")
class TestBigQueryErrorHandling:
"""Test error handling in BigQueryAdapter."""
def test_import_error_when_bigquery_not_installed(self):
"""Test that ImportError is raised when BigQuery is not installed."""
with patch("intugle.adapters.types.bigquery.bigquery.BIGQUERY_AVAILABLE", False):
BigQueryAdapter._instance = None
BigQueryAdapter._initialized = False
with pytest.raises(ImportError, match="BigQuery dependencies are not installed"):
BigQueryAdapter()
def test_check_data_type_error_message(self, mock_settings, mock_bigquery_client):
"""Test that check_data provides clear error message."""
BigQueryAdapter._instance = None
BigQueryAdapter._initialized = False
adapter = BigQueryAdapter()
with pytest.raises(TypeError) as exc_info:
adapter.check_data({"not": "valid"})
assert "Input must be a BigQuery config" in str(exc_info.value)
def test_connection_error_provides_context(self, mock_bigquery_client):
"""Test that connection errors provide helpful context."""
with patch("intugle.adapters.types.bigquery.bigquery.settings") as mock_settings:
mock_settings.PROFILES = {}
BigQueryAdapter._instance = None
BigQueryAdapter._initialized = False
with pytest.raises(ValueError, match="No 'bigquery' section found in profiles.yml"):
BigQueryAdapter()
@pytest.mark.skipif(not BIGQUERY_AVAILABLE, reason="BigQuery dependencies not installed")
class TestBigQueryConnectionConfig:
"""Test BigQueryConnectionConfig model."""
def test_valid_config(self):
"""Test that valid config can be created."""
config = BigQueryConnectionConfig(
project_id="test-project",
dataset="test_dataset",
location="US",
)
assert config.project_id == "test-project"
assert config.dataset_id == "test_dataset"
assert config.location == "US"
def test_optional_credentials_path(self):
"""Test that credentials_path is optional."""
config = BigQueryConnectionConfig(
project_id="test-project",
dataset="test_dataset",
)
assert config.credentials_path is None
def test_with_credentials_path(self):
"""Test config with credentials path."""
config = BigQueryConnectionConfig(
project_id="test-project",
dataset="test_dataset",
credentials_path="/path/to/credentials.json",
)
assert config.credentials_path == "/path/to/credentials.json"