anderson-ufrj commited on
Commit
adf8d4f
·
1 Parent(s): 47a6c2b

test(service): add unit tests for dados.gov.br service layer

Browse files

- Test transparency dataset search with caching
- Test government spending and procurement searches
- Test data availability analysis functionality
- Test organization listing and sorting
- Cover error handling and cleanup operations

tests/unit/services/test_dados_gov_service.py ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Unit tests for dados.gov.br service.
3
+ """
4
+
5
+ import pytest
6
+ from unittest.mock import AsyncMock, patch, MagicMock
7
+
8
+ from src.services.dados_gov_service import DadosGovService
9
+ from src.tools.dados_gov_api import DadosGovAPIError
10
+ from src.tools.dados_gov_models import (
11
+ Dataset,
12
+ DatasetSearchResult,
13
+ Organization,
14
+ Resource,
15
+ )
16
+
17
+
18
+ @pytest.fixture
19
+ def dados_gov_service():
20
+ """Create service instance for testing"""
21
+ return DadosGovService(api_key="test-key")
22
+
23
+
24
+ @pytest.fixture
25
+ def mock_api_client():
26
+ """Create mock API client"""
27
+ return AsyncMock()
28
+
29
+
30
+ @pytest.fixture
31
+ def mock_cache_service():
32
+ """Create mock cache service"""
33
+ mock = AsyncMock()
34
+ mock.get = AsyncMock(return_value=None)
35
+ mock.set = AsyncMock()
36
+ return mock
37
+
38
+
39
+ @pytest.fixture
40
+ def sample_dataset():
41
+ """Create sample dataset for testing"""
42
+ return {
43
+ "id": "test-dataset",
44
+ "name": "test-dataset",
45
+ "title": "Test Dataset",
46
+ "notes": "This is a test dataset",
47
+ "organization": {
48
+ "id": "test-org",
49
+ "name": "test-org",
50
+ "title": "Test Organization",
51
+ },
52
+ "resources": [
53
+ {
54
+ "id": "resource1",
55
+ "package_id": "test-dataset",
56
+ "name": "data.csv",
57
+ "format": "CSV",
58
+ "url": "http://example.com/data.csv",
59
+ }
60
+ ],
61
+ "tags": [
62
+ {"name": "test-tag"},
63
+ ],
64
+ }
65
+
66
+
67
+ class TestDadosGovService:
68
+ """Test suite for dados.gov.br service"""
69
+
70
+ @pytest.mark.asyncio
71
+ async def test_search_transparency_datasets(
72
+ self,
73
+ dados_gov_service,
74
+ mock_api_client,
75
+ mock_cache_service,
76
+ ):
77
+ """Test searching transparency datasets"""
78
+ # Mock API response
79
+ mock_api_client.search_datasets.return_value = {
80
+ "count": 1,
81
+ "results": [sample_dataset()],
82
+ "facets": {},
83
+ "search_facets": {},
84
+ }
85
+
86
+ dados_gov_service.client = mock_api_client
87
+ dados_gov_service.cache = mock_cache_service
88
+
89
+ # Search with keywords
90
+ result = await dados_gov_service.search_transparency_datasets(
91
+ keywords=["gastos", "contratos"],
92
+ limit=20,
93
+ )
94
+
95
+ assert isinstance(result, DatasetSearchResult)
96
+ assert result.count == 1
97
+ assert len(result.results) == 1
98
+ assert result.results[0].id == "test-dataset"
99
+
100
+ # Verify API call
101
+ mock_api_client.search_datasets.assert_called_once()
102
+ call_args = mock_api_client.search_datasets.call_args
103
+ assert "gastos OR contratos" in call_args[1]["query"]
104
+
105
+ # Verify cache usage
106
+ mock_cache_service.get.assert_called_once()
107
+ mock_cache_service.set.assert_called_once()
108
+
109
+ @pytest.mark.asyncio
110
+ async def test_search_transparency_datasets_cached(
111
+ self,
112
+ dados_gov_service,
113
+ mock_cache_service,
114
+ ):
115
+ """Test searching with cached results"""
116
+ # Mock cached data
117
+ cached_data = {
118
+ "count": 1,
119
+ "results": [sample_dataset()],
120
+ "facets": {},
121
+ "search_facets": {},
122
+ }
123
+ mock_cache_service.get.return_value = cached_data
124
+
125
+ dados_gov_service.cache = mock_cache_service
126
+
127
+ result = await dados_gov_service.search_transparency_datasets()
128
+
129
+ assert result.count == 1
130
+ # API should not be called when cache hit
131
+ assert not hasattr(dados_gov_service.client, "search_datasets")
132
+
133
+ @pytest.mark.asyncio
134
+ async def test_get_dataset_with_resources(
135
+ self,
136
+ dados_gov_service,
137
+ mock_api_client,
138
+ mock_cache_service,
139
+ ):
140
+ """Test getting dataset with resources"""
141
+ mock_api_client.get_dataset.return_value = {
142
+ "result": sample_dataset(),
143
+ }
144
+
145
+ dados_gov_service.client = mock_api_client
146
+ dados_gov_service.cache = mock_cache_service
147
+
148
+ result = await dados_gov_service.get_dataset_with_resources("test-dataset")
149
+
150
+ assert isinstance(result, Dataset)
151
+ assert result.id == "test-dataset"
152
+ assert len(result.resources) == 1
153
+ assert result.resources[0].format == "CSV"
154
+
155
+ mock_api_client.get_dataset.assert_called_once_with("test-dataset")
156
+
157
+ @pytest.mark.asyncio
158
+ async def test_find_government_spending_data(
159
+ self,
160
+ dados_gov_service,
161
+ mock_api_client,
162
+ mock_cache_service,
163
+ ):
164
+ """Test finding government spending data"""
165
+ # Create relevant dataset
166
+ spending_dataset = sample_dataset()
167
+ spending_dataset["title"] = "Gastos Públicos 2023"
168
+ spending_dataset["notes"] = "Dados de despesas do governo"
169
+
170
+ mock_api_client.search_datasets.return_value = {
171
+ "count": 1,
172
+ "results": [spending_dataset],
173
+ }
174
+
175
+ dados_gov_service.client = mock_api_client
176
+ dados_gov_service.cache = mock_cache_service
177
+
178
+ result = await dados_gov_service.find_government_spending_data(
179
+ year=2023,
180
+ state="SP",
181
+ )
182
+
183
+ assert len(result) == 1
184
+ assert "Gastos" in result[0].title
185
+
186
+ # Verify search query includes year and state
187
+ call_args = mock_api_client.search_datasets.call_args
188
+ query = call_args[1]["query"]
189
+ assert "2023" in query
190
+ assert "SP" in query
191
+
192
+ @pytest.mark.asyncio
193
+ async def test_find_procurement_data(
194
+ self,
195
+ dados_gov_service,
196
+ mock_api_client,
197
+ mock_cache_service,
198
+ ):
199
+ """Test finding procurement data"""
200
+ procurement_dataset = sample_dataset()
201
+ procurement_dataset["title"] = "Licitações e Contratos"
202
+
203
+ mock_api_client.search_datasets.return_value = {
204
+ "count": 1,
205
+ "results": [procurement_dataset],
206
+ }
207
+
208
+ dados_gov_service.client = mock_api_client
209
+ dados_gov_service.cache = mock_cache_service
210
+
211
+ result = await dados_gov_service.find_procurement_data(
212
+ modality="pregão",
213
+ )
214
+
215
+ assert len(result) == 1
216
+ assert result[0].title == "Licitações e Contratos"
217
+
218
+ @pytest.mark.asyncio
219
+ async def test_analyze_data_availability(
220
+ self,
221
+ dados_gov_service,
222
+ mock_api_client,
223
+ mock_cache_service,
224
+ ):
225
+ """Test analyzing data availability"""
226
+ # Create datasets with different characteristics
227
+ datasets = [
228
+ {
229
+ **sample_dataset(),
230
+ "title": "Educação Básica 2023",
231
+ "organization": {"title": "MEC"},
232
+ "resources": [
233
+ {"format": "CSV"},
234
+ {"format": "JSON"},
235
+ ],
236
+ },
237
+ {
238
+ **sample_dataset(),
239
+ "id": "dataset2",
240
+ "title": "Dados Educacionais Estaduais 2022",
241
+ "organization": {"title": "Secretaria Estadual"},
242
+ "resources": [
243
+ {"format": "CSV"},
244
+ ],
245
+ },
246
+ ]
247
+
248
+ mock_api_client.search_datasets.return_value = {
249
+ "count": 2,
250
+ "results": datasets,
251
+ }
252
+
253
+ dados_gov_service.client = mock_api_client
254
+ dados_gov_service.cache = mock_cache_service
255
+
256
+ analysis = await dados_gov_service.analyze_data_availability("educação")
257
+
258
+ assert analysis["topic"] == "educação"
259
+ assert analysis["total_datasets"] == 2
260
+ assert analysis["analyzed_datasets"] == 2
261
+ assert "MEC" in analysis["organizations"]
262
+ assert analysis["organizations"]["MEC"] == 1
263
+ assert "CSV" in analysis["formats"]
264
+ assert analysis["formats"]["CSV"] == 2
265
+ assert "JSON" in analysis["formats"]
266
+ assert analysis["formats"]["JSON"] == 1
267
+ assert "2022" in analysis["years_covered"]
268
+ assert "2023" in analysis["years_covered"]
269
+
270
+ @pytest.mark.asyncio
271
+ async def test_get_resource_download_url(
272
+ self,
273
+ dados_gov_service,
274
+ mock_api_client,
275
+ ):
276
+ """Test getting resource download URL"""
277
+ mock_api_client.get_resource.return_value = {
278
+ "result": {
279
+ "id": "resource1",
280
+ "url": "http://example.com/data.csv",
281
+ }
282
+ }
283
+
284
+ dados_gov_service.client = mock_api_client
285
+
286
+ url = await dados_gov_service.get_resource_download_url("resource1")
287
+
288
+ assert url == "http://example.com/data.csv"
289
+ mock_api_client.get_resource.assert_called_once_with("resource1")
290
+
291
+ @pytest.mark.asyncio
292
+ async def test_list_government_organizations(
293
+ self,
294
+ dados_gov_service,
295
+ mock_api_client,
296
+ mock_cache_service,
297
+ ):
298
+ """Test listing government organizations"""
299
+ orgs_data = [
300
+ {"id": "org1", "title": "Organization 1", "package_count": 100},
301
+ {"id": "org2", "title": "Organization 2", "package_count": 50},
302
+ {"id": "org3", "title": "Organization 3", "package_count": 150},
303
+ ]
304
+
305
+ mock_api_client.list_organizations.return_value = {
306
+ "result": orgs_data,
307
+ }
308
+
309
+ dados_gov_service.client = mock_api_client
310
+ dados_gov_service.cache = mock_cache_service
311
+
312
+ result = await dados_gov_service.list_government_organizations()
313
+
314
+ assert len(result) == 3
315
+ # Should be sorted by package count
316
+ assert result[0].package_count == 150
317
+ assert result[1].package_count == 100
318
+ assert result[2].package_count == 50
319
+
320
+ @pytest.mark.asyncio
321
+ async def test_error_handling(
322
+ self,
323
+ dados_gov_service,
324
+ mock_api_client,
325
+ mock_cache_service,
326
+ ):
327
+ """Test error handling from API"""
328
+ mock_api_client.search_datasets.side_effect = DadosGovAPIError(
329
+ "API Error",
330
+ status_code=500,
331
+ )
332
+
333
+ dados_gov_service.client = mock_api_client
334
+ dados_gov_service.cache = mock_cache_service
335
+
336
+ with pytest.raises(DadosGovAPIError):
337
+ await dados_gov_service.search_transparency_datasets()
338
+
339
+ @pytest.mark.asyncio
340
+ async def test_service_cleanup(self, dados_gov_service):
341
+ """Test service cleanup"""
342
+ mock_client = AsyncMock()
343
+ dados_gov_service.client = mock_client
344
+
345
+ await dados_gov_service.close()
346
+
347
+ mock_client.close.assert_called_once()