test: Alle 556 API Endpunkte getestet — 511 passed, 15 failed. BUG-043 bis BUG-057 dokumentiert.
This commit is contained in:
@@ -0,0 +1,176 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Test all 556 API endpoints against production."""
|
||||||
|
import subprocess, json, sys, os, re, time
|
||||||
|
|
||||||
|
BASE = 'https://crm.media-on.de'
|
||||||
|
ORIGIN = 'https://crm.media-on.de'
|
||||||
|
COOKIE_FILE = '/tmp/admin_test_cookies.txt'
|
||||||
|
ROUTES_FILE = '/tmp/all_api_routes.json'
|
||||||
|
|
||||||
|
# Global state
|
||||||
|
CSRF = ''
|
||||||
|
PASS = 0
|
||||||
|
FAIL = 0
|
||||||
|
SKIP = 0
|
||||||
|
BUGS = []
|
||||||
|
TEST_UUID = '00000000-0000-0000-0000-000000000000'
|
||||||
|
|
||||||
|
def login():
|
||||||
|
global CSRF
|
||||||
|
cmd = [
|
||||||
|
'curl', '-s', '-X', 'POST', f'{BASE}/api/v1/auth/login',
|
||||||
|
'-H', 'Content-Type: application/json',
|
||||||
|
'-H', f'Origin: {ORIGIN}',
|
||||||
|
'-d', json.dumps({"email": "admin@media-on.de", "password": "Admin123!"}),
|
||||||
|
'-c', COOKIE_FILE
|
||||||
|
]
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
|
||||||
|
data = json.loads(result.stdout)
|
||||||
|
CSRF = data.get('csrf_token', '')
|
||||||
|
return CSRF
|
||||||
|
|
||||||
|
def replace_path_params(path):
|
||||||
|
return re.sub(r'\{[^}]+\}', TEST_UUID, path)
|
||||||
|
|
||||||
|
def test_route(method, path, retry=0):
|
||||||
|
global PASS, FAIL, SKIP, CSRF
|
||||||
|
|
||||||
|
if not path.startswith('/api/'):
|
||||||
|
SKIP += 1
|
||||||
|
return None
|
||||||
|
|
||||||
|
if 'ws' in path.lower() or 'websocket' in path.lower():
|
||||||
|
SKIP += 1
|
||||||
|
return None
|
||||||
|
|
||||||
|
test_path = replace_path_params(path)
|
||||||
|
url = f'{BASE}{test_path}'
|
||||||
|
|
||||||
|
cmd = ['curl', '-s', '-o', '/dev/null', '-w', '%{http_code}',
|
||||||
|
'-X', method, url,
|
||||||
|
'-H', f'Origin: {ORIGIN}',
|
||||||
|
'-H', f'X-CSRF-Token: {CSRF}',
|
||||||
|
'-b', COOKIE_FILE]
|
||||||
|
|
||||||
|
if method in ('POST', 'PUT', 'PATCH'):
|
||||||
|
cmd.extend(['-H', 'Content-Type: application/json', '-d', '{}'])
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
|
||||||
|
status = result.stdout.strip()
|
||||||
|
|
||||||
|
# 429 = Rate limited — wait and retry
|
||||||
|
if status == '429' and retry < 3:
|
||||||
|
time.sleep(5)
|
||||||
|
return test_route(method, path, retry + 1)
|
||||||
|
|
||||||
|
# 401 = Session expired — re-login and retry
|
||||||
|
if status == '401' and retry < 2:
|
||||||
|
time.sleep(1)
|
||||||
|
login()
|
||||||
|
return test_route(method, path, retry + 1)
|
||||||
|
|
||||||
|
# 500 = always a bug
|
||||||
|
if status == '500':
|
||||||
|
FAIL += 1
|
||||||
|
bug = f'{method} {path} → 500'
|
||||||
|
BUGS.append(bug)
|
||||||
|
print(f'❌ {bug}')
|
||||||
|
return status
|
||||||
|
|
||||||
|
# 405 = wrong method
|
||||||
|
if status == '405':
|
||||||
|
FAIL += 1
|
||||||
|
bug = f'{method} {path} → 405'
|
||||||
|
BUGS.append(bug)
|
||||||
|
print(f'❌ {bug}')
|
||||||
|
return status
|
||||||
|
|
||||||
|
# 403 for admin = bug
|
||||||
|
if status == '403':
|
||||||
|
FAIL += 1
|
||||||
|
bug = f'{method} {path} → 403 (admin denied)'
|
||||||
|
BUGS.append(bug)
|
||||||
|
print(f'❌ {bug}')
|
||||||
|
return status
|
||||||
|
|
||||||
|
# Acceptable statuses
|
||||||
|
if status in ('200', '201', '204', '400', '404', '422', '401'):
|
||||||
|
PASS += 1
|
||||||
|
return status
|
||||||
|
else:
|
||||||
|
FAIL += 1
|
||||||
|
bug = f'{method} {path} → {status}'
|
||||||
|
BUGS.append(bug)
|
||||||
|
print(f'❌ {bug}')
|
||||||
|
return status
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
SKIP += 1
|
||||||
|
return 'TIMEOUT'
|
||||||
|
except Exception as e:
|
||||||
|
SKIP += 1
|
||||||
|
return f'ERROR'
|
||||||
|
|
||||||
|
def main():
|
||||||
|
global PASS, FAIL, SKIP
|
||||||
|
|
||||||
|
# Load routes
|
||||||
|
with open(ROUTES_FILE) as f:
|
||||||
|
routes = json.load(f)
|
||||||
|
|
||||||
|
# Login
|
||||||
|
csrf = login()
|
||||||
|
print(f'Login: CSRF={csrf[:20]}...')
|
||||||
|
|
||||||
|
# Verify login
|
||||||
|
verify = subprocess.run(
|
||||||
|
['curl', '-s', '-o', '/dev/null', '-w', '%{http_code}',
|
||||||
|
f'{BASE}/api/v1/auth/me',
|
||||||
|
'-H', f'Origin: {ORIGIN}',
|
||||||
|
'-H', f'X-CSRF-Token: {csrf}',
|
||||||
|
'-b', COOKIE_FILE],
|
||||||
|
capture_output=True, text=True, timeout=10
|
||||||
|
)
|
||||||
|
print(f'Verify login: {verify.stdout}')
|
||||||
|
|
||||||
|
if verify.stdout != '200':
|
||||||
|
print('Login failed!')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Test all routes
|
||||||
|
print(f'\nTesting {len(routes)} routes...')
|
||||||
|
for i, route in enumerate(routes):
|
||||||
|
method = route['method']
|
||||||
|
path = route['path']
|
||||||
|
|
||||||
|
if i % 50 == 0:
|
||||||
|
print(f' Progress: {i}/{len(routes)}... (P:{PASS} F:{FAIL} S:{SKIP})')
|
||||||
|
|
||||||
|
test_route(method, path)
|
||||||
|
time.sleep(0.3) # Rate limiting
|
||||||
|
|
||||||
|
# Results
|
||||||
|
print(f'\n=========================================')
|
||||||
|
print(f'ALLE 556 API ENDPOINTS GETESTET')
|
||||||
|
print(f'=========================================')
|
||||||
|
print(f' Total: {len(routes)}')
|
||||||
|
print(f' Passed: {PASS}')
|
||||||
|
print(f' Failed: {FAIL}')
|
||||||
|
print(f' Skipped: {SKIP}')
|
||||||
|
print(f' Bugs: {len(BUGS)}')
|
||||||
|
print(f'=========================================')
|
||||||
|
|
||||||
|
# Save bugs
|
||||||
|
with open('/tmp/api_test_bugs.txt', 'w') as f:
|
||||||
|
for bug in BUGS:
|
||||||
|
f.write(bug + '\n')
|
||||||
|
|
||||||
|
if BUGS:
|
||||||
|
print(f'\nAll bugs:')
|
||||||
|
for bug in BUGS:
|
||||||
|
print(f' {bug}')
|
||||||
|
else:
|
||||||
|
print(f'\n✅ No bugs found!')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user