mcp-security-framework 1.1.0__py3-none-any.whl → 1.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_security_framework/__init__.py +26 -15
- mcp_security_framework/cli/__init__.py +1 -1
- mcp_security_framework/cli/cert_cli.py +233 -197
- mcp_security_framework/cli/security_cli.py +324 -234
- mcp_security_framework/constants.py +21 -27
- mcp_security_framework/core/auth_manager.py +41 -22
- mcp_security_framework/core/cert_manager.py +210 -147
- mcp_security_framework/core/permission_manager.py +9 -9
- mcp_security_framework/core/rate_limiter.py +2 -2
- mcp_security_framework/core/security_manager.py +284 -229
- mcp_security_framework/examples/__init__.py +6 -0
- mcp_security_framework/examples/comprehensive_example.py +349 -279
- mcp_security_framework/examples/django_example.py +247 -206
- mcp_security_framework/examples/fastapi_example.py +315 -283
- mcp_security_framework/examples/flask_example.py +274 -203
- mcp_security_framework/examples/gateway_example.py +304 -237
- mcp_security_framework/examples/microservice_example.py +258 -189
- mcp_security_framework/examples/standalone_example.py +255 -230
- mcp_security_framework/examples/test_all_examples.py +151 -135
- mcp_security_framework/middleware/__init__.py +46 -55
- mcp_security_framework/middleware/auth_middleware.py +62 -63
- mcp_security_framework/middleware/fastapi_auth_middleware.py +119 -118
- mcp_security_framework/middleware/fastapi_middleware.py +156 -148
- mcp_security_framework/middleware/flask_auth_middleware.py +160 -147
- mcp_security_framework/middleware/flask_middleware.py +183 -157
- mcp_security_framework/middleware/mtls_middleware.py +106 -117
- mcp_security_framework/middleware/rate_limit_middleware.py +105 -101
- mcp_security_framework/middleware/security_middleware.py +109 -124
- mcp_security_framework/schemas/config.py +2 -1
- mcp_security_framework/schemas/models.py +18 -6
- mcp_security_framework/utils/cert_utils.py +14 -8
- mcp_security_framework/utils/datetime_compat.py +116 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/METADATA +4 -3
- mcp_security_framework-1.1.2.dist-info/RECORD +84 -0
- tests/conftest.py +63 -66
- tests/test_cli/test_cert_cli.py +184 -146
- tests/test_cli/test_security_cli.py +274 -247
- tests/test_core/test_cert_manager.py +24 -10
- tests/test_core/test_security_manager.py +2 -2
- tests/test_examples/test_comprehensive_example.py +190 -137
- tests/test_examples/test_fastapi_example.py +124 -101
- tests/test_examples/test_flask_example.py +124 -101
- tests/test_examples/test_standalone_example.py +73 -80
- tests/test_integration/test_auth_flow.py +213 -197
- tests/test_integration/test_certificate_flow.py +180 -149
- tests/test_integration/test_fastapi_integration.py +108 -111
- tests/test_integration/test_flask_integration.py +141 -140
- tests/test_integration/test_standalone_integration.py +290 -259
- tests/test_middleware/test_fastapi_auth_middleware.py +195 -174
- tests/test_middleware/test_fastapi_middleware.py +147 -132
- tests/test_middleware/test_flask_auth_middleware.py +260 -202
- tests/test_middleware/test_flask_middleware.py +201 -179
- tests/test_middleware/test_security_middleware.py +145 -130
- tests/test_utils/test_datetime_compat.py +147 -0
- mcp_security_framework-1.1.0.dist-info/RECORD +0 -82
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/WHEEL +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/entry_points.txt +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/top_level.txt +0 -0
@@ -11,25 +11,30 @@ License: MIT
|
|
11
11
|
"""
|
12
12
|
|
13
13
|
import json
|
14
|
-
import tempfile
|
15
14
|
import os
|
16
|
-
|
17
|
-
from typing import
|
15
|
+
import tempfile
|
16
|
+
from typing import Any, Dict
|
17
|
+
from unittest.mock import MagicMock, patch
|
18
18
|
|
19
19
|
import pytest
|
20
|
-
from flask.testing import FlaskClient
|
21
20
|
from cryptography import x509
|
22
21
|
from cryptography.hazmat.primitives import hashes, serialization
|
23
22
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
23
|
+
from flask.testing import FlaskClient
|
24
24
|
|
25
|
-
from mcp_security_framework.examples.flask_example import FlaskExample
|
26
25
|
from mcp_security_framework.core.security_manager import SecurityManager
|
27
|
-
from mcp_security_framework.
|
26
|
+
from mcp_security_framework.examples.flask_example import FlaskExample
|
27
|
+
from mcp_security_framework.schemas.config import (
|
28
|
+
AuthConfig,
|
29
|
+
RateLimitConfig,
|
30
|
+
SecurityConfig,
|
31
|
+
SSLConfig,
|
32
|
+
)
|
28
33
|
|
29
34
|
|
30
35
|
class TestFlaskIntegration:
|
31
36
|
"""Integration tests for Flask with security framework."""
|
32
|
-
|
37
|
+
|
33
38
|
def setup_method(self):
|
34
39
|
"""Set up test fixtures before each test method."""
|
35
40
|
# Create temporary configuration
|
@@ -40,227 +45,226 @@ class TestFlaskIntegration:
|
|
40
45
|
"api_keys": {
|
41
46
|
"admin_key_123": {"username": "admin", "roles": ["admin", "user"]},
|
42
47
|
"user_key_456": {"username": "user", "roles": ["user"]},
|
43
|
-
"readonly_key_789": {"username": "readonly", "roles": ["readonly"]}
|
44
|
-
}
|
45
|
-
},
|
46
|
-
"rate_limit": {
|
47
|
-
"enabled": True,
|
48
|
-
"default_requests_per_minute": 100
|
49
|
-
},
|
50
|
-
"ssl": {
|
51
|
-
"enabled": False
|
52
|
-
},
|
53
|
-
"permissions": {
|
54
|
-
"enabled": True,
|
55
|
-
"roles_file": "test_roles.json"
|
56
|
-
},
|
57
|
-
"certificates": {
|
58
|
-
"enabled": False
|
48
|
+
"readonly_key_789": {"username": "readonly", "roles": ["readonly"]},
|
49
|
+
},
|
59
50
|
},
|
60
|
-
"
|
61
|
-
|
62
|
-
|
63
|
-
}
|
51
|
+
"rate_limit": {"enabled": True, "default_requests_per_minute": 100},
|
52
|
+
"ssl": {"enabled": False},
|
53
|
+
"permissions": {"enabled": True, "roles_file": "test_roles.json"},
|
54
|
+
"certificates": {"enabled": False},
|
55
|
+
"logging": {"level": "INFO", "format": "standard"},
|
64
56
|
}
|
65
|
-
|
57
|
+
|
66
58
|
# Create temporary config file
|
67
|
-
self.config_fd, self.config_path = tempfile.mkstemp(suffix=
|
68
|
-
with os.fdopen(self.config_fd,
|
59
|
+
self.config_fd, self.config_path = tempfile.mkstemp(suffix=".json")
|
60
|
+
with os.fdopen(self.config_fd, "w") as f:
|
69
61
|
json.dump(self.test_config, f)
|
70
|
-
|
62
|
+
|
71
63
|
# Create temporary roles file
|
72
64
|
self.roles_config = {
|
73
65
|
"roles": {
|
74
66
|
"admin": {
|
75
67
|
"permissions": ["read", "write", "delete", "admin"],
|
76
|
-
"description": "Administrator role"
|
68
|
+
"description": "Administrator role",
|
77
69
|
},
|
78
70
|
"user": {
|
79
71
|
"permissions": ["read", "write"],
|
80
|
-
"description": "Regular user role"
|
72
|
+
"description": "Regular user role",
|
81
73
|
},
|
82
74
|
"readonly": {
|
83
75
|
"permissions": ["read"],
|
84
|
-
"description": "Read-only user role"
|
85
|
-
}
|
76
|
+
"description": "Read-only user role",
|
77
|
+
},
|
86
78
|
}
|
87
79
|
}
|
88
|
-
|
89
|
-
self.roles_fd, self.roles_path = tempfile.mkstemp(suffix=
|
90
|
-
with os.fdopen(self.roles_fd,
|
80
|
+
|
81
|
+
self.roles_fd, self.roles_path = tempfile.mkstemp(suffix=".json")
|
82
|
+
with os.fdopen(self.roles_fd, "w") as f:
|
91
83
|
json.dump(self.roles_config, f)
|
92
|
-
|
84
|
+
|
93
85
|
# Update config to use roles file
|
94
86
|
self.test_config["permissions"]["roles_file"] = self.roles_path
|
95
|
-
|
87
|
+
|
96
88
|
# Recreate config file with updated roles path
|
97
|
-
with open(self.config_path,
|
89
|
+
with open(self.config_path, "w") as f:
|
98
90
|
json.dump(self.test_config, f)
|
99
|
-
|
91
|
+
|
100
92
|
def teardown_method(self):
|
101
93
|
"""Clean up after each test method."""
|
102
94
|
# Remove temporary files
|
103
|
-
if hasattr(self,
|
95
|
+
if hasattr(self, "config_path") and os.path.exists(self.config_path):
|
104
96
|
os.unlink(self.config_path)
|
105
|
-
if hasattr(self,
|
97
|
+
if hasattr(self, "roles_path") and os.path.exists(self.roles_path):
|
106
98
|
os.unlink(self.roles_path)
|
107
|
-
|
99
|
+
|
108
100
|
def test_flask_full_integration(self):
|
109
101
|
"""Test complete Flask integration with security framework."""
|
110
102
|
# Create Flask example
|
111
103
|
example = FlaskExample(config_path=self.config_path)
|
112
|
-
|
104
|
+
|
113
105
|
# Test that the app is properly configured
|
114
106
|
assert example.app is not None
|
115
|
-
assert hasattr(example.app,
|
116
|
-
|
107
|
+
assert hasattr(example.app, "wsgi_app")
|
108
|
+
|
117
109
|
# Test that security manager is configured
|
118
110
|
assert example.security_manager is not None
|
119
111
|
assert isinstance(example.security_manager, SecurityManager)
|
120
|
-
|
112
|
+
|
121
113
|
# Test that configuration is loaded
|
122
114
|
assert example.config is not None
|
123
115
|
assert example.config.auth.enabled is True
|
124
116
|
assert example.config.rate_limit.enabled is True
|
125
|
-
|
117
|
+
|
126
118
|
def test_flask_authentication_flow(self):
|
127
119
|
"""Test complete authentication flow in Flask."""
|
128
120
|
example = FlaskExample(config_path=self.config_path)
|
129
121
|
client = example.app.test_client()
|
130
|
-
|
122
|
+
|
131
123
|
# Test unauthenticated access to protected endpoint
|
132
124
|
response = client.get("/api/v1/users/me")
|
133
125
|
assert response.status_code == 401
|
134
|
-
|
126
|
+
|
135
127
|
# Test authenticated access with valid API key
|
136
128
|
headers = {"X-API-Key": "admin_key_123"}
|
137
129
|
response = client.get("/api/v1/users/me", headers=headers)
|
138
130
|
assert response.status_code == 200 # Should be authenticated
|
139
|
-
|
131
|
+
|
140
132
|
# Test authenticated access with different user
|
141
133
|
headers = {"X-API-Key": "user_key_456"}
|
142
134
|
response = client.get("/api/v1/users/me", headers=headers)
|
143
135
|
assert response.status_code == 200 # User should be authenticated
|
144
|
-
|
136
|
+
|
145
137
|
def test_flask_authorization_flow(self):
|
146
138
|
"""Test complete authorization flow in Flask."""
|
147
139
|
example = FlaskExample(config_path=self.config_path)
|
148
140
|
client = example.app.test_client()
|
149
|
-
|
141
|
+
|
150
142
|
# Test admin access to admin-only endpoint
|
151
143
|
headers = {"X-API-Key": "admin_key_123"}
|
152
144
|
response = client.get("/api/v1/admin/users", headers=headers)
|
153
145
|
assert response.status_code == 200 # Admin should have access
|
154
|
-
|
146
|
+
|
155
147
|
# Test regular user access to admin-only endpoint (should be denied)
|
156
148
|
headers = {"X-API-Key": "user_key_456"}
|
157
149
|
response = client.get("/api/v1/admin/users", headers=headers)
|
158
150
|
assert response.status_code == 403 # User should be denied admin access
|
159
|
-
|
151
|
+
|
160
152
|
# Test readonly user access to write endpoint (should be denied)
|
161
153
|
headers = {"X-API-Key": "readonly_key_789"}
|
162
|
-
response = client.post(
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
154
|
+
response = client.post(
|
155
|
+
"/api/v1/data",
|
156
|
+
headers=headers,
|
157
|
+
json={"name": "test", "value": "test_value"},
|
158
|
+
)
|
159
|
+
assert (
|
160
|
+
response.status_code == 403
|
161
|
+
) # Readonly user should be denied write access
|
162
|
+
|
167
163
|
def test_flask_rate_limiting(self):
|
168
164
|
"""Test rate limiting in Flask."""
|
169
165
|
example = FlaskExample(config_path=self.config_path)
|
170
166
|
client = example.app.test_client()
|
171
|
-
|
167
|
+
|
172
168
|
headers = {"X-API-Key": "user_key_456"}
|
173
|
-
|
169
|
+
|
174
170
|
# Make multiple requests to trigger rate limiting
|
175
171
|
responses = []
|
176
172
|
for i in range(105): # Exceed the 100 requests per minute limit
|
177
173
|
response = client.get("/api/v1/users/me", headers=headers)
|
178
174
|
responses.append(response.status_code)
|
179
|
-
|
175
|
+
|
180
176
|
# Check that some requests were rate limited
|
181
177
|
# Note: Rate limiting may not be triggered in test environment
|
182
178
|
# but the requests should still be processed
|
183
179
|
assert len(responses) == 105, "All requests should be processed"
|
184
|
-
assert all(
|
185
|
-
|
180
|
+
assert all(
|
181
|
+
status in [200, 429] for status in responses
|
182
|
+
), "Responses should be either 200 or 429"
|
183
|
+
|
186
184
|
def test_flask_ssl_integration(self):
|
187
185
|
"""Test SSL/TLS integration in Flask."""
|
188
186
|
# Create config with SSL enabled
|
189
187
|
ssl_config = self.test_config.copy()
|
190
|
-
ssl_config["ssl"] = {
|
191
|
-
|
192
|
-
}
|
193
|
-
|
188
|
+
ssl_config["ssl"] = {"enabled": False} # Disable SSL for testing
|
189
|
+
|
194
190
|
# Create temporary SSL config file
|
195
|
-
ssl_config_fd, ssl_config_path = tempfile.mkstemp(suffix=
|
196
|
-
with os.fdopen(ssl_config_fd,
|
191
|
+
ssl_config_fd, ssl_config_path = tempfile.mkstemp(suffix=".json")
|
192
|
+
with os.fdopen(ssl_config_fd, "w") as f:
|
197
193
|
json.dump(ssl_config, f)
|
198
|
-
|
194
|
+
|
199
195
|
try:
|
200
196
|
# Mock SSL context creation to avoid file requirements
|
201
|
-
with patch(
|
197
|
+
with patch(
|
198
|
+
"mcp_security_framework.core.ssl_manager.SSLManager.create_server_context"
|
199
|
+
) as mock_ssl:
|
202
200
|
mock_ssl.return_value = MagicMock()
|
203
|
-
|
201
|
+
|
204
202
|
example = FlaskExample(config_path=ssl_config_path)
|
205
|
-
|
203
|
+
|
206
204
|
# Test that SSL is configured
|
207
205
|
assert example.config.ssl.enabled is False # SSL disabled for testing
|
208
|
-
|
206
|
+
|
209
207
|
finally:
|
210
208
|
os.unlink(ssl_config_path)
|
211
|
-
|
209
|
+
|
212
210
|
def test_flask_error_handling(self):
|
213
211
|
"""Test error handling in Flask integration."""
|
214
212
|
example = FlaskExample(config_path=self.config_path)
|
215
213
|
client = example.app.test_client()
|
216
|
-
|
214
|
+
|
217
215
|
# Test invalid API key
|
218
216
|
headers = {"X-API-Key": "invalid_key"}
|
219
217
|
response = client.get("/api/v1/users/me", headers=headers)
|
220
218
|
assert response.status_code == 401
|
221
|
-
|
219
|
+
|
222
220
|
# Test malformed request
|
223
221
|
headers = {"X-API-Key": "admin_key_123"}
|
224
|
-
response = client.post(
|
225
|
-
|
226
|
-
|
227
|
-
assert
|
228
|
-
|
222
|
+
response = client.post(
|
223
|
+
"/api/v1/data", headers=headers, json={"invalid": "data"}
|
224
|
+
)
|
225
|
+
assert (
|
226
|
+
response.status_code == 200
|
227
|
+
) # Should succeed with valid auth (Flask returns 200 for POST)
|
228
|
+
|
229
229
|
def test_flask_health_and_metrics(self):
|
230
230
|
"""Test health check and metrics endpoints."""
|
231
231
|
example = FlaskExample(config_path=self.config_path)
|
232
232
|
client = example.app.test_client()
|
233
|
-
|
233
|
+
|
234
234
|
# Test health check
|
235
235
|
response = client.get("/health")
|
236
236
|
assert response.status_code == 200
|
237
237
|
data = response.get_json()
|
238
238
|
assert "status" in data
|
239
239
|
assert data["status"] == "healthy"
|
240
|
-
|
240
|
+
|
241
241
|
# Test metrics
|
242
242
|
response = client.get("/metrics")
|
243
243
|
assert response.status_code == 200
|
244
244
|
data = response.get_json()
|
245
245
|
assert "uptime_seconds" in data
|
246
246
|
assert "requests_total" in data
|
247
|
-
|
247
|
+
|
248
248
|
def test_flask_data_operations(self):
|
249
249
|
"""Test data operations with security."""
|
250
250
|
example = FlaskExample(config_path=self.config_path)
|
251
251
|
client = example.app.test_client()
|
252
|
-
|
252
|
+
|
253
253
|
headers = {"X-API-Key": "admin_key_123"}
|
254
|
-
|
254
|
+
|
255
255
|
# Create data
|
256
|
-
create_response = client.post(
|
257
|
-
|
258
|
-
|
256
|
+
create_response = client.post(
|
257
|
+
"/api/v1/data",
|
258
|
+
headers=headers,
|
259
|
+
json={"name": "test_item", "value": "test_value"},
|
260
|
+
)
|
259
261
|
|
260
|
-
assert
|
262
|
+
assert (
|
263
|
+
create_response.status_code == 200
|
264
|
+
) # Should succeed with valid auth (Flask returns 200 for POST)
|
261
265
|
data = create_response.get_json()
|
262
266
|
assert "id" in data
|
263
|
-
|
267
|
+
|
264
268
|
# Retrieve data
|
265
269
|
data_id = data["id"]
|
266
270
|
get_response = client.get(f"/api/v1/data/{data_id}", headers=headers)
|
@@ -268,133 +272,130 @@ class TestFlaskIntegration:
|
|
268
272
|
retrieved_data = get_response.get_json()
|
269
273
|
assert retrieved_data["id"] == data_id
|
270
274
|
assert "data" in retrieved_data
|
271
|
-
|
275
|
+
|
272
276
|
def test_flask_middleware_integration(self):
|
273
277
|
"""Test that security middleware is properly integrated."""
|
274
278
|
example = FlaskExample(config_path=self.config_path)
|
275
|
-
|
279
|
+
|
276
280
|
# Check that middleware is configured
|
277
281
|
# Note: In test environment, middleware setup is skipped
|
278
282
|
# but we can verify the app structure
|
279
|
-
assert hasattr(example.app,
|
280
|
-
|
283
|
+
assert hasattr(example.app, "wsgi_app")
|
284
|
+
|
281
285
|
# Test that routes are properly configured
|
282
286
|
routes = []
|
283
287
|
for rule in example.app.url_map.iter_rules():
|
284
288
|
routes.append(rule.rule)
|
285
|
-
|
289
|
+
|
286
290
|
expected_routes = [
|
287
291
|
"/health",
|
288
|
-
"/metrics",
|
292
|
+
"/metrics",
|
289
293
|
"/api/v1/users/me",
|
290
294
|
"/api/v1/admin/users",
|
291
295
|
"/api/v1/data",
|
292
|
-
"/api/v1/data/<data_id>"
|
296
|
+
"/api/v1/data/<data_id>",
|
293
297
|
]
|
294
|
-
|
298
|
+
|
295
299
|
for route in expected_routes:
|
296
300
|
assert route in routes, f"Route {route} not found in app routes"
|
297
|
-
|
301
|
+
|
298
302
|
def test_flask_configuration_validation(self):
|
299
303
|
"""Test configuration validation in Flask integration."""
|
300
304
|
# Test with invalid configuration
|
301
|
-
invalid_config = {
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
}
|
306
|
-
}
|
307
|
-
|
308
|
-
invalid_config_fd, invalid_config_path = tempfile.mkstemp(suffix='.json')
|
309
|
-
with os.fdopen(invalid_config_fd, 'w') as f:
|
305
|
+
invalid_config = {"auth": {"enabled": True, "methods": ["invalid_method"]}}
|
306
|
+
|
307
|
+
invalid_config_fd, invalid_config_path = tempfile.mkstemp(suffix=".json")
|
308
|
+
with os.fdopen(invalid_config_fd, "w") as f:
|
310
309
|
json.dump(invalid_config, f)
|
311
|
-
|
310
|
+
|
312
311
|
try:
|
313
312
|
# Should raise validation error
|
314
313
|
with pytest.raises(Exception):
|
315
314
|
FlaskExample(config_path=invalid_config_path)
|
316
315
|
finally:
|
317
316
|
os.unlink(invalid_config_path)
|
318
|
-
|
317
|
+
|
319
318
|
def test_flask_performance_benchmark(self):
|
320
319
|
"""Test performance of Flask integration."""
|
321
320
|
example = FlaskExample(config_path=self.config_path)
|
322
321
|
client = example.app.test_client()
|
323
|
-
|
322
|
+
|
324
323
|
headers = {"X-API-Key": "user_key_456"}
|
325
|
-
|
324
|
+
|
326
325
|
import time
|
327
|
-
|
326
|
+
|
328
327
|
# Benchmark health check endpoint
|
329
328
|
start_time = time.time()
|
330
329
|
for _ in range(100):
|
331
330
|
response = client.get("/health")
|
332
331
|
assert response.status_code == 200
|
333
332
|
end_time = time.time()
|
334
|
-
|
333
|
+
|
335
334
|
avg_time = (end_time - start_time) / 100
|
336
335
|
assert avg_time < 0.01, f"Health check too slow: {avg_time:.4f}s per request"
|
337
|
-
|
336
|
+
|
338
337
|
# Benchmark authenticated endpoint
|
339
338
|
start_time = time.time()
|
340
339
|
for _ in range(50):
|
341
340
|
response = client.get("/api/v1/users/me", headers=headers)
|
342
341
|
assert response.status_code == 200 # Should be authenticated
|
343
342
|
end_time = time.time()
|
344
|
-
|
343
|
+
|
345
344
|
avg_time = (end_time - start_time) / 50
|
346
|
-
assert
|
347
|
-
|
345
|
+
assert (
|
346
|
+
avg_time < 0.02
|
347
|
+
), f"Authenticated endpoint too slow: {avg_time:.4f}s per request"
|
348
|
+
|
348
349
|
def test_flask_session_management(self):
|
349
350
|
"""Test session management in Flask."""
|
350
351
|
example = FlaskExample(config_path=self.config_path)
|
351
352
|
client = example.app.test_client()
|
352
|
-
|
353
|
+
|
353
354
|
# Test that sessions are properly configured
|
354
|
-
assert hasattr(example.app,
|
355
|
-
assert
|
356
|
-
|
355
|
+
assert hasattr(example.app, "config")
|
356
|
+
assert "SECRET_KEY" in example.app.config
|
357
|
+
|
357
358
|
# Test session persistence across requests
|
358
359
|
headers = {"X-API-Key": "admin_key_123"}
|
359
|
-
|
360
|
+
|
360
361
|
# First request
|
361
362
|
response1 = client.get("/api/v1/users/me", headers=headers)
|
362
363
|
assert response1.status_code == 200 # Should be authenticated
|
363
|
-
|
364
|
+
|
364
365
|
# Second request with same session
|
365
366
|
response2 = client.get("/api/v1/users/me", headers=headers)
|
366
367
|
assert response2.status_code == 200 # Should be authenticated
|
367
|
-
|
368
|
+
|
368
369
|
# Verify consistent user data
|
369
370
|
data1 = response1.get_json()
|
370
371
|
data2 = response2.get_json()
|
371
372
|
assert data1["username"] == data2["username"]
|
372
|
-
|
373
|
+
|
373
374
|
def test_flask_cors_integration(self):
|
374
375
|
"""Test CORS integration in Flask."""
|
375
376
|
example = FlaskExample(config_path=self.config_path)
|
376
377
|
client = example.app.test_client()
|
377
|
-
|
378
|
+
|
378
379
|
# Test CORS headers are present
|
379
380
|
response = client.get("/health")
|
380
381
|
assert response.status_code == 200
|
381
|
-
|
382
|
+
|
382
383
|
# Check for CORS headers (if CORS is configured)
|
383
384
|
# Note: This depends on CORS configuration in the Flask app
|
384
|
-
cors_headers = [
|
385
|
+
cors_headers = ["Access-Control-Allow-Origin", "Access-Control-Allow-Methods"]
|
385
386
|
# We don't assert specific CORS headers as they may not be configured in test mode
|
386
|
-
|
387
|
+
|
387
388
|
def test_flask_logging_integration(self):
|
388
389
|
"""Test logging integration in Flask."""
|
389
390
|
example = FlaskExample(config_path=self.config_path)
|
390
391
|
client = example.app.test_client()
|
391
|
-
|
392
|
+
|
392
393
|
# Test that logging is configured
|
393
|
-
assert hasattr(example.app,
|
394
|
-
|
394
|
+
assert hasattr(example.app, "logger")
|
395
|
+
|
395
396
|
# Test that requests are logged
|
396
397
|
response = client.get("/health")
|
397
398
|
assert response.status_code == 200
|
398
|
-
|
399
|
+
|
399
400
|
# Verify that the app has proper logging configuration
|
400
401
|
assert example.app.logger is not None
|