WebsocketTest 1.0.1__py3-none-any.whl → 1.0.3__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.
- WebsocketTest/__init__.py +21 -0
- WebsocketTest/cli.py +56 -0
- WebsocketTest/conftest.py +63 -0
- WebsocketTest/run_tests.py +147 -0
- {WebsocketTest-1.0.1.dist-info → WebsocketTest-1.0.3.dist-info}/METADATA +1 -1
- WebsocketTest-1.0.3.dist-info/RECORD +21 -0
- WebsocketTest-1.0.3.dist-info/entry_points.txt +2 -0
- WebsocketTest-1.0.3.dist-info/top_level.txt +1 -0
- WebsocketTest-1.0.1.dist-info/RECORD +0 -17
- WebsocketTest-1.0.1.dist-info/entry_points.txt +0 -2
- WebsocketTest-1.0.1.dist-info/top_level.txt +0 -3
- {caseScript → WebsocketTest/caseScript}/Aqua.py +0 -0
- {caseScript → WebsocketTest/caseScript}/Gateway.py +0 -0
- {caseScript → WebsocketTest/caseScript}/__init__.py +0 -0
- {common → WebsocketTest/common}/Assertion.py +0 -0
- {common → WebsocketTest/common}/WSBaseApi.py +0 -0
- {common → WebsocketTest/common}/WebSocketApi.py +0 -0
- {common → WebsocketTest/common}/__init__.py +0 -0
- {common → WebsocketTest/common}/assertUtils.py +0 -0
- {common → WebsocketTest/common}/logger.py +0 -0
- {common → WebsocketTest/common}/utils.py +0 -0
- {testcase → WebsocketTest/testcase}/__init__.py +0 -0
- {testcase → WebsocketTest/testcase}/test_all.py +0 -0
- {WebsocketTest-1.0.1.dist-info → WebsocketTest-1.0.3.dist-info}/WHEEL +0 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
__version__ = "1.1.0"
|
2
|
+
__description__ = "One-stop solution for AUTO testing."
|
3
|
+
|
4
|
+
# import firstly for monkey patch if needed
|
5
|
+
# from pastor.ext.locust import main_locusts
|
6
|
+
# from pastor.parser import parse_parameters as Parameters
|
7
|
+
# from pastor.runner import Pastor
|
8
|
+
# from pastor.testcase import Config, Step, RunRequest, RunTestCase, RunLocation
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
"__version__",
|
12
|
+
"__description__"
|
13
|
+
# ,
|
14
|
+
# "Pastor",
|
15
|
+
# "Config",
|
16
|
+
# "Step",
|
17
|
+
# "RunLocation",
|
18
|
+
# "RunRequest",
|
19
|
+
# "RunTestCase",
|
20
|
+
# "Parameters",
|
21
|
+
]
|
WebsocketTest/cli.py
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
import argparse
|
2
|
+
import enum
|
3
|
+
import sys
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
def main_run(pytest_args: list) -> enum.IntEnum:
|
7
|
+
"""Run pytest with given arguments
|
8
|
+
|
9
|
+
Args:
|
10
|
+
pytest_args: List of arguments to pass to pytest
|
11
|
+
|
12
|
+
Returns:
|
13
|
+
pytest.ExitCode enum value
|
14
|
+
"""
|
15
|
+
print("Running pytest with args:", pytest_args)
|
16
|
+
return pytest.main(pytest_args)
|
17
|
+
|
18
|
+
def main():
|
19
|
+
"""API test: parse command line options and run commands."""
|
20
|
+
# 主解析器
|
21
|
+
parser = argparse.ArgumentParser(description="Run tests with Allure reporting")
|
22
|
+
|
23
|
+
# 添加必选参数
|
24
|
+
parser.add_argument("--env", required=True, help="Test environment")
|
25
|
+
parser.add_argument("--app", required=True, help="Application ID")
|
26
|
+
parser.add_argument("--service", required=True, help="Service name")
|
27
|
+
parser.add_argument("--project", required=True, help="Project name")
|
28
|
+
|
29
|
+
# 解析参数,分离已知参数和要传递给pytest的参数
|
30
|
+
args, pytest_args = parser.parse_known_args()
|
31
|
+
|
32
|
+
# 打印调试信息
|
33
|
+
print("Tool arguments:")
|
34
|
+
print(vars(args))
|
35
|
+
print("Pytest arguments:", pytest_args)
|
36
|
+
|
37
|
+
# 将工具参数转换为pytest可用的格式
|
38
|
+
pytest_args.extend([
|
39
|
+
f"--env={args.env}",
|
40
|
+
f"--app={args.app}",
|
41
|
+
f"--service={args.service}",
|
42
|
+
f"--project={args.project}"
|
43
|
+
])
|
44
|
+
|
45
|
+
# 运行测试
|
46
|
+
return_code = main_run(pytest_args)
|
47
|
+
|
48
|
+
# 返回退出码
|
49
|
+
sys.exit(return_code)
|
50
|
+
def main_run_alias():
|
51
|
+
""" command alias
|
52
|
+
prun = pastor run
|
53
|
+
"""
|
54
|
+
main()
|
55
|
+
if __name__ == "__main__":
|
56
|
+
main()
|
@@ -0,0 +1,63 @@
|
|
1
|
+
import pytest
|
2
|
+
import yaml
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
# 设置环境信息的 fixture
|
6
|
+
@pytest.fixture(scope="session")
|
7
|
+
def setup_env(request):
|
8
|
+
# 获取命令行参数
|
9
|
+
service = request.config.getoption("service")
|
10
|
+
project = request.config.getoption("project")
|
11
|
+
env = request.config.getoption("env")
|
12
|
+
app_id = request.config.getoption("app")
|
13
|
+
# 获取当前工作目录,并解析为绝对路径
|
14
|
+
current_working_directory = Path.cwd().resolve()
|
15
|
+
config_dir = current_working_directory.joinpath('config', project)
|
16
|
+
|
17
|
+
# Load base configuration
|
18
|
+
with open(config_dir.joinpath(f'{env}.yml')) as base_file:
|
19
|
+
config = yaml.safe_load(base_file)
|
20
|
+
|
21
|
+
# Load environment-specific configuration and merge into the base config
|
22
|
+
with open(config_dir.joinpath(service, f'{env}.yml')) as env_file:
|
23
|
+
env_config = yaml.safe_load(env_file)
|
24
|
+
# 合并配置
|
25
|
+
environments = config.get('environments', {})
|
26
|
+
environments.update(env_config.get('environments', {}))
|
27
|
+
# 添加运行时参数
|
28
|
+
environments.update({'appId': app_id,
|
29
|
+
'env_name': env,
|
30
|
+
'service': service,
|
31
|
+
'project': project})
|
32
|
+
return environments
|
33
|
+
|
34
|
+
# 添加命令行选项
|
35
|
+
def pytest_addoption(parser):
|
36
|
+
parser.addoption(
|
37
|
+
"--env",
|
38
|
+
action="store",
|
39
|
+
default="uat",
|
40
|
+
help="Environment to use (default: uat)"
|
41
|
+
)
|
42
|
+
parser.addoption(
|
43
|
+
"--app",
|
44
|
+
action="store",
|
45
|
+
default="0f0826ab",
|
46
|
+
help="App to use (default: 0f0826ab)"
|
47
|
+
)
|
48
|
+
parser.addoption(
|
49
|
+
"--service",
|
50
|
+
action="store",
|
51
|
+
default="0f0826ab",
|
52
|
+
help="Service to use (default: aqua)"
|
53
|
+
)
|
54
|
+
parser.addoption(
|
55
|
+
"--project",
|
56
|
+
action="store",
|
57
|
+
default="vwa",
|
58
|
+
help="Project name is required"
|
59
|
+
)
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
@@ -0,0 +1,147 @@
|
|
1
|
+
import subprocess,sys
|
2
|
+
import argparse
|
3
|
+
import shutil
|
4
|
+
from common.logger import *
|
5
|
+
import time
|
6
|
+
import webbrowser
|
7
|
+
from urllib.request import urlopen
|
8
|
+
from urllib.error import URLError
|
9
|
+
|
10
|
+
logger = logging.getLogger('run_tests')
|
11
|
+
|
12
|
+
class AllureManager:
|
13
|
+
def __init__(self, port: int = 8883):
|
14
|
+
self.port = port
|
15
|
+
self.allure_path = shutil.which("allure")
|
16
|
+
if not self.allure_path:
|
17
|
+
raise RuntimeError("Allure command line tool not found in PATH")
|
18
|
+
|
19
|
+
def is_server_running(self) -> bool:
|
20
|
+
"""检查Allure服务是否已在运行"""
|
21
|
+
try:
|
22
|
+
with urlopen(f"http://localhost:{self.port}") as response:
|
23
|
+
return response.status == 200
|
24
|
+
except URLError:
|
25
|
+
return False
|
26
|
+
|
27
|
+
def start_server(self, report_dir: str) -> bool:
|
28
|
+
"""启动Allure服务"""
|
29
|
+
try:
|
30
|
+
cmd = [self.allure_path, "open", report_dir, "-p", str(self.port)]
|
31
|
+
logger.info(f"start_server Executing: {' '.join(cmd)}")
|
32
|
+
subprocess.Popen(
|
33
|
+
cmd,
|
34
|
+
stdout=subprocess.DEVNULL,
|
35
|
+
stderr=subprocess.DEVNULL,
|
36
|
+
start_new_session=True
|
37
|
+
)
|
38
|
+
|
39
|
+
time.sleep(3) # 等待服务启动
|
40
|
+
return True
|
41
|
+
except Exception as e:
|
42
|
+
logger.error(f"Failed to start Allure server: {e}")
|
43
|
+
return False
|
44
|
+
|
45
|
+
def refresh_browser_tab(self) -> bool:
|
46
|
+
"""尝试刷新已打开的Allure标签页"""
|
47
|
+
url = f"http://localhost:{self.port}"
|
48
|
+
|
49
|
+
# 方法1: 使用JavaScript刷新(需要浏览器支持)
|
50
|
+
try:
|
51
|
+
webbrowser.open_new_tab("javascript:location.reload(true);")
|
52
|
+
return True
|
53
|
+
except Exception as e:
|
54
|
+
logger.warning(f"JavaScript refresh failed: {e}")
|
55
|
+
|
56
|
+
# 方法2: 使用webbrowser直接打开(会聚焦到已有标签页)
|
57
|
+
try:
|
58
|
+
browser = webbrowser.get() # 获取系统默认浏览器控制器
|
59
|
+
if hasattr(browser, 'open_new_tab'):
|
60
|
+
browser.open_new_tab(url) # 大多数浏览器会聚焦到已有标签页
|
61
|
+
return True
|
62
|
+
except Exception as e:
|
63
|
+
logger.error(f"Browser refresh failed: {e}")
|
64
|
+
|
65
|
+
return False
|
66
|
+
|
67
|
+
class TestRunner:
|
68
|
+
def __init__(self, args):
|
69
|
+
"""直接存储args对象"""
|
70
|
+
self.args = args
|
71
|
+
self.allure_manager = AllureManager(self.args.port)
|
72
|
+
|
73
|
+
def run_pytest_tests(self) -> bool:
|
74
|
+
"""执行pytest测试"""
|
75
|
+
case = self.args.service.split('_')[0]
|
76
|
+
cmd = [
|
77
|
+
"pytest",
|
78
|
+
"-m", case,
|
79
|
+
"--env", self.args.env,
|
80
|
+
"--app", self.args.app,
|
81
|
+
"--service", self.args.service,
|
82
|
+
"--project", self.args.project,
|
83
|
+
"--alluredir", "./allure_results"
|
84
|
+
]
|
85
|
+
try:
|
86
|
+
logger.info(f"run_pytest_tests Executing: {' '.join(cmd)}")
|
87
|
+
subprocess.run(cmd, check=True, timeout=3600)
|
88
|
+
except subprocess.CalledProcessError as e:
|
89
|
+
logger.error(f"Tests failed with exit code {e.returncode}")
|
90
|
+
except subprocess.TimeoutExpired:
|
91
|
+
logger.error("Test execution timed out after 1 hour")
|
92
|
+
|
93
|
+
def generate_allure_report(self) -> bool:
|
94
|
+
"""生成Allure报告"""
|
95
|
+
try:
|
96
|
+
cmd = [self.allure_manager.allure_path, "generate", "./allure_results", "-o", self.args.report_dir, "--clean"]
|
97
|
+
subprocess.run(
|
98
|
+
cmd,
|
99
|
+
check=True,
|
100
|
+
timeout=300
|
101
|
+
)
|
102
|
+
logger.info(f"generate_allure_report Executing: {' '.join(cmd)}")
|
103
|
+
except subprocess.CalledProcessError as e:
|
104
|
+
logger.error(f"Report generation failed: {e}")
|
105
|
+
def _handle_allure_report(self) -> bool:
|
106
|
+
"""Handle Allure report serving and browser opening."""
|
107
|
+
if self.allure_manager.is_server_running():
|
108
|
+
logger.info("Refreshing existing Allure report tab...")
|
109
|
+
if not self.allure_manager.refresh_browser_tab():
|
110
|
+
logger.info("Opening new report...")
|
111
|
+
webbrowser.open(f"http://localhost:{self.args.port}")
|
112
|
+
else:
|
113
|
+
logger.info("Starting new Allure server...")
|
114
|
+
if self.allure_manager.start_server(self.args.report_dir):
|
115
|
+
webbrowser.open(f"http://localhost:{self.args.port}")
|
116
|
+
|
117
|
+
def run(self):
|
118
|
+
# 1. 运行测试
|
119
|
+
self.run_pytest_tests()
|
120
|
+
|
121
|
+
# 2. 生成报告数据
|
122
|
+
self.generate_allure_report()
|
123
|
+
|
124
|
+
# 3. 启动Allure服务
|
125
|
+
self._handle_allure_report()
|
126
|
+
logger.info(f"http://localhost:{self.args.port}")
|
127
|
+
return 0
|
128
|
+
def main():
|
129
|
+
try:
|
130
|
+
parser = argparse.ArgumentParser(description="Run tests with Allure reporting")
|
131
|
+
parser.add_argument("--env", required=True, help="Test environment")
|
132
|
+
parser.add_argument("--app", required=True, help="Application ID")
|
133
|
+
parser.add_argument("--service", required=True, help="Service name")
|
134
|
+
parser.add_argument("--project", required=True, help="Project name")
|
135
|
+
parser.add_argument("--port", type=int, default=8883, help="Allure report port")
|
136
|
+
parser.add_argument("--report-dir", default="allure_report", help="Allure report directory")
|
137
|
+
args = parser.parse_args()
|
138
|
+
test_runner = TestRunner(args)
|
139
|
+
exit(test_runner.run())
|
140
|
+
except KeyboardInterrupt:
|
141
|
+
logger.info("Process interrupted by user")
|
142
|
+
exit(1)
|
143
|
+
except Exception as e:
|
144
|
+
logger.error(f"Unexpected error: {e}", exc_info=True)
|
145
|
+
exit(1)
|
146
|
+
if __name__ == "__main__":
|
147
|
+
main()
|
@@ -0,0 +1,21 @@
|
|
1
|
+
WebsocketTest/__init__.py,sha256=u71SAVmbgsyp0K21kilo7pIDgeyxsaHAi93clC0OIPQ,556
|
2
|
+
WebsocketTest/cli.py,sha256=Y8lQ_V5blKxNg-Q9PeuPu7zasMNDPmry9g4KXnliDfk,1636
|
3
|
+
WebsocketTest/conftest.py,sha256=y2_2961mhCYSRMp5X9xLq3nOmRzaTSgD3GDdrlTqxn4,1885
|
4
|
+
WebsocketTest/run_tests.py,sha256=cRKvcQGBZG-1XkeoSMeCU8TI76FunrS2jkx6COx91_o,5776
|
5
|
+
WebsocketTest/caseScript/Aqua.py,sha256=zr1mURYGikzzJDsx2lwdH9m_ENlJzH3vDvepsfwhfdg,7405
|
6
|
+
WebsocketTest/caseScript/Gateway.py,sha256=D_oVhhKGFA3FdFblg_YmUj2HhlPNI3kht9LqHnSuNZA,11723
|
7
|
+
WebsocketTest/caseScript/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
+
WebsocketTest/common/Assertion.py,sha256=xMesYiZY6FSf7_Zf6BIXGl9a2Prx-blRYGK2zKQy81M,5215
|
9
|
+
WebsocketTest/common/WSBaseApi.py,sha256=-zDpMsaF4ugVKUpzDwbs6l9DBvlh2uw1oJGY64fk6fY,3982
|
10
|
+
WebsocketTest/common/WebSocketApi.py,sha256=JpgX1qAA8skh_t9hLDmgEx6KugewF284u2cjrQkbLnI,2743
|
11
|
+
WebsocketTest/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
WebsocketTest/common/assertUtils.py,sha256=xJvjm0QDIxUHIZm96-2sKkDUWzPQHaOJB9x9eSLlIoU,4699
|
13
|
+
WebsocketTest/common/logger.py,sha256=B7jjPd5miAYorveHUEGW8xKxUNlJzKlVug0rKWRK3p0,783
|
14
|
+
WebsocketTest/common/utils.py,sha256=5NrU_cXzpzTYSI38LZe4IjJe3HxNB64rtrXZcf3Vixs,8706
|
15
|
+
WebsocketTest/testcase/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
+
WebsocketTest/testcase/test_all.py,sha256=mdfivt5vmfcn_4LZVcQ1KTH0n69gKxd_Id-DCapGlu4,198
|
17
|
+
WebsocketTest-1.0.3.dist-info/METADATA,sha256=TOfnxrmuLy4hqPkXk_9d5w-94RuyYqC4r3ecSH0X-Sg,378
|
18
|
+
WebsocketTest-1.0.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
19
|
+
WebsocketTest-1.0.3.dist-info/entry_points.txt,sha256=7ppZml2d-BVAurDYhw301dOBU8wDgQrtTNlkJNGSc_Q,59
|
20
|
+
WebsocketTest-1.0.3.dist-info/top_level.txt,sha256=2iF1gZSbXLjVFOe5ZKQiCLC1FzAwhcQUM88yGi-vrCU,14
|
21
|
+
WebsocketTest-1.0.3.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
WebsocketTest
|
@@ -1,17 +0,0 @@
|
|
1
|
-
caseScript/Aqua.py,sha256=zr1mURYGikzzJDsx2lwdH9m_ENlJzH3vDvepsfwhfdg,7405
|
2
|
-
caseScript/Gateway.py,sha256=D_oVhhKGFA3FdFblg_YmUj2HhlPNI3kht9LqHnSuNZA,11723
|
3
|
-
caseScript/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
common/Assertion.py,sha256=xMesYiZY6FSf7_Zf6BIXGl9a2Prx-blRYGK2zKQy81M,5215
|
5
|
-
common/WSBaseApi.py,sha256=-zDpMsaF4ugVKUpzDwbs6l9DBvlh2uw1oJGY64fk6fY,3982
|
6
|
-
common/WebSocketApi.py,sha256=JpgX1qAA8skh_t9hLDmgEx6KugewF284u2cjrQkbLnI,2743
|
7
|
-
common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
common/assertUtils.py,sha256=xJvjm0QDIxUHIZm96-2sKkDUWzPQHaOJB9x9eSLlIoU,4699
|
9
|
-
common/logger.py,sha256=B7jjPd5miAYorveHUEGW8xKxUNlJzKlVug0rKWRK3p0,783
|
10
|
-
common/utils.py,sha256=5NrU_cXzpzTYSI38LZe4IjJe3HxNB64rtrXZcf3Vixs,8706
|
11
|
-
testcase/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
testcase/test_all.py,sha256=mdfivt5vmfcn_4LZVcQ1KTH0n69gKxd_Id-DCapGlu4,198
|
13
|
-
WebsocketTest-1.0.1.dist-info/METADATA,sha256=Vr1be7OlsFVWKE1DMIgKEZBCFAz93S8-GBLrSCney68,378
|
14
|
-
WebsocketTest-1.0.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
15
|
-
WebsocketTest-1.0.1.dist-info/entry_points.txt,sha256=9TXEzvfAj1QvYGZKFgi09VFqBnizLmJ8HjVsHhhmenk,55
|
16
|
-
WebsocketTest-1.0.1.dist-info/top_level.txt,sha256=KmqByBRxmIHZiIEEGQnX5_C3urP-43-6TCFUhM3aMKk,27
|
17
|
-
WebsocketTest-1.0.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|