319 lines
11 KiB
Python
319 lines
11 KiB
Python
"""Test API routes"""
|
|
import pytest
|
|
import json
|
|
from decimal import Decimal
|
|
|
|
|
|
class TestAuthRoutes:
|
|
"""Test authentication routes"""
|
|
|
|
@pytest.mark.auth
|
|
def test_register_success(self, client):
|
|
"""Test successful user registration"""
|
|
response = client.post('/api/auth/register', json={
|
|
'email': 'newuser@example.com',
|
|
'password': 'password123',
|
|
'username': 'newuser',
|
|
'first_name': 'New',
|
|
'last_name': 'User'
|
|
})
|
|
|
|
assert response.status_code == 201
|
|
data = response.get_json()
|
|
assert data['email'] == 'newuser@example.com'
|
|
assert data['username'] == 'newuser'
|
|
assert 'password' not in data
|
|
assert 'password_hash' not in data
|
|
|
|
@pytest.mark.auth
|
|
def test_register_missing_fields(self, client):
|
|
"""Test registration with missing required fields"""
|
|
response = client.post('/api/auth/register', json={
|
|
'email': 'newuser@example.com'
|
|
})
|
|
|
|
assert response.status_code == 400
|
|
data = response.get_json()
|
|
assert 'error' in data
|
|
|
|
@pytest.mark.auth
|
|
def test_register_duplicate_email(self, client, regular_user):
|
|
"""Test registration with duplicate email"""
|
|
response = client.post('/api/auth/register', json={
|
|
'email': regular_user.email,
|
|
'password': 'password123'
|
|
})
|
|
|
|
assert response.status_code == 400
|
|
data = response.get_json()
|
|
assert 'already exists' in data['error'].lower()
|
|
|
|
@pytest.mark.auth
|
|
def test_login_success(self, client, regular_user):
|
|
"""Test successful login"""
|
|
response = client.post('/api/auth/login', json={
|
|
'email': regular_user.email,
|
|
'password': 'password123'
|
|
})
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert 'access_token' in data
|
|
assert 'refresh_token' in data
|
|
assert data['user']['email'] == regular_user.email
|
|
|
|
@pytest.mark.auth
|
|
@pytest.mark.parametrize("email,password,expected_status", [
|
|
("wrong@example.com", "password123", 401),
|
|
("user@example.com", "wrongpassword", 401),
|
|
(None, "password123", 400),
|
|
("user@example.com", None, 400),
|
|
])
|
|
def test_login_validation(self, client, regular_user, email, password, expected_status):
|
|
"""Test login with various invalid inputs"""
|
|
login_data = {}
|
|
if email is not None:
|
|
login_data['email'] = email
|
|
if password is not None:
|
|
login_data['password'] = password
|
|
|
|
response = client.post('/api/auth/login', json=login_data)
|
|
assert response.status_code == expected_status
|
|
|
|
@pytest.mark.auth
|
|
def test_login_inactive_user(self, client, inactive_user):
|
|
"""Test login with inactive user"""
|
|
response = client.post('/api/auth/login', json={
|
|
'email': inactive_user.email,
|
|
'password': 'password123'
|
|
})
|
|
|
|
assert response.status_code == 401
|
|
data = response.get_json()
|
|
assert 'inactive' in data['error'].lower()
|
|
|
|
@pytest.mark.auth
|
|
def test_get_current_user(self, client, auth_headers, regular_user):
|
|
"""Test getting current user"""
|
|
response = client.get('/api/users/me', headers=auth_headers)
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert data['email'] == regular_user.email
|
|
|
|
@pytest.mark.auth
|
|
def test_get_current_user_unauthorized(self, client):
|
|
"""Test getting current user without authentication"""
|
|
response = client.get('/api/users/me')
|
|
assert response.status_code == 401
|
|
|
|
|
|
class TestProductRoutes:
|
|
"""Test product routes"""
|
|
|
|
@pytest.mark.product
|
|
def test_get_products(self, client, products):
|
|
"""Test getting all products"""
|
|
response = client.get('/api/products')
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert len(data) == 5
|
|
|
|
@pytest.mark.product
|
|
def test_get_products_empty(self, client):
|
|
"""Test getting products when none exist"""
|
|
response = client.get('/api/products')
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert len(data) == 0
|
|
|
|
@pytest.mark.product
|
|
def test_get_single_product(self, client, product):
|
|
"""Test getting a single product"""
|
|
response = client.get(f'/api/products/{product.id}')
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert data['id'] == product.id
|
|
assert data['name'] == product.name
|
|
|
|
@pytest.mark.product
|
|
def test_get_product_not_found(self, client):
|
|
"""Test getting non-existent product"""
|
|
response = client.get('/api/products/999')
|
|
assert response.status_code == 404
|
|
|
|
@pytest.mark.product
|
|
def test_create_product_admin(self, client, admin_headers):
|
|
"""Test creating product as admin"""
|
|
response = client.post('/api/products', headers=admin_headers, json={
|
|
'name': 'New Product',
|
|
'description': 'A new product',
|
|
'price': 29.99,
|
|
'stock': 10
|
|
})
|
|
|
|
assert response.status_code == 201
|
|
data = response.get_json()
|
|
assert data['name'] == 'New Product'
|
|
assert data['price'] == 29.99
|
|
|
|
@pytest.mark.product
|
|
def test_create_product_regular_user(self, client, auth_headers):
|
|
"""Test creating product as regular user (should fail)"""
|
|
response = client.post('/api/products', headers=auth_headers, json={
|
|
'name': 'New Product',
|
|
'price': 29.99
|
|
})
|
|
|
|
assert response.status_code == 403
|
|
data = response.get_json()
|
|
assert 'admin' in data['error'].lower()
|
|
|
|
@pytest.mark.product
|
|
def test_create_product_unauthorized(self, client):
|
|
"""Test creating product without authentication"""
|
|
response = client.post('/api/products', json={
|
|
'name': 'New Product',
|
|
'price': 29.99
|
|
})
|
|
|
|
assert response.status_code == 401
|
|
|
|
@pytest.mark.product
|
|
def test_create_product_validation_error(self, client, admin_headers):
|
|
"""Test creating product with invalid data"""
|
|
response = client.post('/api/products', headers=admin_headers, json={
|
|
'name': 'New Product',
|
|
'price': -10.99
|
|
})
|
|
|
|
assert response.status_code == 400
|
|
data = response.get_json()
|
|
assert 'Validation error' in data['error']
|
|
|
|
@pytest.mark.product
|
|
def test_create_product_missing_required_fields(self, client, admin_headers):
|
|
"""Test creating product with missing required fields"""
|
|
response = client.post('/api/products', headers=admin_headers, json={
|
|
'description': 'Missing name and price'
|
|
})
|
|
|
|
assert response.status_code == 400
|
|
data = response.get_json()
|
|
assert 'Validation error' in data['error']
|
|
|
|
@pytest.mark.product
|
|
def test_create_product_minimal_data(self, client, admin_headers):
|
|
"""Test creating product with minimal valid data"""
|
|
response = client.post('/api/products', headers=admin_headers, json={
|
|
'name': 'Minimal Product',
|
|
'price': 19.99
|
|
})
|
|
|
|
assert response.status_code == 201
|
|
data = response.get_json()
|
|
assert data['name'] == 'Minimal Product'
|
|
assert data['stock'] == 0 # Default value
|
|
|
|
@pytest.mark.product
|
|
def test_update_product_admin(self, client, admin_headers, product):
|
|
"""Test updating product as admin"""
|
|
response = client.put(f'/api/products/{product.id}', headers=admin_headers, json={
|
|
'name': 'Updated Product',
|
|
'price': 39.99
|
|
})
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert data['name'] == 'Updated Product'
|
|
assert data['price'] == 39.99
|
|
|
|
@pytest.mark.product
|
|
def test_delete_product_admin(self, client, admin_headers, product):
|
|
"""Test deleting product as admin"""
|
|
response = client.delete(f'/api/products/{product.id}', headers=admin_headers)
|
|
assert response.status_code == 200
|
|
|
|
# Verify product is deleted
|
|
response = client.get(f'/api/products/{product.id}')
|
|
assert response.status_code == 404
|
|
|
|
|
|
class TestOrderRoutes:
|
|
"""Test order routes"""
|
|
|
|
@pytest.mark.order
|
|
def test_get_orders(self, client, auth_headers, order):
|
|
"""Test getting orders for current user"""
|
|
response = client.get('/api/orders', headers=auth_headers)
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert len(data) >= 1
|
|
|
|
@pytest.mark.order
|
|
def test_get_orders_unauthorized(self, client):
|
|
"""Test getting orders without authentication"""
|
|
response = client.get('/api/orders')
|
|
assert response.status_code == 401
|
|
|
|
@pytest.mark.order
|
|
def test_create_order(self, client, auth_headers, products):
|
|
"""Test creating an order"""
|
|
response = client.post('/api/orders', headers=auth_headers, json={
|
|
'items': [
|
|
{'product_id': products[0].id, 'quantity': 2},
|
|
{'product_id': products[1].id, 'quantity': 1}
|
|
],
|
|
'shipping_address': '123 Test St'
|
|
})
|
|
|
|
assert response.status_code == 201
|
|
data = response.get_json()
|
|
assert 'id' in data
|
|
assert len(data['items']) == 2
|
|
|
|
@pytest.mark.order
|
|
def test_create_order_insufficient_stock(self, client, auth_headers, db_session, products):
|
|
"""Test creating order with insufficient stock"""
|
|
# Set stock to 0
|
|
products[0].stock = 0
|
|
db_session.commit()
|
|
|
|
response = client.post('/api/orders', headers=auth_headers, json={
|
|
'items': [
|
|
{'product_id': products[0].id, 'quantity': 2}
|
|
]
|
|
})
|
|
|
|
assert response.status_code == 400
|
|
data = response.get_json()
|
|
assert 'insufficient' in data['error'].lower()
|
|
|
|
@pytest.mark.order
|
|
def test_get_single_order(self, client, auth_headers, order):
|
|
"""Test getting a single order"""
|
|
response = client.get(f'/api/orders/{order.id}', headers=auth_headers)
|
|
|
|
print('test_get_single_order', response.get_json())
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert data['id'] == order.id
|
|
|
|
@pytest.mark.order
|
|
def test_get_other_users_order(self, client, admin_headers, regular_user, products):
|
|
"""Test admin accessing another user's order"""
|
|
# Create an order for regular_user
|
|
client.post('/api/auth/login', json={
|
|
'email': regular_user.email,
|
|
'password': 'password123'
|
|
})
|
|
|
|
# Admin should be able to access any order
|
|
response = client.get(f'/api/orders/1', headers=admin_headers)
|
|
# This test assumes order exists, adjust as needed
|
|
pass
|