2026.03.30 front és garázs logika
This commit is contained in:
266
backend/app/tests/test_issues_176_177_internal.py
Normal file
266
backend/app/tests/test_issues_176_177_internal.py
Normal file
@@ -0,0 +1,266 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Internal test script for Issues #176 and #177
|
||||
This script runs inside the sf_api container using only Python standard library.
|
||||
"""
|
||||
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import json
|
||||
import sys
|
||||
|
||||
# Configuration - internal Docker network
|
||||
BASE_URL = "http://sf_api:8000/api/v1"
|
||||
# Using a test user that should exist in the dev database
|
||||
TEST_EMAIL = "tester_pro@profibot.hu"
|
||||
TEST_PASSWORD = "Password123!"
|
||||
|
||||
def make_request(method, url, data=None, headers=None, is_form_data=False):
|
||||
"""Make HTTP request using urllib"""
|
||||
if headers is None:
|
||||
headers = {}
|
||||
|
||||
req = urllib.request.Request(url, method=method)
|
||||
for key, value in headers.items():
|
||||
req.add_header(key, value)
|
||||
|
||||
if data is not None:
|
||||
if is_form_data:
|
||||
# For form data (application/x-www-form-urlencoded)
|
||||
data_bytes = urllib.parse.urlencode(data).encode('utf-8')
|
||||
req.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||
else:
|
||||
# For JSON data
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
data_bytes = json.dumps(data).encode('utf-8')
|
||||
req.data = data_bytes
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(req) as response:
|
||||
response_data = response.read().decode('utf-8')
|
||||
return response.status, response_data
|
||||
except urllib.error.HTTPError as e:
|
||||
return e.code, e.read().decode('utf-8')
|
||||
except Exception as e:
|
||||
return 0, str(e)
|
||||
|
||||
def login():
|
||||
"""Login and get JWT token"""
|
||||
print("🔐 Logging in...")
|
||||
login_data = {
|
||||
"username": TEST_EMAIL,
|
||||
"password": TEST_PASSWORD
|
||||
}
|
||||
|
||||
# Login endpoint expects form data, not JSON
|
||||
status, response = make_request("POST", f"{BASE_URL}/auth/login", data=login_data, is_form_data=True)
|
||||
|
||||
if status == 200:
|
||||
data = json.loads(response)
|
||||
token = data.get("access_token")
|
||||
if token:
|
||||
print(f"✅ Login successful")
|
||||
return token
|
||||
else:
|
||||
print(f"❌ No access_token in response")
|
||||
print(f"Response: {response}")
|
||||
return None
|
||||
else:
|
||||
print(f"❌ Login failed with status {status}")
|
||||
print(f"Response: {response}")
|
||||
return None
|
||||
|
||||
def test_issue_176_organization_switch(token):
|
||||
"""Test Issue #176: PATCH /api/v1/users/me/active-organization endpoint"""
|
||||
print("\n" + "="*60)
|
||||
print("🧪 Testing Issue #176: Organization Switch (500 Error Fix)")
|
||||
print("="*60)
|
||||
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
# Step 1: Get current user info
|
||||
print("\n1. Getting current user info...")
|
||||
status, response = make_request("GET", f"{BASE_URL}/users/me", headers=headers)
|
||||
|
||||
if status == 200:
|
||||
user_data = json.loads(response)
|
||||
current_scope_id = user_data.get("scope_id")
|
||||
print(f" Current scope_id: {current_scope_id}")
|
||||
else:
|
||||
print(f"❌ Failed to get user info: {status}")
|
||||
print(f" Response: {response}")
|
||||
return False
|
||||
|
||||
# Step 2: Try to switch to null (personal mode)
|
||||
print("\n2. Testing switch to null (personal mode)...")
|
||||
switch_data = {"organization_id": None}
|
||||
status, response = make_request("PATCH", f"{BASE_URL}/users/me/active-organization",
|
||||
data=switch_data, headers=headers)
|
||||
|
||||
if status == 200:
|
||||
print(f"✅ Switch to null successful (200 OK)")
|
||||
result = json.loads(response)
|
||||
print(f" New scope_id: {result.get('scope_id')}")
|
||||
else:
|
||||
print(f"❌ Switch to null failed with status {status}")
|
||||
print(f" Response: {response}")
|
||||
return False
|
||||
|
||||
# Step 3: Get organizations to test switching
|
||||
print("\n3. Checking user's organizations...")
|
||||
status, response = make_request("GET", f"{BASE_URL}/organizations/my", headers=headers)
|
||||
|
||||
if status == 200:
|
||||
orgs = json.loads(response)
|
||||
if orgs and len(orgs) > 0:
|
||||
org_id = orgs[0].get("id")
|
||||
print(f" Found organization with ID: {org_id}")
|
||||
|
||||
# Try to switch to this organization
|
||||
switch_data = {"organization_id": org_id}
|
||||
status, response = make_request("PATCH", f"{BASE_URL}/users/me/active-organization",
|
||||
data=switch_data, headers=headers)
|
||||
|
||||
if status == 200:
|
||||
print(f"✅ Switch to organization successful (200 OK)")
|
||||
result = json.loads(response)
|
||||
print(f" New scope_id: {result.get('scope_id')}")
|
||||
elif status == 403:
|
||||
print(f"⚠️ Switch to organization failed with 403 (not a member)")
|
||||
print(f" This is expected if user is not a member of this org")
|
||||
else:
|
||||
print(f"❌ Switch to organization failed with status {status}")
|
||||
print(f" Response: {response}")
|
||||
# Don't fail the test if we can't switch to org (might not be a member)
|
||||
else:
|
||||
print(" No organizations found for user, skipping organization switch test")
|
||||
else:
|
||||
print(f" Could not fetch organizations: {status}")
|
||||
|
||||
# Step 4: Verify final state
|
||||
print("\n4. Verifying final user state...")
|
||||
status, response = make_request("GET", f"{BASE_URL}/users/me", headers=headers)
|
||||
|
||||
if status == 200:
|
||||
user_data = json.loads(response)
|
||||
final_scope_id = user_data.get("scope_id")
|
||||
print(f" Final scope_id: {final_scope_id}")
|
||||
print(f"✅ Issue #176 test completed successfully!")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Failed to verify user info: {status}")
|
||||
return False
|
||||
|
||||
def test_issue_177_garage_dynamic_data(token):
|
||||
"""Test Issue #177: GET /api/v1/assets/vehicles with dynamic data fields"""
|
||||
print("\n" + "="*60)
|
||||
print("🧪 Testing Issue #177: Garage Dynamic Data")
|
||||
print("="*60)
|
||||
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
# Get user's vehicles
|
||||
print("\n1. Fetching user's vehicles...")
|
||||
status, response = make_request("GET", f"{BASE_URL}/assets/vehicles", headers=headers)
|
||||
|
||||
if status == 200:
|
||||
vehicles = json.loads(response)
|
||||
print(f" Found {len(vehicles)} vehicles")
|
||||
|
||||
if len(vehicles) == 0:
|
||||
print(" No vehicles found, but endpoint works correctly")
|
||||
print(" ✅ GET /api/v1/assets/vehicles endpoint is working (200 OK)")
|
||||
return True
|
||||
|
||||
# Check first vehicle
|
||||
first_vehicle = vehicles[0]
|
||||
print(f"\n2. Checking first vehicle:")
|
||||
print(f" Vehicle ID: {first_vehicle.get('id')}")
|
||||
print(f" License plate: {first_vehicle.get('license_plate')}")
|
||||
|
||||
# Check for profile_completion_percentage field
|
||||
profile_completion = first_vehicle.get("profile_completion_percentage")
|
||||
if profile_completion is not None:
|
||||
print(f" ✅ profile_completion_percentage field found: {profile_completion}%")
|
||||
else:
|
||||
print(f" ❌ profile_completion_percentage field MISSING!")
|
||||
return False
|
||||
|
||||
# Check all fields
|
||||
print(f"\n3. Available fields in vehicle response:")
|
||||
for key in first_vehicle.keys():
|
||||
print(f" - {key}")
|
||||
|
||||
# Check for data_status field
|
||||
if "data_status" in first_vehicle:
|
||||
print(f" ✅ data_status field found: {first_vehicle['data_status']}")
|
||||
else:
|
||||
print(f" ⚠️ data_status field not in response")
|
||||
# Check if it might be in a nested structure
|
||||
for key, value in first_vehicle.items():
|
||||
if isinstance(value, dict) and "data_status" in value:
|
||||
print(f" ✅ data_status found in nested {key}: {value['data_status']}")
|
||||
break
|
||||
|
||||
# Check required fields
|
||||
required_fields = ["id", "status", "is_verified", "created_at"]
|
||||
missing_fields = []
|
||||
for field in required_fields:
|
||||
if field not in first_vehicle:
|
||||
missing_fields.append(field)
|
||||
|
||||
if missing_fields:
|
||||
print(f" ❌ Missing required fields: {missing_fields}")
|
||||
return False
|
||||
else:
|
||||
print(f" ✅ All required fields present")
|
||||
|
||||
print(f"\n✅ Issue #177 test completed successfully!")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Failed to get vehicles: {status}")
|
||||
print(f" Response: {response}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""Main test function"""
|
||||
print("🚀 Starting Internal QA Tests for Issues #176 and #177")
|
||||
print("="*60)
|
||||
|
||||
# Login
|
||||
token = login()
|
||||
if not token:
|
||||
print("❌ Cannot proceed without authentication token")
|
||||
return False
|
||||
|
||||
# Test Issue #176
|
||||
issue_176_passed = test_issue_176_organization_switch(token)
|
||||
|
||||
# Test Issue #177
|
||||
issue_177_passed = test_issue_177_garage_dynamic_data(token)
|
||||
|
||||
# Summary
|
||||
print("\n" + "="*60)
|
||||
print("📊 TEST SUMMARY")
|
||||
print("="*60)
|
||||
print(f"Issue #176 (Organization Switch): {'✅ PASSED' if issue_176_passed else '❌ FAILED'}")
|
||||
print(f"Issue #177 (Garage Dynamic Data): {'✅ PASSED' if issue_177_passed else '❌ FAILED'}")
|
||||
|
||||
overall_passed = issue_176_passed and issue_177_passed
|
||||
print(f"\nOverall Result: {'✅ ALL TESTS PASSED' if overall_passed else '❌ SOME TESTS FAILED'}")
|
||||
|
||||
return overall_passed
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
success = main()
|
||||
sys.exit(0 if success else 1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n⚠️ Test interrupted by user")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"\n❌ Unexpected error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user