160 lines
5.6 KiB
Python
160 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Authenticated E2E test for vehicle registration endpoint.
|
|
Reads credentials from backend/tests/test_credentials.json,
|
|
authenticates via /api/v1/auth/login, obtains JWT token,
|
|
and tests POST /api/v1/assets/vehicles endpoint.
|
|
"""
|
|
import json
|
|
import sys
|
|
import os
|
|
import asyncio
|
|
import aiohttp
|
|
import logging
|
|
from typing import Dict, Any
|
|
|
|
# Add parent directory to path to import app modules if needed
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
CREDENTIALS_PATH = os.path.join(
|
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
|
"tests",
|
|
"test_credentials.json"
|
|
)
|
|
|
|
async def load_credentials() -> Dict[str, Any]:
|
|
"""Load test credentials from JSON file."""
|
|
try:
|
|
with open(CREDENTIALS_PATH, 'r') as f:
|
|
creds = json.load(f)
|
|
logger.info(f"Loaded credentials for {creds.get('email')}")
|
|
return creds
|
|
except FileNotFoundError:
|
|
logger.error(f"Credentials file not found at {CREDENTIALS_PATH}")
|
|
sys.exit(1)
|
|
except json.JSONDecodeError as e:
|
|
logger.error(f"Invalid JSON in credentials file: {e}")
|
|
sys.exit(1)
|
|
|
|
async def authenticate(session: aiohttp.ClientSession, base_url: str, email: str, password: str) -> str:
|
|
"""Authenticate and return JWT token."""
|
|
login_url = f"{base_url}/api/v1/auth/login"
|
|
# Use form data as required by OAuth2PasswordRequestForm
|
|
form_data = aiohttp.FormData()
|
|
form_data.add_field('username', email)
|
|
form_data.add_field('password', password)
|
|
|
|
try:
|
|
async with session.post(login_url, data=form_data) as resp:
|
|
if resp.status != 200:
|
|
text = await resp.text()
|
|
logger.error(f"Authentication failed: {resp.status} - {text}")
|
|
raise ValueError(f"Authentication failed: {resp.status}")
|
|
data = await resp.json()
|
|
token = data.get("access_token")
|
|
if not token:
|
|
logger.error(f"No access_token in response: {data}")
|
|
raise ValueError("No access_token in response")
|
|
logger.info("Authentication successful")
|
|
return token
|
|
except aiohttp.ClientError as e:
|
|
logger.error(f"Network error during authentication: {e}")
|
|
raise
|
|
|
|
async def test_vehicle_creation(session: aiohttp.ClientSession, base_url: str, token: str):
|
|
"""Test POST /api/v1/assets/vehicles endpoint."""
|
|
url = f"{base_url}/api/v1/assets/vehicles"
|
|
headers = {
|
|
"Authorization": f"Bearer {token}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
|
|
# Realistic payload with valid catalog_id and required fields
|
|
payload = {
|
|
"vin": "TESTVIN1234567890",
|
|
"license_plate": "TEST-001",
|
|
"catalog_id": 5, # Valid catalog_id from database
|
|
"brand": "BMW",
|
|
"model": "X5",
|
|
"vehicle_class": "car",
|
|
"fuel_type": "petrol",
|
|
"engine_capacity": 3000,
|
|
"power_kw": 200,
|
|
"transmission_type": "automatic",
|
|
"drive_type": "awd"
|
|
}
|
|
|
|
logger.info(f"Testing vehicle creation with payload: {payload}")
|
|
|
|
try:
|
|
async with session.post(url, json=payload, headers=headers) as resp:
|
|
status = resp.status
|
|
text = await resp.text()
|
|
logger.info(f"Response status: {status}")
|
|
logger.info(f"Response body: {text}")
|
|
|
|
if status == 201:
|
|
logger.info("✅ Vehicle creation test PASSED")
|
|
return True
|
|
elif status == 404:
|
|
logger.error("❌ Endpoint not found (404). Check API route.")
|
|
return False
|
|
elif status == 400:
|
|
logger.warning("⚠️ Bad request (400). Might be due to missing catalog_id or validation.")
|
|
# Try to parse error details
|
|
try:
|
|
error_data = json.loads(text)
|
|
logger.warning(f"Error details: {error_data}")
|
|
except:
|
|
pass
|
|
return False
|
|
elif status == 500:
|
|
logger.error("❌ Internal server error (500). Check server logs.")
|
|
# Try to parse error details
|
|
try:
|
|
error_data = json.loads(text)
|
|
logger.error(f"Error details: {error_data}")
|
|
except:
|
|
pass
|
|
return False
|
|
else:
|
|
logger.error(f"❌ Unexpected status: {status}")
|
|
return False
|
|
except aiohttp.ClientError as e:
|
|
logger.error(f"Network error during vehicle creation: {e}")
|
|
return False
|
|
|
|
async def main():
|
|
"""Main test execution."""
|
|
logger.info("Starting authenticated E2E test for vehicle registration")
|
|
|
|
# Load credentials
|
|
creds = await load_credentials()
|
|
base_url = creds.get("base_url", "http://localhost:8000")
|
|
email = creds["email"]
|
|
password = creds["password"]
|
|
|
|
# Create aiohttp session
|
|
async with aiohttp.ClientSession() as session:
|
|
# Authenticate
|
|
try:
|
|
token = await authenticate(session, base_url, email, password)
|
|
except Exception as e:
|
|
logger.error(f"Authentication failed: {e}")
|
|
sys.exit(1)
|
|
|
|
# Test vehicle creation
|
|
success = await test_vehicle_creation(session, base_url, token)
|
|
|
|
if success:
|
|
logger.info("🎉 All tests passed!")
|
|
sys.exit(0)
|
|
else:
|
|
logger.error("💥 Test failed!")
|
|
sys.exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main()) |