-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathconftest.py
More file actions
241 lines (197 loc) · 6.63 KB
/
conftest.py
File metadata and controls
241 lines (197 loc) · 6.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
"""
Pytest Configuration and Fixtures
This file contains shared fixtures used across all test modules.
Team Convention: All new tests should use these fixtures.
Do not create duplicate fixtures in individual test files.
"""
import pytest
from datetime import datetime, timedelta
from decimal import Decimal
from typing import Generator
from fastapi.testclient import TestClient
from httpx import AsyncClient
from src.main import app
from src.models.order import Order, OrderItem, OrderStatus, ShippingAddress
from src.models.customer import Customer, CustomerTier
from src.legacy.auth_provider import Session, _SESSION_STORE
# NOTE: Use datetime.utcnow() to match legacy auth_provider pattern (naive datetime)
# Do NOT use timezone-aware datetimes here - it causes comparison issues
# =============================================================================
# CLIENT FIXTURES
# =============================================================================
@pytest.fixture
def client() -> Generator[TestClient, None, None]:
"""
Synchronous test client for FastAPI.
Use this for simple endpoint tests that don't require async.
"""
with TestClient(app) as c:
yield c
@pytest.fixture
async def async_client() -> AsyncClient:
"""
Async test client for FastAPI.
Use this when testing async endpoints or when you need
to make multiple concurrent requests.
"""
async with AsyncClient(app=app, base_url="http://test") as ac:
yield ac
# =============================================================================
# AUTHENTICATION FIXTURES
# =============================================================================
@pytest.fixture
def test_session() -> Session:
"""
Create a test session for authenticated requests.
Usage:
def test_protected_endpoint(client, test_session):
response = client.get(
"/api/v1/orders",
headers={"X-Session-ID": test_session.session_id}
)
"""
session = Session(
session_id="test_session_12345",
user_id="test_user_001",
user_email="test@contoso.com",
is_admin=False,
created_at=datetime.utcnow(),
expires_at=datetime.utcnow() + timedelta(hours=1),
)
_SESSION_STORE[session.session_id] = session
yield session
# Cleanup
_SESSION_STORE.pop(session.session_id, None)
@pytest.fixture
def admin_session() -> Session:
"""
Create an admin session for testing admin-only endpoints.
"""
session = Session(
session_id="admin_test_session_12345",
user_id="admin_test_001",
user_email="admin.test@contoso.com",
is_admin=True,
created_at=datetime.utcnow(),
expires_at=datetime.utcnow() + timedelta(hours=1),
)
_SESSION_STORE[session.session_id] = session
yield session
# Cleanup
_SESSION_STORE.pop(session.session_id, None)
@pytest.fixture
def auth_headers(test_session: Session) -> dict:
"""
Convenience fixture for authentication headers.
Usage:
def test_endpoint(client, auth_headers):
response = client.get("/api/v1/orders", headers=auth_headers)
"""
return {"X-Session-ID": test_session.session_id}
@pytest.fixture
def admin_headers(admin_session: Session) -> dict:
"""Authentication headers for admin requests."""
return {"X-Session-ID": admin_session.session_id}
# =============================================================================
# DOMAIN OBJECT FIXTURES
# =============================================================================
@pytest.fixture
def sample_customer() -> Customer:
"""Create a sample customer for testing."""
return Customer(
id="cust_test_001",
name="Test Customer",
email="customer@test.com",
company="Test Corp",
tier=CustomerTier.PREMIUM,
is_active=True,
created_at=datetime.utcnow(),
)
@pytest.fixture
def sample_order_item() -> OrderItem:
"""Create a sample order item."""
return OrderItem(
product_id="prod_test_001",
sku="TEST-SKU-001",
name="Test Product",
quantity=2,
unit_price=Decimal("99.99"),
)
@pytest.fixture
def sample_shipping_address() -> ShippingAddress:
"""Create a sample shipping address."""
return ShippingAddress(
street="123 Test Street",
city="Test City",
state="WA",
postal_code="98101",
country="US",
name="Test Recipient",
phone="555-0100",
)
@pytest.fixture
def sample_order(
sample_order_item: OrderItem,
sample_shipping_address: ShippingAddress,
) -> Order:
"""
Create a sample order for testing.
This fixture depends on sample_order_item and sample_shipping_address.
"""
return Order(
id="ORD-TEST12345678",
customer_id="cust_test_001",
items=[sample_order_item],
status=OrderStatus.PENDING,
subtotal=Decimal("199.98"),
tax=Decimal("16.00"),
shipping_cost=Decimal("8.99"),
total=Decimal("224.97"),
shipping_address=sample_shipping_address,
created_at=datetime.utcnow(),
updated_at=datetime.utcnow(),
)
# =============================================================================
# REQUEST PAYLOAD FIXTURES
# =============================================================================
@pytest.fixture
def create_order_payload() -> dict:
"""
Sample payload for creating an order.
Use this as a base and modify as needed for specific tests.
"""
return {
"customer_id": "cust_test_001",
"items": [
{
"product_id": "prod_001",
"sku": "LAPTOP-PRO-15",
"name": "ProBook Laptop 15\"",
"quantity": 1,
"unit_price": "1299.99",
}
],
"shipping_address": {
"street": "123 Test Street",
"city": "Seattle",
"state": "WA",
"postal_code": "98101",
"country": "US",
},
}
# =============================================================================
# CLEANUP FIXTURES
# =============================================================================
@pytest.fixture(autouse=True)
def cleanup_orders():
"""
Automatically clean up orders after each test.
This ensures tests don't interfere with each other.
"""
from src.repositories.order_repo import _ORDERS
# Store existing orders
existing_orders = dict(_ORDERS)
yield
# Restore original state after test
_ORDERS.clear()
_ORDERS.update(existing_orders)