-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbackend_test.py
More file actions
345 lines (301 loc) · 15.3 KB
/
Copy pathbackend_test.py
File metadata and controls
345 lines (301 loc) · 15.3 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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#!/usr/bin/env python3
"""
Backend API Testing for ZVG-Portal Termin-Extraktor
Tests all API endpoints and functionality
"""
import requests
import sys
import json
from datetime import datetime
class ZVGAPITester:
def __init__(self, base_url="http://localhost:8001"):
self.base_url = base_url
self.api_url = f"{base_url}/api"
self.tests_run = 0
self.tests_passed = 0
self.failed_tests = []
def run_test(self, name, method, endpoint, expected_status, data=None, params=None):
"""Run a single API test"""
url = f"{self.api_url}/{endpoint}"
headers = {'Content-Type': 'application/json'}
self.tests_run += 1
print(f"\n🔍 Testing {name}...")
print(f" URL: {url}")
try:
if method == 'GET':
response = requests.get(url, headers=headers, params=params, timeout=30)
elif method == 'POST':
response = requests.post(url, json=data, headers=headers, timeout=30)
elif method == 'PUT':
response = requests.put(url, json=data, headers=headers, timeout=30)
elif method == 'DELETE':
response = requests.delete(url, headers=headers, timeout=30)
success = response.status_code == expected_status
if success:
self.tests_passed += 1
print(f"✅ Passed - Status: {response.status_code}")
try:
response_data = response.json()
if isinstance(response_data, dict) and 'message' in response_data:
print(f" Message: {response_data['message']}")
elif isinstance(response_data, list):
print(f" Returned {len(response_data)} items")
elif isinstance(response_data, dict) and 'total' in response_data:
print(f" Total items: {response_data.get('total', 'N/A')}")
except:
pass
else:
print(f"❌ Failed - Expected {expected_status}, got {response.status_code}")
try:
error_detail = response.json()
print(f" Error: {error_detail}")
except:
print(f" Response: {response.text[:200]}")
self.failed_tests.append({
'test': name,
'expected': expected_status,
'actual': response.status_code,
'endpoint': endpoint
})
return success, response.json() if success and response.content else {}
except requests.exceptions.Timeout:
print(f"❌ Failed - Request timeout")
self.failed_tests.append({'test': name, 'error': 'timeout', 'endpoint': endpoint})
return False, {}
except Exception as e:
print(f"❌ Failed - Error: {str(e)}")
self.failed_tests.append({'test': name, 'error': str(e), 'endpoint': endpoint})
return False, {}
def test_root_endpoint(self):
"""Test API root endpoint"""
return self.run_test("API Root", "GET", "", 200)
def test_bundeslaender(self):
"""Test bundeslaender endpoint"""
success, data = self.run_test("Get Bundesländer", "GET", "bundeslaender", 200)
if success and isinstance(data, list) and len(data) > 0:
print(f" Found {len(data)} Bundesländer")
return True
return False
def test_objekt_typen(self):
"""Test objekt-typen endpoint"""
success, data = self.run_test("Get Objekt Typen", "GET", "objekt-typen", 200)
if success and isinstance(data, list) and len(data) > 0:
print(f" Found {len(data)} Objekt Typen")
return True
return False
def test_statistics(self):
"""Test statistics endpoint"""
success, data = self.run_test("Get Statistics", "GET", "statistics", 200)
if success and isinstance(data, dict):
total = data.get('total', 0)
by_classification = data.get('by_classification', {})
by_state = data.get('by_state', {})
print(f" Total foreclosures: {total}")
print(f" Classifications: {list(by_classification.keys())}")
print(f" States: {list(by_state.keys())}")
return True
return False
def test_foreclosures(self):
"""Test foreclosures endpoint"""
success, data = self.run_test("Get All Foreclosures", "GET", "foreclosures", 200)
if success and isinstance(data, list):
print(f" Found {len(data)} foreclosures")
if len(data) > 0:
sample = data[0]
required_fields = ['id', 'aktenzeichen', 'gericht', 'bundesland', 'termin_datum']
missing_fields = [field for field in required_fields if field not in sample]
if missing_fields:
print(f" ⚠️ Missing required fields: {missing_fields}")
else:
print(f" ✅ All required fields present")
return True
return False
def test_foreclosures_with_filters(self):
"""Test foreclosures with filters"""
# Test with bundesland filter
success1, data1 = self.run_test("Get Foreclosures (BW filter)", "GET", "foreclosures", 200, params={"bundesland": "bw"})
# Test with klassifizierung filter
success2, data2 = self.run_test("Get Foreclosures (Wohnhäuser filter)", "GET", "foreclosures", 200, params={"klassifizierung": "Wohnhäuser"})
return success1 and success2
def test_price_range_filters(self):
"""Test price range filtering functionality"""
# Test price_min filter
success1, data1 = self.run_test("Get Foreclosures (price_min=100000)", "GET", "foreclosures", 200, params={"price_min": 100000})
# Test price_max filter
success2, data2 = self.run_test("Get Foreclosures (price_max=300000)", "GET", "foreclosures", 200, params={"price_max": 300000})
# Test price range filter
success3, data3 = self.run_test("Get Foreclosures (price range 100k-300k)", "GET", "foreclosures", 200, params={"price_min": 100000, "price_max": 300000})
if success1 and success2 and success3:
print(f" Price filters working: min={len(data1)}, max={len(data2)}, range={len(data3)} results")
return True
return False
def test_search_filter(self):
"""Test search functionality across multiple fields"""
# Test search by court name
success1, data1 = self.run_test("Search by court (Stuttgart)", "GET", "foreclosures", 200, params={"search": "Stuttgart"})
# Test search by case number pattern
success2, data2 = self.run_test("Search by case number (K)", "GET", "foreclosures", 200, params={"search": "K"})
# Test search by city
success3, data3 = self.run_test("Search by city (München)", "GET", "foreclosures", 200, params={"search": "München"})
if success1 and success2 and success3:
print(f" Search filters working: court={len(data1)}, case={len(data2)}, city={len(data3)} results")
return True
return False
def test_all_16_states_available(self):
"""Test that all 16 German states are available"""
success, data = self.run_test("Get All Bundesländer", "GET", "bundeslaender", 200)
if success and isinstance(data, list):
expected_states = 16
actual_states = len(data)
if actual_states == expected_states:
print(f" ✅ All {expected_states} German states available")
# Print state codes for verification
state_codes = [state.get('code', '') for state in data]
expected_codes = ['bw', 'by', 'he', 'rp', 'th', 'sn', 'nw', 'ni', 'st', 'br', 'be', 'sh', 'mv', 'hb', 'hh', 'sl']
missing_codes = [code for code in expected_codes if code not in state_codes]
if missing_codes:
print(f" ⚠️ Missing state codes: {missing_codes}")
return False
else:
print(f" ✅ All expected state codes present: {sorted(state_codes)}")
return True
else:
print(f" ❌ Expected {expected_states} states, found {actual_states}")
return False
return False
def test_filter_combinations(self):
"""Test combining multiple filters"""
# Test state + price filter combination
success1, data1 = self.run_test("Combined filter (state+price)", "GET", "foreclosures", 200,
params={"bundesland": "bw", "price_min": 50000, "price_max": 200000})
# Test state + search filter combination
success2, data2 = self.run_test("Combined filter (state+search)", "GET", "foreclosures", 200,
params={"bundesland": "by", "search": "München"})
# Test all filters combined
success3, data3 = self.run_test("All filters combined", "GET", "foreclosures", 200,
params={"bundesland": "bw", "klassifizierung": "Wohnhäuser", "price_min": 100000, "search": "Stuttgart"})
if success1 and success2 and success3:
print(f" Filter combinations working: state+price={len(data1)}, state+search={len(data2)}, all={len(data3)} results")
return True
return False
def test_classification_rules(self):
"""Test classification rules endpoint"""
success, data = self.run_test("Get Classification Rules", "GET", "classification-rules", 200)
if success and isinstance(data, list):
print(f" Found {len(data)} classification rules")
if len(data) > 0:
sample = data[0]
required_fields = ['id', 'name', 'objekt_typ_ids', 'active']
missing_fields = [field for field in required_fields if field not in sample]
if missing_fields:
print(f" ⚠️ Missing required fields: {missing_fields}")
else:
print(f" ✅ All required fields present")
return True
return False
def test_settings(self):
"""Test settings endpoint"""
success, data = self.run_test("Get Settings", "GET", "settings", 200)
if success and isinstance(data, dict):
print(f" Email notifications: {data.get('email_notifications_enabled', False)}")
print(f" Selected Bundesländer: {data.get('selected_bundeslaender', [])}")
return True
return False
def test_notifications(self):
"""Test notifications endpoint"""
success, data = self.run_test("Get Notifications", "GET", "notifications", 200)
if success and isinstance(data, list):
print(f" Found {len(data)} notifications")
unread = len([n for n in data if not n.get('read', True)])
print(f" Unread: {unread}")
return True
return False
def test_fetch_data(self):
"""Test manual data fetch"""
print(f"\n🔍 Testing Manual Data Fetch...")
print(f" This may take 10-30 seconds as it fetches demo data...")
success, data = self.run_test("Trigger Data Fetch", "POST", "fetch", 200)
if success and isinstance(data, dict):
status = data.get('status', '')
message = data.get('message', '')
new_count = data.get('new_count', 0)
total_count = data.get('total_count', 0)
print(f" Status: {status}")
print(f" New foreclosures: {new_count}")
print(f" Total processed: {total_count}")
return True
return False
def test_settings_update(self):
"""Test settings update"""
update_data = {
"email_notifications_enabled": False,
"selected_bundeslaender": ["bw", "by"]
}
success, data = self.run_test("Update Settings", "PUT", "settings", 200, data=update_data)
if success:
# Verify the update
success2, verify_data = self.run_test("Verify Settings Update", "GET", "settings", 200)
if success2:
if verify_data.get('selected_bundeslaender') == ["bw", "by"]:
print(f" ✅ Settings update verified")
return True
else:
print(f" ⚠️ Settings not properly updated")
return False
def test_individual_foreclosure(self):
"""Test getting individual foreclosure"""
# First get all foreclosures to get an ID
success, data = self.run_test("Get Foreclosures for ID", "GET", "foreclosures", 200)
if success and isinstance(data, list) and len(data) > 0:
foreclosure_id = data[0]['id']
success2, detail_data = self.run_test(f"Get Foreclosure Detail", "GET", f"foreclosures/{foreclosure_id}", 200)
if success2 and isinstance(detail_data, dict):
print(f" Retrieved foreclosure: {detail_data.get('aktenzeichen', 'N/A')}")
return True
return False
def main():
print("🏛️ ZVG-Portal Termin-Extraktor Backend API Tests")
print("=" * 60)
tester = ZVGAPITester()
# Run all tests
tests = [
tester.test_root_endpoint,
tester.test_bundeslaender,
tester.test_all_16_states_available, # New test for all 16 states
tester.test_objekt_typen,
tester.test_statistics,
tester.test_foreclosures,
tester.test_foreclosures_with_filters,
tester.test_price_range_filters, # New test for price filters
tester.test_search_filter, # New test for search functionality
tester.test_filter_combinations, # New test for filter combinations
tester.test_classification_rules,
tester.test_settings,
tester.test_notifications,
tester.test_individual_foreclosure,
tester.test_settings_update,
tester.test_fetch_data, # Run this last as it may add new data
]
print(f"\nRunning {len(tests)} test suites...")
for test_func in tests:
try:
test_func()
except Exception as e:
print(f"❌ Test suite failed with exception: {e}")
tester.failed_tests.append({'test': test_func.__name__, 'error': str(e)})
# Print summary
print("\n" + "=" * 60)
print(f"📊 Test Results Summary")
print(f"Tests run: {tester.tests_run}")
print(f"Tests passed: {tester.tests_passed}")
print(f"Tests failed: {tester.tests_run - tester.tests_passed}")
print(f"Success rate: {(tester.tests_passed / tester.tests_run * 100):.1f}%" if tester.tests_run > 0 else "0%")
if tester.failed_tests:
print(f"\n❌ Failed Tests:")
for failure in tester.failed_tests:
error_msg = failure.get('error', f"Expected {failure.get('expected')}, got {failure.get('actual')}")
print(f" - {failure['test']}: {error_msg}")
# Return appropriate exit code
return 0 if tester.tests_passed == tester.tests_run else 1
if __name__ == "__main__":
sys.exit(main())