pytest-dsl 0.1.0__py3-none-any.whl → 0.2.0__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.
- pytest_dsl/core/__init__.py +7 -0
- pytest_dsl/core/auth_provider.py +50 -10
- pytest_dsl/core/custom_keyword_manager.py +213 -0
- pytest_dsl/core/dsl_executor.py +39 -2
- pytest_dsl/core/http_client.py +11 -6
- pytest_dsl/core/http_request.py +517 -119
- pytest_dsl/core/lexer.py +14 -1
- pytest_dsl/core/parser.py +45 -2
- pytest_dsl/core/parsetab.py +50 -38
- pytest_dsl/core/variable_utils.py +1 -1
- pytest_dsl/examples/custom/test_advanced_keywords.auto +31 -0
- pytest_dsl/examples/custom/test_custom_keywords.auto +37 -0
- pytest_dsl/examples/custom/test_default_values.auto +34 -0
- pytest_dsl/examples/http/http_retry_assertions.auto +2 -2
- pytest_dsl/examples/http/http_retry_assertions_enhanced.auto +2 -2
- pytest_dsl/examples/quickstart/api_basics.auto +55 -0
- pytest_dsl/examples/quickstart/assertions.auto +31 -0
- pytest_dsl/examples/quickstart/loops.auto +24 -0
- pytest_dsl/examples/test_custom_keyword.py +9 -0
- pytest_dsl/examples/test_http.py +0 -139
- pytest_dsl/examples/test_quickstart.py +14 -0
- pytest_dsl/keywords/http_keywords.py +290 -102
- pytest_dsl/parsetab.py +69 -0
- pytest_dsl-0.2.0.dist-info/METADATA +504 -0
- {pytest_dsl-0.1.0.dist-info → pytest_dsl-0.2.0.dist-info}/RECORD +29 -24
- {pytest_dsl-0.1.0.dist-info → pytest_dsl-0.2.0.dist-info}/WHEEL +1 -1
- pytest_dsl/core/custom_auth_example.py +0 -425
- pytest_dsl/examples/http/csrf_auth_test.auto +0 -64
- pytest_dsl/examples/http/custom_auth_test.auto +0 -76
- pytest_dsl/examples/http_clients.yaml +0 -48
- pytest_dsl/examples/keyword_example.py +0 -70
- pytest_dsl-0.1.0.dist-info/METADATA +0 -537
- {pytest_dsl-0.1.0.dist-info → pytest_dsl-0.2.0.dist-info}/entry_points.txt +0 -0
- {pytest_dsl-0.1.0.dist-info → pytest_dsl-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {pytest_dsl-0.1.0.dist-info → pytest_dsl-0.2.0.dist-info}/top_level.txt +0 -0
@@ -1,425 +0,0 @@
|
|
1
|
-
"""自定义授权示例模块
|
2
|
-
|
3
|
-
该模块提供了几个复杂授权流程的实现示例,展示如何开发和注册自定义授权提供者。
|
4
|
-
"""
|
5
|
-
|
6
|
-
import time
|
7
|
-
import json
|
8
|
-
import logging
|
9
|
-
import hashlib
|
10
|
-
import hmac
|
11
|
-
import base64
|
12
|
-
import uuid
|
13
|
-
from datetime import datetime
|
14
|
-
from typing import Dict, Any, Optional
|
15
|
-
|
16
|
-
import requests
|
17
|
-
|
18
|
-
from pytest_dsl.core.auth_provider import (
|
19
|
-
AuthProvider, register_auth_provider, CustomAuthProvider
|
20
|
-
)
|
21
|
-
|
22
|
-
logger = logging.getLogger(__name__)
|
23
|
-
|
24
|
-
|
25
|
-
class HmacAuthProvider(AuthProvider):
|
26
|
-
"""HMAC签名认证提供者
|
27
|
-
|
28
|
-
使用HMAC算法对请求参数进行签名,常用于AWS, 阿里云等API认证。
|
29
|
-
"""
|
30
|
-
|
31
|
-
def __init__(self, access_key: str, secret_key: str,
|
32
|
-
region: str = None, service: str = None,
|
33
|
-
algorithm: str = 'sha256'):
|
34
|
-
"""初始化HMAC签名认证
|
35
|
-
|
36
|
-
Args:
|
37
|
-
access_key: 访问密钥ID
|
38
|
-
secret_key: 秘密访问密钥
|
39
|
-
region: 区域 (用于某些云服务API)
|
40
|
-
service: 服务名称 (用于某些云服务API)
|
41
|
-
algorithm: 哈希算法 ('sha256', 'sha1', 等)
|
42
|
-
"""
|
43
|
-
self.access_key = access_key
|
44
|
-
self.secret_key = secret_key
|
45
|
-
self.region = region
|
46
|
-
self.service = service
|
47
|
-
self.algorithm = algorithm
|
48
|
-
|
49
|
-
def apply_auth(self, request_kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
50
|
-
"""应用HMAC签名认证
|
51
|
-
|
52
|
-
Args:
|
53
|
-
request_kwargs: 请求参数
|
54
|
-
|
55
|
-
Returns:
|
56
|
-
更新后的请求参数
|
57
|
-
"""
|
58
|
-
# 确保headers存在
|
59
|
-
if "headers" not in request_kwargs:
|
60
|
-
request_kwargs["headers"] = {}
|
61
|
-
|
62
|
-
# 获取当前时间
|
63
|
-
now = datetime.utcnow()
|
64
|
-
amz_date = now.strftime('%Y%m%dT%H%M%SZ')
|
65
|
-
date_stamp = now.strftime('%Y%m%d')
|
66
|
-
|
67
|
-
# 添加必要的头部
|
68
|
-
request_kwargs["headers"]["X-Amz-Date"] = amz_date
|
69
|
-
|
70
|
-
# 准备签名参数
|
71
|
-
method = request_kwargs.get("method", "GET")
|
72
|
-
url_path = "/" # 简化示例
|
73
|
-
|
74
|
-
# 创建规范请求
|
75
|
-
canonical_uri = url_path
|
76
|
-
canonical_querystring = ""
|
77
|
-
|
78
|
-
# 创建规范头部
|
79
|
-
canonical_headers = '\n'.join([
|
80
|
-
f"{header.lower()}:{value}"
|
81
|
-
for header, value in sorted(request_kwargs["headers"].items())
|
82
|
-
]) + '\n'
|
83
|
-
|
84
|
-
signed_headers = ';'.join([
|
85
|
-
header.lower() for header in sorted(request_kwargs["headers"].keys())
|
86
|
-
])
|
87
|
-
|
88
|
-
# 获取请求体的哈希值
|
89
|
-
payload_hash = hashlib.sha256(b'').hexdigest() # 简化示例
|
90
|
-
|
91
|
-
# 创建规范请求
|
92
|
-
canonical_request = f"{method}\n{canonical_uri}\n{canonical_querystring}\n{canonical_headers}\n{signed_headers}\n{payload_hash}"
|
93
|
-
|
94
|
-
# 创建签名字符串
|
95
|
-
algorithm = f"HMAC-{self.algorithm.upper()}"
|
96
|
-
credential_scope = f"{date_stamp}/{self.region}/{self.service}/aws4_request"
|
97
|
-
string_to_sign = f"{algorithm}\n{amz_date}\n{credential_scope}\n{hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()}"
|
98
|
-
|
99
|
-
# 计算签名
|
100
|
-
k_date = hmac.new(f"AWS4{self.secret_key}".encode('utf-8'), date_stamp.encode('utf-8'), hashlib.sha256).digest()
|
101
|
-
k_region = hmac.new(k_date, self.region.encode('utf-8'), hashlib.sha256).digest()
|
102
|
-
k_service = hmac.new(k_region, self.service.encode('utf-8'), hashlib.sha256).digest()
|
103
|
-
k_signing = hmac.new(k_service, b"aws4_request", hashlib.sha256).digest()
|
104
|
-
signature = hmac.new(k_signing, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()
|
105
|
-
|
106
|
-
# 添加授权头
|
107
|
-
authorization_header = (
|
108
|
-
f"{algorithm} "
|
109
|
-
f"Credential={self.access_key}/{credential_scope}, "
|
110
|
-
f"SignedHeaders={signed_headers}, "
|
111
|
-
f"Signature={signature}"
|
112
|
-
)
|
113
|
-
request_kwargs["headers"]["Authorization"] = authorization_header
|
114
|
-
|
115
|
-
return request_kwargs
|
116
|
-
|
117
|
-
|
118
|
-
class JwtRefreshAuthProvider(AuthProvider):
|
119
|
-
"""JWT刷新令牌授权提供者
|
120
|
-
|
121
|
-
使用刷新令牌自动获取和刷新JWT访问令牌。
|
122
|
-
"""
|
123
|
-
|
124
|
-
def __init__(self, token_url: str, refresh_token: str, client_id: str = None,
|
125
|
-
client_secret: str = None, token_refresh_window: int = 60):
|
126
|
-
"""初始化JWT刷新令牌授权
|
127
|
-
|
128
|
-
Args:
|
129
|
-
token_url: 获取/刷新令牌的URL
|
130
|
-
refresh_token: 刷新令牌
|
131
|
-
client_id: 客户端ID (可选)
|
132
|
-
client_secret: 客户端密钥 (可选)
|
133
|
-
token_refresh_window: 令牌刷新窗口 (秒)
|
134
|
-
"""
|
135
|
-
self.token_url = token_url
|
136
|
-
self.refresh_token = refresh_token
|
137
|
-
self.client_id = client_id
|
138
|
-
self.client_secret = client_secret
|
139
|
-
self.token_refresh_window = token_refresh_window
|
140
|
-
|
141
|
-
# 令牌缓存
|
142
|
-
self._access_token = None
|
143
|
-
self._token_expires_at = 0
|
144
|
-
|
145
|
-
def apply_auth(self, request_kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
146
|
-
"""应用JWT授权
|
147
|
-
|
148
|
-
Args:
|
149
|
-
request_kwargs: 请求参数
|
150
|
-
|
151
|
-
Returns:
|
152
|
-
更新后的请求参数
|
153
|
-
"""
|
154
|
-
# 确保有有效的令牌
|
155
|
-
self._ensure_valid_token()
|
156
|
-
|
157
|
-
# 确保headers存在
|
158
|
-
if "headers" not in request_kwargs:
|
159
|
-
request_kwargs["headers"] = {}
|
160
|
-
|
161
|
-
# 添加认证头
|
162
|
-
request_kwargs["headers"]["Authorization"] = f"Bearer {self._access_token}"
|
163
|
-
return request_kwargs
|
164
|
-
|
165
|
-
def _ensure_valid_token(self) -> None:
|
166
|
-
"""确保有有效的访问令牌"""
|
167
|
-
current_time = time.time()
|
168
|
-
|
169
|
-
# 如果令牌不存在或即将过期,刷新令牌
|
170
|
-
if not self._access_token or current_time + self.token_refresh_window >= self._token_expires_at:
|
171
|
-
self._refresh_token()
|
172
|
-
|
173
|
-
def _refresh_token(self) -> None:
|
174
|
-
"""使用刷新令牌获取新的访问令牌"""
|
175
|
-
data = {
|
176
|
-
"grant_type": "refresh_token",
|
177
|
-
"refresh_token": self.refresh_token
|
178
|
-
}
|
179
|
-
|
180
|
-
# 添加客户端凭据(如果提供)
|
181
|
-
headers = {}
|
182
|
-
if self.client_id and self.client_secret:
|
183
|
-
auth_header = base64.b64encode(f"{self.client_id}:{self.client_secret}".encode('utf-8')).decode('utf-8')
|
184
|
-
headers["Authorization"] = f"Basic {auth_header}"
|
185
|
-
|
186
|
-
try:
|
187
|
-
response = requests.post(self.token_url, data=data, headers=headers)
|
188
|
-
response.raise_for_status()
|
189
|
-
|
190
|
-
token_data = response.json()
|
191
|
-
self._access_token = token_data.get("access_token")
|
192
|
-
expires_in = token_data.get("expires_in", 3600) # 默认1小时
|
193
|
-
|
194
|
-
# 如果有新的刷新令牌,更新它
|
195
|
-
if "refresh_token" in token_data:
|
196
|
-
self.refresh_token = token_data["refresh_token"]
|
197
|
-
|
198
|
-
if not self._access_token:
|
199
|
-
raise ValueError("响应中缺少access_token字段")
|
200
|
-
|
201
|
-
# 计算过期时间
|
202
|
-
self._token_expires_at = time.time() + expires_in
|
203
|
-
logger.info(f"成功刷新JWT令牌,有效期{expires_in}秒")
|
204
|
-
|
205
|
-
except Exception as e:
|
206
|
-
logger.error(f"刷新JWT令牌失败: {str(e)}")
|
207
|
-
raise
|
208
|
-
|
209
|
-
|
210
|
-
class WechatMiniAppAuthProvider(AuthProvider):
|
211
|
-
"""微信小程序认证提供者
|
212
|
-
|
213
|
-
使用code获取微信小程序的session_key和openid,适用于微信小程序API调用。
|
214
|
-
"""
|
215
|
-
|
216
|
-
def __init__(self, appid: str, secret: str, code: str = None):
|
217
|
-
"""初始化微信小程序认证
|
218
|
-
|
219
|
-
Args:
|
220
|
-
appid: 小程序的AppID
|
221
|
-
secret: 小程序的AppSecret
|
222
|
-
code: 登录时获取的临时code
|
223
|
-
"""
|
224
|
-
self.appid = appid
|
225
|
-
self.secret = secret
|
226
|
-
self.code = code
|
227
|
-
|
228
|
-
# 微信登录凭据
|
229
|
-
self.session_key = None
|
230
|
-
self.openid = None
|
231
|
-
|
232
|
-
def apply_auth(self, request_kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
233
|
-
"""应用微信小程序认证
|
234
|
-
|
235
|
-
Args:
|
236
|
-
request_kwargs: 请求参数
|
237
|
-
|
238
|
-
Returns:
|
239
|
-
更新后的请求参数
|
240
|
-
"""
|
241
|
-
# 如果未获取过登录凭据且有code,则先获取凭据
|
242
|
-
if (not self.session_key or not self.openid) and self.code:
|
243
|
-
self._login_with_code()
|
244
|
-
|
245
|
-
# 确保已获取登录凭据
|
246
|
-
if not self.session_key or not self.openid:
|
247
|
-
raise ValueError("未获取到微信登录凭据,无法完成认证")
|
248
|
-
|
249
|
-
# 确保headers存在
|
250
|
-
if "headers" not in request_kwargs:
|
251
|
-
request_kwargs["headers"] = {}
|
252
|
-
|
253
|
-
# 添加认证头
|
254
|
-
request_kwargs["headers"]["X-WX-OPENID"] = self.openid
|
255
|
-
request_kwargs["headers"]["X-WX-SESSION-KEY"] = self.session_key
|
256
|
-
|
257
|
-
return request_kwargs
|
258
|
-
|
259
|
-
def _login_with_code(self) -> None:
|
260
|
-
"""使用code获取登录凭据"""
|
261
|
-
url = f"https://api.weixin.qq.com/sns/jscode2session"
|
262
|
-
params = {
|
263
|
-
"appid": self.appid,
|
264
|
-
"secret": self.secret,
|
265
|
-
"js_code": self.code,
|
266
|
-
"grant_type": "authorization_code"
|
267
|
-
}
|
268
|
-
|
269
|
-
try:
|
270
|
-
response = requests.get(url, params=params)
|
271
|
-
response.raise_for_status()
|
272
|
-
|
273
|
-
data = response.json()
|
274
|
-
if "errcode" in data and data["errcode"] != 0:
|
275
|
-
raise ValueError(f"微信登录失败: {data.get('errmsg', '未知错误')}")
|
276
|
-
|
277
|
-
self.session_key = data.get("session_key")
|
278
|
-
self.openid = data.get("openid")
|
279
|
-
|
280
|
-
if not self.session_key or not self.openid:
|
281
|
-
raise ValueError("微信登录响应缺少session_key或openid")
|
282
|
-
|
283
|
-
logger.info(f"成功获取微信小程序登录凭据,openid: {self.openid}")
|
284
|
-
|
285
|
-
# 清除code,因为它是一次性的
|
286
|
-
self.code = None
|
287
|
-
|
288
|
-
except Exception as e:
|
289
|
-
logger.error(f"获取微信小程序登录凭据失败: {str(e)}")
|
290
|
-
raise
|
291
|
-
|
292
|
-
|
293
|
-
class MultiStepAuthProvider(CustomAuthProvider):
|
294
|
-
"""多步骤认证提供者
|
295
|
-
|
296
|
-
实现一个需要多个步骤的认证流程,包括获取临时令牌、换取访问令牌等。
|
297
|
-
"""
|
298
|
-
|
299
|
-
def __init__(self):
|
300
|
-
"""初始化多步骤认证提供者"""
|
301
|
-
self._cached_token = None
|
302
|
-
self._token_expires_at = 0
|
303
|
-
|
304
|
-
def apply_auth(self, request_kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
305
|
-
"""应用多步骤认证
|
306
|
-
|
307
|
-
Args:
|
308
|
-
request_kwargs: 请求参数
|
309
|
-
|
310
|
-
Returns:
|
311
|
-
更新后的请求参数
|
312
|
-
"""
|
313
|
-
# 检查缓存令牌是否有效
|
314
|
-
current_time = time.time()
|
315
|
-
cached_token = self._cached_token
|
316
|
-
|
317
|
-
if cached_token and current_time < self._token_expires_at:
|
318
|
-
# 令牌有效,直接使用
|
319
|
-
if "headers" not in request_kwargs:
|
320
|
-
request_kwargs["headers"] = {}
|
321
|
-
request_kwargs["headers"]["Authorization"] = f"Bearer {cached_token}"
|
322
|
-
return request_kwargs
|
323
|
-
|
324
|
-
# 令牌无效,开始多步骤认证流程
|
325
|
-
try:
|
326
|
-
# 步骤1: 获取临时令牌
|
327
|
-
temp_token_response = requests.post(
|
328
|
-
"https://api.example.com/auth/temp-token",
|
329
|
-
json={
|
330
|
-
"app_id": "your_app_id",
|
331
|
-
"app_secret": "your_app_secret",
|
332
|
-
"device_id": str(uuid.uuid4()) # 示例设备ID
|
333
|
-
}
|
334
|
-
)
|
335
|
-
temp_token_response.raise_for_status()
|
336
|
-
temp_token_data = temp_token_response.json()
|
337
|
-
temp_token = temp_token_data.get("temp_token")
|
338
|
-
|
339
|
-
if not temp_token:
|
340
|
-
raise ValueError("无法获取临时令牌")
|
341
|
-
|
342
|
-
# 步骤2: 用临时令牌换取访问令牌
|
343
|
-
access_token_response = requests.post(
|
344
|
-
"https://api.example.com/auth/access-token",
|
345
|
-
json={
|
346
|
-
"temp_token": temp_token,
|
347
|
-
"client_id": "your_client_id",
|
348
|
-
"signature": hashlib.sha256(f"your_client_id:{temp_token}:your_client_secret".encode()).hexdigest()
|
349
|
-
}
|
350
|
-
)
|
351
|
-
access_token_response.raise_for_status()
|
352
|
-
token_data = access_token_response.json()
|
353
|
-
|
354
|
-
# 提取数据
|
355
|
-
access_token = token_data.get("access_token")
|
356
|
-
expires_in = token_data.get("expires_in", 3600)
|
357
|
-
|
358
|
-
if not access_token:
|
359
|
-
raise ValueError("无法获取访问令牌")
|
360
|
-
|
361
|
-
# 缓存令牌
|
362
|
-
self._cached_token = access_token
|
363
|
-
self._token_expires_at = time.time() + expires_in - 60 # 提前60秒刷新
|
364
|
-
|
365
|
-
# 步骤3: 应用访问令牌到请求
|
366
|
-
if "headers" not in request_kwargs:
|
367
|
-
request_kwargs["headers"] = {}
|
368
|
-
request_kwargs["headers"]["Authorization"] = f"Bearer {access_token}"
|
369
|
-
|
370
|
-
return request_kwargs
|
371
|
-
|
372
|
-
except Exception as e:
|
373
|
-
logger.error(f"多步骤认证失败: {str(e)}")
|
374
|
-
# 出错时,尝试使用之前的令牌(即使已过期)
|
375
|
-
if cached_token:
|
376
|
-
if "headers" not in request_kwargs:
|
377
|
-
request_kwargs["headers"] = {}
|
378
|
-
request_kwargs["headers"]["Authorization"] = f"Bearer {cached_token}"
|
379
|
-
logger.warning("使用过期的缓存令牌")
|
380
|
-
|
381
|
-
return request_kwargs
|
382
|
-
|
383
|
-
|
384
|
-
def register_complex_auth_example():
|
385
|
-
"""注册复杂授权示例"""
|
386
|
-
# 注册HMAC签名认证提供者
|
387
|
-
register_auth_provider(
|
388
|
-
"hmac_aws_auth",
|
389
|
-
HmacAuthProvider,
|
390
|
-
access_key="AKIAIOSFODNN7EXAMPLE",
|
391
|
-
secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
392
|
-
region="us-west-2",
|
393
|
-
service="s3",
|
394
|
-
algorithm="sha256"
|
395
|
-
)
|
396
|
-
|
397
|
-
# 注册JWT刷新令牌认证提供者
|
398
|
-
register_auth_provider(
|
399
|
-
"jwt_refresh_auth",
|
400
|
-
JwtRefreshAuthProvider,
|
401
|
-
token_url="https://api.example.com/oauth/token",
|
402
|
-
refresh_token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
403
|
-
client_id="client_id_example",
|
404
|
-
client_secret="client_secret_example"
|
405
|
-
)
|
406
|
-
|
407
|
-
# 注册微信小程序认证提供者
|
408
|
-
register_auth_provider(
|
409
|
-
"wechat_miniapp_auth",
|
410
|
-
WechatMiniAppAuthProvider,
|
411
|
-
appid="wx1234567890abcdef",
|
412
|
-
secret="abcdef1234567890abcdef1234567890",
|
413
|
-
code="023Bj3ll2EE81B0TrTnl2kQSll2Bj3lY"
|
414
|
-
)
|
415
|
-
|
416
|
-
# 注册多步骤认证提供者
|
417
|
-
register_auth_provider(
|
418
|
-
"multi_step_auth",
|
419
|
-
MultiStepAuthProvider
|
420
|
-
)
|
421
|
-
|
422
|
-
logger.info("已注册复杂授权示例")
|
423
|
-
|
424
|
-
# 自动注册示例(取消注释以启用)
|
425
|
-
# register_complex_auth_example()
|
@@ -1,64 +0,0 @@
|
|
1
|
-
@name: CSRF登录授权测试
|
2
|
-
@description: 演示CSRF登录授权提供者的使用
|
3
|
-
@tags: [HTTP, API, 授权, CSRF, 示例]
|
4
|
-
@author: Felix
|
5
|
-
@date: 2024-05-20
|
6
|
-
|
7
|
-
# CSRF登录授权测试示例
|
8
|
-
# 演示CSRF登录授权提供者的使用
|
9
|
-
|
10
|
-
# 测试CSRF登录认证
|
11
|
-
[HTTP请求],客户端:'csrf_auth',配置:'''
|
12
|
-
method: GET
|
13
|
-
url: /headers
|
14
|
-
captures:
|
15
|
-
csrf_token: ["jsonpath", "$.headers.X-Csrf-Token"]
|
16
|
-
asserts:
|
17
|
-
- ["status", "eq", 200]
|
18
|
-
- ["jsonpath", "$.headers.X-Csrf-Token", "exists"]
|
19
|
-
'''
|
20
|
-
|
21
|
-
[打印],内容:'CSRF令牌: ${csrf_token}'
|
22
|
-
|
23
|
-
# 测试CSRF令牌过期后自动重新登录
|
24
|
-
[HTTP请求],客户端:'csrf_auth',配置:'''
|
25
|
-
method: GET
|
26
|
-
url: /headers
|
27
|
-
headers:
|
28
|
-
X-Csrf-Token: expired_token # 模拟过期令牌
|
29
|
-
captures:
|
30
|
-
csrf_token: ["jsonpath", "$.headers.X-Csrf-Token"]
|
31
|
-
asserts:
|
32
|
-
- ["status", "eq", 200]
|
33
|
-
- ["jsonpath", "$.headers.X-Csrf-Token", "exists"]
|
34
|
-
'''
|
35
|
-
|
36
|
-
[打印],内容:'新的CSRF令牌: ${csrf_token}'
|
37
|
-
|
38
|
-
# 测试需要CSRF令牌的POST请求
|
39
|
-
[HTTP请求],客户端:'csrf_auth',配置:'''
|
40
|
-
method: POST
|
41
|
-
url: /anything
|
42
|
-
json:
|
43
|
-
test: "data"
|
44
|
-
captures:
|
45
|
-
headers: ["jsonpath", "$.headers"]
|
46
|
-
json: ["jsonpath", "$.json"]
|
47
|
-
asserts:
|
48
|
-
- ["status", "eq", 200]
|
49
|
-
- ["jsonpath", "$.headers.X-Csrf-Token", "exists"]
|
50
|
-
'''
|
51
|
-
|
52
|
-
[打印],内容:'请求头: ${headers}'
|
53
|
-
[打印],内容:'请求体: ${json}'
|
54
|
-
|
55
|
-
# 测试禁用CSRF认证
|
56
|
-
[HTTP请求],客户端:'csrf_auth',配置:'''
|
57
|
-
method: GET
|
58
|
-
url: /headers
|
59
|
-
disable_auth: true
|
60
|
-
asserts:
|
61
|
-
- ["status", "eq", 200]
|
62
|
-
'''
|
63
|
-
|
64
|
-
[打印],内容:'CSRF登录授权测试完成'
|
@@ -1,76 +0,0 @@
|
|
1
|
-
@name: 自定义授权测试
|
2
|
-
@description: 演示自定义授权提供者的使用
|
3
|
-
@tags: [HTTP, API, 授权, 示例]
|
4
|
-
@author: Felix
|
5
|
-
@date: 2024-05-01
|
6
|
-
|
7
|
-
# 自定义授权测试示例
|
8
|
-
# 演示自定义授权提供者的使用
|
9
|
-
|
10
|
-
# 测试HMAC签名授权
|
11
|
-
[HTTP请求],客户端:'hmac_auth',配置:'''
|
12
|
-
method: GET
|
13
|
-
url: /headers
|
14
|
-
captures:
|
15
|
-
auth_header: ["jsonpath", "$.headers.Authorization"]
|
16
|
-
asserts:
|
17
|
-
- ["status", "eq", 200]
|
18
|
-
- ["jsonpath", "$.headers.Authorization", "exists"]
|
19
|
-
- ["jsonpath", "$.headers.X-Amz-Date", "exists"]
|
20
|
-
'''
|
21
|
-
|
22
|
-
[打印],内容:'HMAC授权头: ${auth_header}'
|
23
|
-
|
24
|
-
# 测试JWT刷新令牌授权
|
25
|
-
[HTTP请求],客户端:'jwt_auth',配置:'''
|
26
|
-
method: GET
|
27
|
-
url: /headers
|
28
|
-
captures:
|
29
|
-
auth_header: ["jsonpath", "$.headers.Authorization"]
|
30
|
-
asserts:
|
31
|
-
- ["status", "eq", 200]
|
32
|
-
- ["jsonpath", "$.headers.Authorization", "startswith", "Bearer "]
|
33
|
-
'''
|
34
|
-
|
35
|
-
[打印],内容:'JWT授权头: ${auth_header}'
|
36
|
-
|
37
|
-
# 测试微信小程序授权
|
38
|
-
[HTTP请求],客户端:'wechat_auth',配置:'''
|
39
|
-
method: GET
|
40
|
-
url: /headers
|
41
|
-
captures:
|
42
|
-
openid: ["jsonpath", "$.headers.X-Wx-Openid"]
|
43
|
-
session_key: ["jsonpath", "$.headers.X-Wx-Session-Key"]
|
44
|
-
asserts:
|
45
|
-
- ["status", "eq", 200]
|
46
|
-
- ["jsonpath", "$.headers.X-Wx-Openid", "exists"]
|
47
|
-
- ["jsonpath", "$.headers.X-Wx-Session-Key", "exists"]
|
48
|
-
'''
|
49
|
-
|
50
|
-
[打印],内容:'微信OpenID: ${openid}'
|
51
|
-
[打印],内容:'微信Session Key: ${session_key}'
|
52
|
-
|
53
|
-
# 测试多步骤授权
|
54
|
-
[HTTP请求],客户端:'multi_step_auth',配置:'''
|
55
|
-
method: GET
|
56
|
-
url: /headers
|
57
|
-
captures:
|
58
|
-
auth_header: ["jsonpath", "$.headers.Authorization"]
|
59
|
-
asserts:
|
60
|
-
- ["status", "eq", 200]
|
61
|
-
- ["jsonpath", "$.headers.Authorization", "startswith", "Bearer "]
|
62
|
-
'''
|
63
|
-
|
64
|
-
[打印],内容:'多步骤授权头: ${auth_header}'
|
65
|
-
|
66
|
-
# 测试禁用授权
|
67
|
-
[HTTP请求],客户端:'multi_step_auth',配置:'''
|
68
|
-
method: GET
|
69
|
-
url: /headers
|
70
|
-
disable_auth: true
|
71
|
-
asserts:
|
72
|
-
- ["status", "eq", 200]
|
73
|
-
- ["jsonpath", "$.headers.Authorization", "not_exists"]
|
74
|
-
'''
|
75
|
-
|
76
|
-
[打印],内容:'自定义授权测试完成'
|
@@ -1,48 +0,0 @@
|
|
1
|
-
http_clients:
|
2
|
-
hmac_auth:
|
3
|
-
base_url: https://httpbin.org
|
4
|
-
headers:
|
5
|
-
User-Agent: pytest-dsl-client/1.0
|
6
|
-
Accept: application/json
|
7
|
-
timeout: 30
|
8
|
-
verify_ssl: true
|
9
|
-
session: true
|
10
|
-
auth:
|
11
|
-
type: custom
|
12
|
-
provider_name: hmac_aws_auth
|
13
|
-
|
14
|
-
jwt_auth:
|
15
|
-
base_url: https://httpbin.org
|
16
|
-
headers:
|
17
|
-
User-Agent: pytest-dsl-client/1.0
|
18
|
-
Accept: application/json
|
19
|
-
timeout: 30
|
20
|
-
verify_ssl: true
|
21
|
-
session: true
|
22
|
-
auth:
|
23
|
-
type: custom
|
24
|
-
provider_name: jwt_refresh_auth
|
25
|
-
|
26
|
-
wechat_auth:
|
27
|
-
base_url: https://httpbin.org
|
28
|
-
headers:
|
29
|
-
User-Agent: pytest-dsl-client/1.0
|
30
|
-
Accept: application/json
|
31
|
-
timeout: 30
|
32
|
-
verify_ssl: true
|
33
|
-
session: true
|
34
|
-
auth:
|
35
|
-
type: custom
|
36
|
-
provider_name: wechat_miniapp_auth
|
37
|
-
|
38
|
-
multi_step_auth:
|
39
|
-
base_url: https://httpbin.org
|
40
|
-
headers:
|
41
|
-
User-Agent: pytest-dsl-client/1.0
|
42
|
-
Accept: application/json
|
43
|
-
timeout: 30
|
44
|
-
verify_ssl: true
|
45
|
-
session: true
|
46
|
-
auth:
|
47
|
-
type: custom
|
48
|
-
provider_name: multi_step_auth
|
@@ -1,70 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
示例: 如何在您的项目中创建自定义关键字
|
3
|
-
|
4
|
-
将该文件放在您项目的 keywords 目录下,
|
5
|
-
例如: my_project/keywords/my_keywords.py
|
6
|
-
|
7
|
-
pytest-dsl 会自动导入这些关键字。
|
8
|
-
"""
|
9
|
-
|
10
|
-
from pytest_dsl.core.keyword_manager import keyword_manager
|
11
|
-
|
12
|
-
# 示例1: 简单关键字
|
13
|
-
@keyword_manager.register('打印消息', [
|
14
|
-
{'name': '消息', 'mapping': 'message', 'description': '要打印的消息内容'},
|
15
|
-
])
|
16
|
-
def print_message(**kwargs):
|
17
|
-
"""打印一条消息到控制台
|
18
|
-
|
19
|
-
Args:
|
20
|
-
message: 要打印的消息
|
21
|
-
context: 测试上下文 (自动传入)
|
22
|
-
"""
|
23
|
-
message = kwargs.get('message', '')
|
24
|
-
print(f"自定义关键字消息: {message}")
|
25
|
-
return True
|
26
|
-
|
27
|
-
# 示例2: 带返回值的关键字
|
28
|
-
@keyword_manager.register('生成随机数', [
|
29
|
-
{'name': '最小值', 'mapping': 'min_value', 'description': '随机数范围最小值'},
|
30
|
-
{'name': '最大值', 'mapping': 'max_value', 'description': '随机数范围最大值'},
|
31
|
-
])
|
32
|
-
def generate_random(**kwargs):
|
33
|
-
"""生成指定范围内的随机整数
|
34
|
-
|
35
|
-
Args:
|
36
|
-
min_value: 最小值
|
37
|
-
max_value: 最大值
|
38
|
-
context: 测试上下文 (自动传入)
|
39
|
-
|
40
|
-
Returns:
|
41
|
-
随机整数
|
42
|
-
"""
|
43
|
-
import random
|
44
|
-
min_value = int(kwargs.get('min_value', 1))
|
45
|
-
max_value = int(kwargs.get('max_value', 100))
|
46
|
-
|
47
|
-
result = random.randint(min_value, max_value)
|
48
|
-
return result
|
49
|
-
|
50
|
-
# 示例3: 操作上下文的关键字
|
51
|
-
@keyword_manager.register('保存到上下文', [
|
52
|
-
{'name': '键名', 'mapping': 'key', 'description': '保存在上下文中的键名'},
|
53
|
-
{'name': '值', 'mapping': 'value', 'description': '要保存的值'},
|
54
|
-
])
|
55
|
-
def save_to_context(**kwargs):
|
56
|
-
"""将值保存到测试上下文中
|
57
|
-
|
58
|
-
Args:
|
59
|
-
key: 键名
|
60
|
-
value: 要保存的值
|
61
|
-
context: 测试上下文 (自动传入)
|
62
|
-
"""
|
63
|
-
key = kwargs.get('key')
|
64
|
-
value = kwargs.get('value')
|
65
|
-
context = kwargs.get('context')
|
66
|
-
|
67
|
-
if key and context:
|
68
|
-
context.set(key, value)
|
69
|
-
return True
|
70
|
-
return False
|