iflow-mcp_galaxyxieyu_api-auto-test 0.1.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.
atf/__init__.py ADDED
@@ -0,0 +1,48 @@
1
+ """API Test Framework
2
+ 自动化测试框架主包
3
+
4
+ 使用延迟导入,避免在导入 atf 包时加载所有依赖。
5
+ """
6
+
7
+ from importlib import import_module
8
+
9
+ __version__ = '1.0.0'
10
+ __package_name__ = 'atf'
11
+
12
+ __all__ = [
13
+ "Globals",
14
+ "ConfigManager",
15
+ "LogManager",
16
+ "RequestHandler",
17
+ "VariableResolver",
18
+ "AssertHandler",
19
+ "LoginHandler",
20
+ "CaseGenerator",
21
+ "run_tests",
22
+ ]
23
+
24
+ _LAZY_IMPORTS = {
25
+ "Globals": "atf.core.globals",
26
+ "ConfigManager": "atf.core.config_manager",
27
+ "LogManager": "atf.core.log_manager",
28
+ "RequestHandler": "atf.core.request_handler",
29
+ "VariableResolver": "atf.core.variable_resolver",
30
+ "AssertHandler": "atf.core.assert_handler",
31
+ "LoginHandler": "atf.core.login_handler",
32
+ "CaseGenerator": "atf.case_generator",
33
+ "run_tests": "atf.runner",
34
+ }
35
+
36
+
37
+ def __getattr__(name):
38
+ module_path = _LAZY_IMPORTS.get(name)
39
+ if not module_path:
40
+ raise AttributeError(f"module 'atf' has no attribute '{name}'")
41
+ module = import_module(module_path)
42
+ value = getattr(module, name)
43
+ globals()[name] = value
44
+ return value
45
+
46
+
47
+ def __dir__():
48
+ return sorted(list(globals().keys()) + list(_LAZY_IMPORTS.keys()))
atf/assets/__init__.py ADDED
File without changes
atf/assets/report.css ADDED
@@ -0,0 +1,243 @@
1
+ /* API Auto Test - 现代化报告主题 */
2
+
3
+ :root {
4
+ --primary: #4f46e5;
5
+ --success: #10b981;
6
+ --danger: #ef4444;
7
+ --warning: #f59e0b;
8
+ --bg: #f8fafc;
9
+ --card: #ffffff;
10
+ --text: #1e293b;
11
+ --border: #e2e8f0;
12
+ --muted: #64748b;
13
+ }
14
+
15
+ body {
16
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
17
+ background: var(--bg);
18
+ color: var(--text);
19
+ line-height: 1.6;
20
+ margin: 0;
21
+ padding: 24px;
22
+ }
23
+
24
+ h1 {
25
+ color: var(--primary);
26
+ font-size: 26px;
27
+ font-weight: 700;
28
+ margin: 0 0 8px 0;
29
+ padding-bottom: 16px;
30
+ border-bottom: 3px solid var(--primary);
31
+ }
32
+
33
+ h2 {
34
+ font-size: 18px;
35
+ font-weight: 600;
36
+ margin: 24px 0 12px 0;
37
+ color: var(--text);
38
+ }
39
+
40
+ /* 卡片容器 */
41
+ #environment, .summary {
42
+ background: var(--card);
43
+ border-radius: 12px;
44
+ padding: 20px 24px;
45
+ margin: 20px 0;
46
+ box-shadow: 0 1px 3px rgba(0,0,0,0.08);
47
+ border: 1px solid var(--border);
48
+ }
49
+
50
+ /* 统计标签 */
51
+ .summary__data {
52
+ display: flex;
53
+ flex-wrap: wrap;
54
+ gap: 12px;
55
+ margin-top: 12px;
56
+ }
57
+
58
+ .summary p, p.passed, p.failed, p.error, p.skipped, p.rerun {
59
+ display: inline-flex;
60
+ align-items: center;
61
+ margin: 0;
62
+ padding: 8px 16px;
63
+ border-radius: 8px;
64
+ font-weight: 600;
65
+ font-size: 14px;
66
+ }
67
+
68
+ p.passed, .passed { background: #dcfce7; color: #166534; }
69
+ p.failed, .failed { background: #fee2e2; color: #991b1b; }
70
+ p.error, .error { background: #fef3c7; color: #92400e; }
71
+ p.skipped, .skipped { background: #e0e7ff; color: #3730a3; }
72
+ p.rerun { background: #f1f5f9; color: #475569; }
73
+
74
+ /* 表格 */
75
+ table {
76
+ width: 100%;
77
+ border-collapse: separate;
78
+ border-spacing: 0;
79
+ background: var(--card);
80
+ border-radius: 12px;
81
+ overflow: hidden;
82
+ box-shadow: 0 1px 3px rgba(0,0,0,0.08);
83
+ margin: 20px 0;
84
+ }
85
+
86
+ thead tr {
87
+ background: linear-gradient(135deg, var(--primary) 0%, #6366f1 100%);
88
+ }
89
+
90
+ th {
91
+ color: white;
92
+ font-weight: 600;
93
+ padding: 14px 16px;
94
+ text-align: left;
95
+ font-size: 13px;
96
+ text-transform: uppercase;
97
+ letter-spacing: 0.5px;
98
+ border: none;
99
+ }
100
+
101
+ th.sortable {
102
+ cursor: pointer;
103
+ user-select: none;
104
+ }
105
+
106
+ th.sortable:hover {
107
+ background: rgba(255,255,255,0.1);
108
+ }
109
+
110
+ td {
111
+ padding: 14px 16px;
112
+ border-bottom: 1px solid var(--border);
113
+ font-size: 14px;
114
+ vertical-align: middle;
115
+ }
116
+
117
+ tbody tr:hover {
118
+ background: #f8fafc;
119
+ }
120
+
121
+ tbody tr:last-child td {
122
+ border-bottom: none;
123
+ }
124
+
125
+ /* 结果状态 */
126
+ td.col-result {
127
+ font-weight: 700;
128
+ text-transform: uppercase;
129
+ font-size: 11px;
130
+ letter-spacing: 0.5px;
131
+ }
132
+
133
+ td.col-result.passed { color: #059669; }
134
+ td.col-result.failed { color: #dc2626; }
135
+ td.col-result.error { color: #d97706; }
136
+ td.col-result.skipped { color: #6366f1; }
137
+
138
+ /* 展开详情 */
139
+ .extra, .log {
140
+ background: #f8fafc;
141
+ border-left: 4px solid var(--primary);
142
+ padding: 16px;
143
+ margin: 12px 0;
144
+ font-family: 'SF Mono', Monaco, Consolas, monospace;
145
+ font-size: 13px;
146
+ line-height: 1.5;
147
+ white-space: pre-wrap;
148
+ word-break: break-word;
149
+ overflow-x: auto;
150
+ border-radius: 0 8px 8px 0;
151
+ }
152
+
153
+ /* 环境信息 */
154
+ #environment td {
155
+ padding: 10px 16px;
156
+ }
157
+
158
+ #environment td:first-child {
159
+ font-weight: 600;
160
+ color: var(--muted);
161
+ width: 180px;
162
+ }
163
+
164
+ /* 链接 */
165
+ a {
166
+ color: var(--primary);
167
+ text-decoration: none;
168
+ font-weight: 500;
169
+ }
170
+
171
+ a:hover {
172
+ text-decoration: underline;
173
+ }
174
+
175
+ /* 筛选按钮 */
176
+ .filter {
177
+ display: flex;
178
+ gap: 8px;
179
+ margin: 20px 0;
180
+ flex-wrap: wrap;
181
+ }
182
+
183
+ .filter button, button {
184
+ background: var(--card);
185
+ border: 1px solid var(--border);
186
+ padding: 8px 16px;
187
+ border-radius: 8px;
188
+ cursor: pointer;
189
+ font-size: 13px;
190
+ font-weight: 500;
191
+ transition: all 0.15s ease;
192
+ color: var(--text);
193
+ }
194
+
195
+ .filter button:hover, button:hover {
196
+ background: var(--primary);
197
+ color: white;
198
+ border-color: var(--primary);
199
+ }
200
+
201
+ .filter button.active {
202
+ background: var(--primary);
203
+ color: white;
204
+ border-color: var(--primary);
205
+ }
206
+
207
+ /* 折叠/展开按钮 */
208
+ .expander {
209
+ cursor: pointer;
210
+ padding: 4px 8px;
211
+ border-radius: 4px;
212
+ font-size: 12px;
213
+ background: #f1f5f9;
214
+ border: none;
215
+ color: var(--muted);
216
+ }
217
+
218
+ .expander:hover {
219
+ background: var(--primary);
220
+ color: white;
221
+ }
222
+
223
+ /* 时间/持续时间 */
224
+ td.col-time, td.col-duration {
225
+ font-family: 'SF Mono', Monaco, monospace;
226
+ font-size: 13px;
227
+ color: var(--muted);
228
+ }
229
+
230
+ /* 响应式 */
231
+ @media (max-width: 768px) {
232
+ body { padding: 12px; }
233
+ th, td { padding: 10px 12px; font-size: 13px; }
234
+ h1 { font-size: 22px; }
235
+ .filter { flex-direction: column; }
236
+ }
237
+
238
+ /* 打印优化 */
239
+ @media print {
240
+ body { background: white; padding: 0; }
241
+ table, #environment, .summary { box-shadow: none; border: 1px solid #ddd; }
242
+ .filter, button { display: none; }
243
+ }
atf/auth.py ADDED
@@ -0,0 +1,99 @@
1
+ # @time: 2024-08-15
2
+ # @author: xiaoqq
3
+
4
+ import requests
5
+ from atf.core.globals import Globals
6
+ from atf.core.log_manager import log
7
+ import base64
8
+
9
+
10
+ class EncryptionManager:
11
+ """
12
+ 加密管理器,用于处理不同项目的加密逻辑。
13
+ PS: 需要根据被测项目登录接口的加密方式进行定义,此处只是一个demo。
14
+ """
15
+
16
+ def __init__(self):
17
+ self.public_keys = {
18
+ "merchant": {
19
+ "test": "test_public_key",
20
+ "online": "online_public_key"
21
+ },
22
+ "user": {
23
+ "test": "test_public_key",
24
+ "online": "test_public_key"
25
+ }
26
+ }
27
+
28
+ def encrypt(self, src_str, project_name, env="pre"):
29
+ """
30
+ 根据给定的项目名称使用RSA加密并转换为Base64格式。
31
+
32
+ :param src_str: 需要加密的原始字符串
33
+ :param project_name: 项目名称("merchant" 或 "user")
34
+ :param env: 环境名称("test", "pre", 等),默认为 "pre"
35
+ :return: 加密后的字符串
36
+ """
37
+ from Crypto.Cipher import PKCS1_v1_5 as Cipher_pksc1_v1_5
38
+ from Crypto.PublicKey import RSA
39
+
40
+ if env == "pre": # 预发与线上的签名一样
41
+ env = "online"
42
+ public_key = self.public_keys[project_name].get(env)
43
+ if not public_key:
44
+ log.error(f"No public key found for project '{project_name}' in environment '{env}'")
45
+ raise ValueError(f"No public key found for project '{project_name}' in environment '{env}'")
46
+
47
+ public_key_str = '-----BEGIN PUBLIC KEY-----\n' + public_key + '\n-----END PUBLIC KEY-----'
48
+ rsakey = RSA.importKey(public_key_str)
49
+ cipher = Cipher_pksc1_v1_5.new(rsakey)
50
+ cipher_text = base64.b64encode(cipher.encrypt(src_str.encode()))
51
+ return cipher_text.decode()
52
+
53
+
54
+ class Auth:
55
+ """
56
+ 项目登录
57
+ PS: 需要根据被测项目登录接口的加密方式进行定义,此处只是一个demo。
58
+ """
59
+
60
+ def __init__(self):
61
+ self.encryption_manager = EncryptionManager()
62
+
63
+ def login(self, project_name, login_config, env='pre', timeout=10):
64
+ url = login_config['url']
65
+ method = login_config['method']
66
+ headers = login_config.get('headers', {})
67
+ data = login_config['data'].copy()
68
+
69
+ # 加密用户名和密码
70
+ if project_name in ["merchant", "user"]:
71
+ data['account'] = self.encryption_manager.encrypt(data['account'], project_name, env)
72
+ data['password'] = self.encryption_manager.encrypt(data['password'], project_name, env)
73
+
74
+ # 执行登录请求
75
+ response = requests.request(method, url, json=data, headers=headers, timeout=timeout)
76
+
77
+ # 提取token
78
+ token = response.json().get('data').get('token')
79
+ return token
80
+
81
+ if __name__ == '__main__':
82
+ # pass
83
+ project_name = "user"
84
+ login_config = {
85
+ "url": "https://xxxxxx.com/login",
86
+ "method": "POST",
87
+ "headers": {
88
+ "Content-Type": "application/json"
89
+ },
90
+ "data": {
91
+ "account": "13500000000",
92
+ "password": "xxxxxx",
93
+ "authorizationClientType": 2,
94
+ "loginType": 3,
95
+ "loginVersion": 2
96
+ }
97
+ }
98
+
99
+ print(Auth().login(project_name, login_config))