266 lines
9.7 KiB
Python
266 lines
9.7 KiB
Python
#!/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) |