#!/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)